构造函数深入理解

目录

  • 构造函数
    • 构造函数体赋值
    • 初始化列表
      • 初始化列表格式
      • 初始化列表的意义以及注意点
        • const修饰的成员变量初始化
        • 对象成员具体初始化的地方
        • 缺省值存在的意义
          • 例子1
          • 例子2
        • 初始化与赋值
        • 引用成员变量的初始化
          • 注意点1
          • 注意点2
          • 我的疑惑
        • 自定义类型成员初始化
          • 例子1
          • 例子2
          • 例子3
          • 例子4
        • 初始化列表可以调用函数
          • 例子1
          • 例子2
          • 例子3
        • 我的疑惑
        • 拓展
          • 例子1
          • 例子2
          • 不同类型的赋值
          • 编译器优化拷贝构造函数的情景
            • 例子1
            • 例子2
        • 总结
    • explicit关键字
      • 例子1
      • 例子2
    • 补充

感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接
🐒🐒🐒 个人主页
🥸🥸🥸 C语言
🐿️🐿️🐿️ C语言例题
🐣🐣🐣 python
🐓🐓🐓 数据结构C语言
🐔🐔🐔 C++
🐿️🐿️🐿️ 文章链接目录

构造函数

构造函数体赋值

在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值

class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};

虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量
的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始
化一次,而构造函数体内可以多次赋值。

初始化列表

初始化列表格式

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟
一个放在括号中的初始值或表达式

class Date
{
public:Date(int year, int month, int day): _year(year), _month(month), _day(day){}/*  Date(int year, int month, int day)之前的构造函数格式{_year = year;_day = day;_month = month;}*/private:int _year;int _month;int _day;
};

下面的写法都是错误的
在这里插入图片描述
在这里插入图片描述

初始化列表的意义以及注意点

const修饰的成员变量初始化
class Date
{
public:Date(int year, int month, int day){_n = 1;_year = year;_day = day;_month=month;}private:int _year;int _month;int _day;const int _n;
};
int main()
{Date d1(2024, 5, 14);return 0;
}

const int _n不能够初始化
在这里插入图片描述
在这里插入图片描述
对于下面的代码我们都知道,这是声明,当对象实例化的时候他们才会整体定义,比如Date d1(2024,5,14)

private:int _year;int _month;int _day;const int _n;

但是有些成员在定义的时候是必须初始化的,就如 const int _n,因为const修饰了int_n,const只有一次修改的机会就是在初始化的时候,初始化的时候没有被修改,就会导致后面要想再修改就不可能了,因为const不允许修改被修饰的变量

对象成员具体初始化的地方

那对象成员具体初始化的地方都在哪呢?

在这里插入图片描述
上图我们可以认为是具体的初始化吗?来看看下面的图片
在这里插入图片描述
定义和初始化应该只有一个位置,但是上图中我们对_year进行了两次初始化,那这是开辟了两次空间吗?
当然不是,这样的情况我们并不想让他发生
所以构造函数才有了初始化列表,初始化列表是每个成员变量定义初始化的位置,也就是说想_year=1这样重复初始化的就别出现在初始化列表当中了
在这里插入图片描述
在这里插入图片描述
上图中_month和_day默认初始化为0
在之前的构造函数当中,有提到过缺省值

缺省值存在的意义
private:int _year=2;int _month=1;int _day=3;const int _n=1;

这里的_year _month _day _n的缺省值分别为2 1 3 1,而这个缺省值其实就是为初始化列表准备的,当初始化列表当中什么都没有的时候,缺省值就发挥作用了

例子1
class Date
{
public:Date(int year, int month, int day){}void Print(){cout << _year << "/" << _month << "/" << _day << "/" << _n << endl;}
private:int _year=2;int _month=1;int _day=3;const int _n=1;
};
int main()
{Date d1(2024, 5, 14);d1.Print();return 0;
}

在这里插入图片描述

例子2

在这里插入图片描述

