再看const成员函数

文章目录

  • 再看函数重载
  • const成员函数
    • 保安(const)能保护所有人(类成员)吗?
    • mutable修饰类成员
  • const/非const成员函数的复用

关于函数重载实际上我理解不是很深入,直接导致const成员函数这块出大问题,在阅读《effective C++》后才恍然大悟,做如下学习记录

再看函数重载

关于函数重载的概念我就不多说了,这里我要讨论的是一种我们常常忽视的函数重载:

下面这两个函数构成函数重载吗?

void fun(int a)
{
    cout << "int a" << endl;
}


void fun(const int a)
{
    cout << "const int a" << endl;
}

答案是不构成!这种写法是错误的,编译器会报错对于fun函数的重定义!

那下面两个函数构成函数重载吗

void fun(int *a)
{
    cout << "int *a" << endl;
}

void fun(const int *a)
{
    cout << "const int* a" << endl;
}

int main()
{
    int a = 1;
    const int b = 2;
    fun(&a);
    fun(&b);
    return 0;
}

答案是构成函数重载😅,当我知道这个特性的时候人都麻了,上面的程序的运行成果如下图:
在这里插入图片描述
那么这个特性实际上又会引出另一个特性:const成员函数重载

作为上面两个用例的引申,请判断一下下面两个 类C 的成员函数是否构成重载?


class  C
{
public:
    void fun() 
    {
        cout << "i am not const" << endl;
    }

    void fun() const 
    {
        cout << "i am const" << endl;
    }
};

int main()
{
    C x;
    const C y;
    x.fun();
    y.fun();
}

答案是肯定的,问题就处在C++类的this指针上,const成员函数的const修饰的是被类成员函数的参数列表省略的this指针,而非constthis指针就和const this指针就构成了重载。这种重载是相当隐晦,很长时间我都没有理解明白其中原理。

const成员函数

接下来我们将对const成员函数的特性进行一系列的讨论
众所周知const成员函数是:const对象才能调用的函数,非const类对象只能调用非const类对象
而const 代表的是不被修改——换句话说就是const修饰对象会被保护?

首先我们先复习一下指针的const特性,下面两种定义方式的区别是什么?

const int* a = new int;
int* const a = new int;
  1. const修饰的是指针变量a所存储的地址所对应的内存
  2. const修饰的是指针变量a

保安(const)能保护所有人(类成员)吗?


struct A
{
    //真的能保护成员变量吗
    void func1() const 
    {
        a = 10;  //错误的 ,不能修改
        cout << a << endl;
    }
    const int* func2()const   //注意返回值类型
    {
        return &a;
    }
    const int& func3()const   //注意返回值类型
    {
        return a;
    }
    int a=1;
};

int main()
{
	const A a;
}

这时你定义了一个const对象a,他在调用成员函数时那个隐藏的this指针显式的写出来为const A* this,这里的const修饰的指针变量存储地址所对应的内存,所以你无法修改对象a里面任何的字节!

但是我们可以看一下下面的情况

class A
{
public:
    A()
    {
        p = new char[100];
        strcpy(p, "hello world");
    }
    void func() const
    {
        p[5] = '$';
    }
    char* p;
};

int main()
{
    const A a;
    a.func();
    cout << a.p << endl;
}

在这里插入图片描述
在这个Demo中我们定义了一个类A的常量对象,并在常量成员函数func中对成员函数进行修改,结果就是字符串的内容被修改成功了。常量对象的成员变量被修改了,逻辑上违背了const的定义,所以这时一个很大的坑,但是语法上是没有问题的,解释如下👇
在这里插入图片描述
虽然对象a是一个const对象,但是只能代表指针变量这个变量的值是不变的,并不能保证指针变量所指向内存的不变。所以字符串“hello world”还是可以修改的。
这样就会让我们在编写代码的时候,逻辑上认为自己定义了一个“const的对象“,但是可能在使用时对数据发生了改变。

所以认为const能保护所有的成员对象的想法其实并不靠谱

mutable修饰类成员

对于某些情况我们想要修改部分const对象的成员变量值,但是const这个属性是对所有成员变量所施加的,这样会导致一些很麻烦的问题

在一个类A中我们希望部分一个const对象的a和b可以修改,但是c不能修改

class A
{
public:
    void func() const
    {
        a = 10;  //错误的行为
        b = 20;  //错误的行为
    }
    int a;
    int b;
    int c;
};
int main()
{
    const A a;
    a.func();
}

上面的代码中定义了一个const对象,这个const属性也继承到了三个成员变量身上,所以func中修改a和b的值是非法的。
如果我们想要部分的const成员变量可以修改,可以加上mutable 关键字来修饰。

class A
{
public:
    void func() const
    {
        a = 10;
        b = 20;
    }
    mutable int a;
    mutable int b;
    int c;
};
int main()
{
    const A a;
    a.func();
}

加上mutable之后,上面的代码就正确了

const/非const成员函数的复用

对于const和非const成员函数两者的功能是类似的,在一定情况下是可以代码复用的,示例如下:
我们在模拟实现stl的string 类型时对于const char& operator[](int pos) constchar& operator[](int pos)两个函数就可以使用强制类型转化进行代码复用。

class String
{
    const char& operator[](int pos) const 
    {
        //省略边界检查等一系列操作
        return p[pos];
    }

    char& operator[](int pos)
    {
        return const_cast<char&>(static_cast<const String>(*this)[pos]);
    }
    char p[];
};

但是注意你可以用const成员函数去复用非const成员函数,但是反过来的复用(即用非const成员函数去复用const成员函数)就是一种非常不安全的做法!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/27592.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

chatgpt赋能python:Python如何升序输出?从入门到实践!

Python如何升序输出&#xff1f;从入门到实践&#xff01; 作为一门高级编程语言&#xff0c;Python是目前应用广泛且最为流行的一门语言之一。它逐渐成为开发者的首选语言&#xff0c;因为它易学易用&#xff0c;可读性强&#xff0c;支持多种编程范式&#xff0c;提供了强大…

【MySQL 数据库】9、存储过程

目录 一、存储过程是什么二、存储过程的基本语法三、MySQL 中的变量(1) 系统变量(2) 用户自定义变量(3) 局部变量 四、if 判断五、参数传递和返回值六、case 语句七、while 循环八、repeat 循环九、loop 循环十、游标十一、条件处理程序 一、存储过程是什么 &#x1f331; 存储…

Java反射与注解

文章目录 一、 注解1.简介2. 元注解3. 自定义注解 二、 反射1. 简介2. 理解Class类并获取Class实例3. 类的加载与初始化4. 类加载器ClassLoader5. 获取运行时类的完整结构6. 动态创建对象执行方法7. 反射操作泛型8. 反射操作注解 一、 注解 1.简介 Annotation是JDK5.0开始引入…

第二章 搭建TS环境

搭建 TypeScript 的开发环境。一个舒适、便捷且顺手的开发环境&#xff0c;不仅能大大提高学习效率&#xff0c;也会对我们日常的开发工作有很大帮助。 这一节我们就来介绍 VS Code 下的 TypeScript 环境搭建&#xff1a;插件以及配置项。对于 TS 文件的执行&#xff0c;我们会…

《横向联邦学习中 PCA差分隐私数据发布算法》论文算法原理笔记

论文地址&#xff1a;https://www.arocmag.com/article/01-2022-01-041.html 论文摘要 为了让不同组织在保护本地敏感数据和降维后发布数据隐私的前提下&#xff0c;联合使用 PCA进行降维和数据发布&#xff0c;提出横向联邦 PCA差分隐私数据发布算法。引入随机种子联合协商方…

linuxOPS基础_linux软件包安装

软件包概述 上图是windows下的软件包 Linux下也有很多可以安装的软件&#xff0c;而这些软件的安装包可细分为两种&#xff0c;分别是源码包和二进制包。 Linux下软件的安装方式 ① RPM软件包安装 > 软件名称.rpm ② YUM包管理工具 > yum install 软件名称 -y ③ 源码…

基于QGIS的长株潭城市群边界范围融合实战

背景 在面向区域的研究过程中&#xff0c;比如一些研究区域&#xff0c;如果是具体的行政区划&#xff0c;比如具体的某省或者某市或者县&#xff0c;可以直接从国家官方的地理数据中直接下载就可以。但如果并没有直接的空间数据那怎么办呢&#xff1f;比如之前遇到的一个场景&…

【郭东白架构课 模块二:创造价值】31 |节点六: 如何组织阶段性的价值交付?

你好&#xff0c;我是郭东白。上节课我们讲了为什么要做阶段性的价值交付&#xff0c;以及进入阶段性价值交付环节的准备工作。有了这些学习基础&#xff0c;这节课我们就可以进行阶段性价值交付了。 在交付的过程中&#xff0c;主要有三部分工作&#xff1a;目标分解、定义交…

数据结构——堆(C语言实现)

文章目录 什么是堆堆的实现堆的结构定义堆的初始化接口堆的销毁接口堆的插入数据接口向上调整建堆接口判断堆是否为空堆的删除数据接口向下调整建堆接口获取堆顶数据获取堆的有效数据个数完整实现代码小结 堆排序堆排序的实现 关于建堆和堆排序时间复杂度的分析向下调整建堆向上…