_year输出的结果是2因为在初始化列表当中我们对_year初始化成了2,所以并没有用到缺省值
_month输出结果是一个随机值因为_month即没给缺省值,又没对其进行初始化
_day输出结果是0,虽然_day给了缺省值,但是在初始化列表当中没有具体给初始化值,所以_day最后初始化的值为0(具体为什么我也不清楚,可能就觉得既然你都有缺省值了,要想让_day=缺省值,就不要在初始化列表里面写_day(),这样让人感觉你就像让_day默认初始化成0)
_n输出结果是4因为给的缺省值是4,虽然在初始化列表当中没有写n,但是初始化列表会用这个缺省值给n进行初始化

初始化与赋值

知道了初始化列表的用处后我们看看下面这个代码

class Date
{
public:Date(int year, int month, int day):_year(1),_month(2), _day(3),_n(4){_year = year;_month = month;_day = day;_year = 1;}void Print(){cout << _year << "/" << _month << "/" << _day << "/" << _n << endl;}
private:int _year=1;int _month;int _day=2;const int _n=4;
};
int main()
{Date d1(2024, 5, 14);d1.Print();return 0;
}

在这里插入图片描述
结果是1/5/14/4,为什么不是1/2/3/4呢?不是说初始化列表值允许初始化1次吗?
初始化的确只能初始化1次,但是赋值可以赋值很多次
所以大括号里面的_year=year…其实是对_year…赋值

并且从这个例子我们也可以看出谁才是初始化,因为按照程序运行的顺序,初始化必然是排在最前面的,赋值是在初始化成功的基础上才能进行

尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化,所以最终的写法如下

class Date
{
public:Date(int year, int month, int day):_year(year),_month(month), _day(day),_n(4){}void Print(){cout << _year << "/" << _month << "/" << _day << "/" << _n << endl;}
private:int _year=1;int _month;int _day=2;const int _n=4;
};
int main()
{Date d1(2024, 5, 14);d1.Print();return 0;
}

在这里插入图片描述

注意:
1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)
2. 类中包含以下成员,必须放在初始化列表位置进行初始化:
引用成员变量
const成员变量
自定义类型成员(且该类没有默认构造函数时)

引用成员变量的初始化

为什么引用也要房子初始化列表当中呢?
在写引用的文章当中说过引用必须要在定义的时候初始化

注意点1

在这里插入图片描述
因为是引用,所以在初始化的时候我们可以填入成员变量,如下图_ref是_year的别名
在这里插入图片描述

注意点2

但同时也需要注意因为_ref是引用,所以在初始化的时候需要注意不可以成为const修饰成员的别名
在这里插入图片描述

我的疑惑

在写到这里的时候我还在想像这种引用放在初始化列表开始的位置,在_year还没初始化的时候,就让_ref作为_year的别名会不会报错呢?
在这里插入图片描述
这里我说一下我自己的想法,因为_year已经声明了,而_ref作为_year的别名,由于_year没有初始化,所以_ref只是套了一个空壳子,单号_year初始化后,_ref才真正的初始化成功
为了验证想法我见year的缺省值删掉,并且不在初始化列表当中加入_year,最后输出的是随机值
在这里插入图片描述
这时我有一点疑惑,如果没有初始化_year那_year应该只有一个声明才对,只有一个声明就说明_year没有空间,输出的时候应该会报错才对
于是我打印了一下_year和_ref的地址,发现他们是有地址的,说明_year和_ref是存在的
为了解释这种情况我觉得可能是在定义这个对象的时候想_year这些成员变量就已经有空间了,但是又和前面所的知识冲突,也可能是我自己没学懂😕😕😕
在这里插入图片描述

自定义类型成员初始化
例子1
class A
{
public:A(int a = 0):_a(a){cout << "A(int a=0)" << endl;}
private:int _a;
};
class Date
{
public:Date(int year, int month, int day):_ref(_year), _month(month), _day(day), _n(4)   {}void Print(){cout << &_year << "/" << _month << "/" << _day << "/" << _n << "/" << &_ref << endl;}
private:int _year;int _month;int _day=2;const int _n=4;int& _ref;A _aa;
};
int main()
{Date d1(2024, 5, 14);d1.Print();return 0;
}

现在有一个自定义类型A,将A_aa的声明放在Date的成员声明里,但是A_aa没有在Date的初始化列表当中定义,那A_aa会不会被定义呢?

我们需要理解下面这句话
我们不写默认构造函数,编译器会自动生成,编译器自动生成的默认构造函数不对内置类型进行处理,自定义类型调用他自己的默认构造函数

在Date的初始化列表当中由于没有写A的构造函数,所以在运行的过程当中会调用A的默认构造函数,而A的默认构造函数是在A这个类里面
在这里插入图片描述

例子2

当我们不给a默认构造呢(只写了int a,没有些int a=0)
在这里插入图片描述
因为我们给_a初始化成a,但是a没有给值,所以会报错

例子3

而当我们不给_a初始化成a时,也就是_a(),这样a就是初始化成0
在这里插入图片描述

例子4

我们也可以这样写
在这里插入图片描述

初始化列表可以调用函数
例子1
class Date
{
public:Date():_p((int*)malloc(sizeof(4))){//函数体if (_p == nullptr){perror("malloc fail");}}void Print(){cout << _p << endl;}
private:int* _p;
};
int main()
{Date d1;d1.Print();return 0;
}

在这里插入图片描述

例子2
class Date
{
public:Date(int year, int month, int day):_ref(_year), _month(month), _day(day), _n(4),_p((int*)malloc(sizeof(4))){//函数体if (_p == nullptr){perror("malloc fail");}}void Print(){cout << &_year << "/" << _month << "/" << _day << "/" << _n << "/" << &_ref << endl;cout << _p << endl;}
private:int _year;int _month;int _day = 2;const int _n = 4;int& _ref;int* _p;
};
int main()
{Date d1(2024, 5, 14);d1.Print();return 0;
}

在这里插入图片描述

例子3
class A
{
public:void Print(){cout << a << " " << p1 << " " << *p2 << endl;}
private:int a = 1;int* p1 = nullptr;int* p2 = (int*)malloc(4);};
int main()
{A a;a.Print();
}

因为缺省值是给初始化列表的,这里的int* p2 = (int*)malloc(4)和上面初始化列表当中的 ,_p((int*)malloc(sizeof(4)))写法是相同的,既然初始化列表可以这样写,那缺省值也是可以这样写的

所以缺省值不一定是常量
在这里插入图片描述

我的疑惑
class Date
{
public:Date():_p((int*)malloc(sizeof(4))){//函数体if (_p == nullptr){perror("malloc fail");}}void Print(){cout << _p << endl;}
private:int* _p;
};
int main()
{Date d1();d1.Print();return 0;
}

这段代码和上面的例子1的唯一区别就是Date d1有无括号,但是就是因为这个括号导致报错了,我也不清楚为什么会这样
例子2中Date d1有括号,并且也定义了_p,为什么就可以正常运行
在这里插入图片描述
在这里插入图片描述
除了上面的一些问题还有关于_p初始化的一些问题等

拓展
例子1
class C
{
public:C(int x = 0):_x(x){}void Print(){cout << _x << endl;}
private:int _x;
};
int main()
{C c1(1);c1.Print();
}

在这里插入图片描述

例子2
class C
{
public:C(int x = 0):_x(x){}void Print(){cout << _x << endl;}
private:int _x;
};
int main()
{C c2 = 2;c2.Print();
}

单参数构造函数支持隐式类型的转换,这里的2构造出了一个C的对象,我们先称为c3,然后将c3拷贝构造给c2,这样的话C c2=2就可以理解成c2=c3,

在这里插入图片描述

不同类型的赋值

另外之前提到过不同类型赋值过程也和这个是一样的
比如现在有一个int类型的a和double类型的b
在这里插入图片描述
现在要将b用来给a赋值,在赋值的过程中b会创建出一个临时变量,然后a拷贝这个临时变量,因为double类型有8个字节,而int类型只有4个字节,所以拷贝的时候就出现了数据丢失
在这里插入图片描述
在这里插入图片描述

同样的当一个char类型的变量赋值给int类型的变量,因为char类型只有1个字节,而int类型有4个字节,所以当char类型赋值给int类型的时候会出现类型提升
在这里插入图片描述

所以现在倒回来理解C c2=2应该就容易一点了

在这里插入图片描述

编译器优化拷贝构造函数的情景
例子1

我们也可以验证一下他时候调用了拷贝构造函数

在这里插入图片描述
这里没有调用拷贝构造函数是因为编译器优化了,同一个表达式连续步骤的构造,一般会合二为一

例子2
class C
{
public:C(int x = 0):_x(x){}C(const C& cc){cout << "C(const C& cc)" << endl;}void Print()const{cout << _x << endl;}
private:int _x;
};
int main()
{const C& c2 = 2;c2.Print();
}

这里的c2引用的是临时变量,而临时变量具有常性
在这里插入图片描述

总结

类中以下成员必须放在初始化列表中初始化
引用成员变量
const修饰成员变量
自定义类型成员(且该类没有构造默认成员函数时)
其他成员可以在函数体内初始化也可以在初始化列表里初始化

这里的函数体具体是指哪里呢?