day52|动态规划13-子序列问题

子序列系列问题 300.最长递增子序列 什么是递增子序列&#xff1a; 元素之间可以不连续&#xff0c;但是需要保证他们所在位置是元素在数组中的原始位置。 dp数组dp[i]表示以nums[i]为结尾的最长递增子序列的长度。递归函数&#xff1a;dp[i] max(dp[j]1,dp[j])初始化条件&…

算法刷题-链表-移除链表元素

链表操作中&#xff0c;可以使用原链表来直接进行删除操作&#xff0c;也可以设置一个虚拟头结点再进行删除操作&#xff0c;接下来看一看哪种方式更方便。 203.移除链表元素 力扣题目链接 题意&#xff1a;删除链表中等于给定值 val 的所有节点。 示例 1&#xff1a; 输入&…

Linux下信号量使用总结

目录 1.Linux下信号量简介 2.POSIX信号量 2.1 无名信号量 2.2 有名信号量 3.System V信号量 1.Linux下信号量简介 信号量是解决进程之间的同步与互斥的IPC机制&#xff0c;互斥与同步关系存在的症结在于临界资源。 临界资源是在同一个时刻只容许有限个&#xff08;一般只有…

【数据结构与算法】03 队列(顺序队列--循环队列--优先级队列--链队列)

一、概念1.1 队列的基本概念1.2 队列的顺序存储结构1.21 顺序队列&#xff08;静态队列&#xff09;1.22 循环队列1.23 优先级队列 1.3 队列的链式存储结构 二、C语言实现2.1 顺序存储2.11 顺序队列2.12 循环队列2.13 优先级队列 2.2 链式存储 一、概念 1.1 队列的基本概念 队…

Linux内核中断和Linux内核定时器

目录 Linux内核中断 Linux内核定时器 Linux内核中断 int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev) 功能&#xff1a;注册中断 参数&#xff1a; irq : 软中断号 gpio的软中断号 软中断号 gpio_to_i…

【PCB专题】案例:绕等长怎么直接以颜色区分看出是否绕好

PCB上对于时序的处理,在板卡上实际我们是通过绕等长的手段。做为一个合格的Layout工程师,等长的处理是不可或缺的技能。 一般来说,在绕等长的时候我们可以使用Delay Tune命令来改变走线的长度,然后通过规则管理器中分析看看哪根线长哪根线短。 但是在实际工作中,很可能绕着…

Android应用程序进程的启动过程

Android应用程序进程的启动过程 导语 到这篇文章为止&#xff0c;我们已经简要地了解过了Android系统的启动流程了&#xff0c;其中比较重要的内容有Zygote进程的启动和SystemService以及Launcher的启动&#xff0c;接下来我们将要学习的是Android应用程序的启动过程&#xff…

华为OD机试真题 JavaScript 实现【最多几个直角三角形】【2023Q1 100分】

一、题目描述 有 N 条线段&#xff0c;长度分别为 a[1]-a[n]。 现要求你计算这 N 条线段最多可以组合成几个直角三角形&#xff0c;每条线段只能使用一次&#xff0c;每个三角形包含三条线段。 二、输入描述 第一行输入一个正整数 T (1< T< 100) &#xff0c;表示有…

2023蓝桥杯大学A组C++决赛游记+个人题解

Day0 发烧了一晚上没睡着&#xff0c;感觉鼻子被打火机烧烤一样难受&#xff0c;心情烦躁 早上6点起来吃了个早饭&#xff0c;思考能力完全丧失了&#xff0c;开始看此花亭奇谭 看了六集&#xff0c;准备复习数据结构考试&#xff0c;然后秒睡 一睁眼就是下午2点了 挂了个…

springboot项目外卖管理 day05-新增与删除套餐

文章目录 一、新增菜品1.1、需求分析1.2、数据模型setmealsetmeal_dish 1.3、代码开发-梳理交互过程1.3.1、下拉框展示1.3.2、菜品窗口展示1.3.3、新增套餐 2、套餐分页查询 一、新增菜品 1.1、需求分析 套餐就是菜品的集合。 后台系统中可以管理套餐信息&#xff0c;通过新…

一文打通:从字节码指令的角度解读前置后置自增自减(加加++减减--)

文章目录 1.前置了解的知识1.1 栈这种数据结构1.2 局部变量表和操作数栈1.3 三个字节码指令 2.单独使用后置与前置2.1 后置字节码指令2.2 前置字节码指令2.3 总结 3.需要返回值的情况下使用后置与前置3.1 后置字节码指令3.2 前置字节码指令3.3 总结3.4 练习&#x1f340; 练习一…