    Date(int year, int month, int day):_ref(_year), _month(month), _day(day), _n(4)   ,_aa(1){//函数体}

explicit关键字

对于上面的代码有许多情况都是通过隐式类型转换,如果我们不想让这种转换发生,我们可以通过explicit去修饰

例子1

class C
{
public:C(int x = 0):_x(x){}C(const C& cc){cout << "C(const C& cc)" << endl;}void Print()const{cout << _x << endl;}
private:int _x;
};
int main()
{const C& c2 = 2;c2.Print();
}

在这里插入图片描述

例子2

多参数的类也是可以支持explicit修饰

class A
{
public:A(int a1,int a2):_a1(a1),_a2(a2){}
private:int _a1;int _a2;
};
int main()
{A b1 = { 1,2 };const A& b2 = { 1,2 };A b3(1,2);return 0;
}

需要注意的是A b1 = { 1,2 }中用的是花括号
在构造时我们我们可以写成A b3(1,2)

当加上explicit之后

class A
{
public:explicit A(int a1,int a2):_a1(a1),_a2(a2){}
private:int _a1;int _a2;
};
int main()
{A b1 = { 1,2 };const A& b2 = { 1,2 };A b3(1,2);return 0;
}

在这里插入图片描述

补充

看一下下面这道题

class A
{
public:A(int a):_a1(a),_a2(_a1){}void Print() {cout<<_a1<<" "<<_a2<<endl;}
private:int _a2;int _a1;
};
int main() {A aa(1);aa.Print();
}
A. 输出1  1
B.程序崩溃
C.编译不通过
D.输出1  随机值

_a2输出的是一个随机值,这是因为成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
注意看private的中的_a1和_a2的先后顺序,_a2是在_a1前面的,所以_a2要比_a1先赋值
在这里插入图片描述

在这里插入图片描述
当我们调整顺序后,输出就是1 1了
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

平衡二叉查找树和多路查找树

平衡二叉查找树 普通平衡二叉查找树 平衡二叉树定义是按照有序排列成树状&#xff0c;左子树数据大于右子树&#xff0c;任意节点的左右子树高度不能大于1 优点&#xff1a;可以保证绝对的平衡 缺点&#xff1a;当进行删除节点和新增节点&#xff0c;树进行自平衡的时候&…

Springboot 敏感词过滤

参考&#xff1a;网站是怎么屏蔽脏话的呢&#xff1a;简单学会SpringBoot项目敏感词、违规词过滤方案_springboot 项目关键词过滤-CSDN博客 【敏感词过滤】_wx60d2a462203aa的技术博客_51CTO博客 1、添加依赖 <dependency><groupId>com.github.houbb</group…

筛斗数据:开启数据提取与治理的新篇章

在数字化转型的浪潮中&#xff0c;数据已成为企业最宝贵的资产之一。然而&#xff0c;如何从海量、复杂的数据中准确提取有价值的信息&#xff0c;并有效治理这些数据&#xff0c;使其成为驱动业务决策的重要力量&#xff0c;成为了众多企业面临的共同挑战。在十堰市这片充满活…

Vanchip新一代WiFi产品全新亮相

1‧ 研讨会介绍 随着 Wi-Fi7 时代的到来&#xff0c;高频信号衰减较高&#xff0c;因此需要外挂 FEM 电路以提高发射信号的增益&#xff0c;从而保障远距离通信的效果和范围。WiFi-FEM 逐渐成为智慧手机、路由器等终端产品中的标配芯片。Vanchip 针对客户的迫切需求&#x…

Python网页正文提取神器: readability库详解

Python网页正文提取神器: readability库详解 1. readability简介2. 安装3. 基本使用3.1 提取文章内容3.2 获取元数据 4. 高级功能4.1 自定义选项4.2 处理非标准HTML 5. 实际应用示例5.1 批量提取新闻文章5.2 创建简单的阅读模式 6. 性能优化7. 注意事项和局限性8. 总结 在网络爬…

【高精度实验】PFA具塞试管 四氟塞子 实验室分析专用

PFA具塞试管&#xff0c;以其四氟塞子的卓越密封性能&#xff0c;为实验室的精确度和安全性提供了双重保障。以下是针对PFA具塞试管的营销文案&#xff0c;特别强调其四氟塞子的特性&#xff1a; 【四氟密封&#xff0c;科研无忧】 在追求极致精确的科研世界里&#xff0c;PF…

上海市计算机学会竞赛平台2023年3月月赛丙组循环播放

题目描述 又是一年春暖花开&#xff0c;小爱与家人一起开车出游&#xff0c;在漫长的路途上&#xff0c;他开始播放自己的歌单以舒缓疲劳。 小爱的歌单中有&#x1d45b;n首歌&#xff0c;其中第 &#x1d456;i 首歌的时长为 &#x1d461;&#x1d456;ti​ 分钟&#xff0…

安华金和—可信数据空间助力公共数据授权运营安全有序开展的实践探索

伴随数字化、网络化和智能化的快速发展&#xff0c;数字经济与实体经济深度融合&#xff0c;数据已然成为经济发展赖以依托的基础性、战略性资源&#xff0c;对社会生产、分配、流通、消费和社会服务管理等各环节产生深刻影响。我国高度重视数字经济发展&#xff0c;将数据列入…

Linux安全加固:防火墙规则与SELinux策略

Linux系统的安全加固是一个多层面的过程&#xff0c;其中防火墙规则与SELinux策略是两个至关重要的方面&#xff0c;它们共同为系统提供了网络和本地级别的安全保障。 防火墙规则&#xff08;FirewallD/Iptables&#xff09; Firewalld • 简介&#xff1a;Firewalld是Linux…

理解 React 中的 API 封装、自定义 Hooks、组件、独立模块与 Context

在现代前端开发中&#xff0c;React 提供了多种方法来组织和管理代码。这些方法包括 API 封装、自定义 Hooks、组件、独立模块和 Context。理解它们的区别和联系&#xff0c;可以帮助我们编写更清晰、更模块化的代码。 1. API 封装 目的&#xff1a;处理与后端服务的通信逻辑…

14-21 人工智能的历史以及简单神经网络的工作原理

初始 “我们需要走得更深”这句台词出自电影《盗梦空间》。这是在讨论深入梦境更深层次时说的&#xff0c;暗示需要探索梦境的更深层次。虽然这似乎是不可能的&#xff0c;但它传达的理念是&#xff0c;要创造一个新的世界&#xff0c;就必须冒险进入更深的层次。 电影《盗梦空…

将List切割为多个指定长度的多个List

参考: https://blog.csdn.net/baidu_41480640/article/details/122507018https://blog.csdn.net/H1767410/article/details/138333350https://blog.51cto.com/u_16213352/7632003https://blog.csdn.net/2301_82243396/article/details/137900249 手写1 private List<List&l…

3D问界—MAYA中冻结变化的作用以及应用的场景

问题提出&#xff1a;MAYA中冻结变化的作用以及应用的场景 在Maya中&#xff0c;“冻结变换”&#xff08;Freeze Transformations&#xff09;的作用是重置对象的变换属性&#xff0c;包括平移、旋转和缩放&#xff0c;将它们归零或者设定为特定的值。这通常在以下几个步骤中需…

unity强力配置插件Luban【Next最新版本】(二)本地化

文章目录 前言一、快速实现静态本地化1、表格格式2、本地化文本3、修改bat文件3、打表 二、多语言切换1、修改bat文件2、增加本地化管理脚本3、测试 总结 前言 无需多言&#xff0c;本地化&#xff08;Localization&#xff0c;简称 L10N&#xff09;是指将产品、内容或服务适…

软考的报名详细流程

2024年软考的考试时间已经公布&#xff0c;分别为5月25日至28日和11月9日至12日。准备参加2024年软考的朋友们&#xff0c;一定要提前关注官方发布的考试安排。 本文将详细介绍软考报考的整个流程。准备报考的朋友们&#xff0c;阅读本文就足够啦&#xff01;软考的报考流程大致…

如何系列 如何确保Kafka消息可靠性/防止消息丢失

文章目录 ProducerBrokerConsumer流程 从Kafka架构来看&#xff0c;理论上仍有消息丢失的可能性&#xff0c;但实际发生的概率极低&#xff0c;只有在所有副本的机器都宕机时才会丢失。 当所有代理&#xff08;broker&#xff09;都确认消息时&#xff0c;这并不意味着消息已经…

【CT】LeetCode手撕—1143. 最长公共子序列

目录 题目1- 思路2- 实现⭐1143. 最长公共子序列——题解思路 3- ACM 实现 题目 原题连接&#xff1a;1143. 最长公共子序列 1- 思路 模式识别&#xff1a;最长公共子序列——> 动规五部曲 2- 实现 ⭐1143. 最长公共子序列——题解思路 class Solution {public int longe…

2024上半年网络工程师考试《应用技术》试题一

阅读以下说明&#xff0c;回答问题。 【说明】 MPLS基于(1)进行转发&#xff0c;进行MPLS标签交换和报文转发的网络设备称为(2)&#xff0c;构成MPLS域(MPSDomain)。位于MPLS域边缘、连接其他网络的LSR称为(3),区域内部的LSR称为核心LSR(CoreLSR)IP报文进入MPLS网络时&#xf…

LLM4Decompile——专门用于反编译的大规模语言模型

概述 论文地址&#xff1a;https://arxiv.org/abs/2403.05286 反编译是一种将已编译的机器语言或字节码转换回原始高级编程语言的技术。该技术用于分析软件的内部工作原理&#xff0c;尤其是在没有源代码的情况下&#xff1b;Ghidra 和 IDA Pro 等专用工具已经开发出来&#…

等保2.0 实施方案

一、引言 随着信息技术的广泛应用&#xff0c;网络安全问题日益突出&#xff0c;为确保信息系统安全、稳定、可靠运行&#xff0c;保障国家安全、公共利益和个人信息安全&#xff0c;根据《网络安全法》及《信息安全技术 网络安全等级保护基本要求》&#xff08;等保2.0&#x…