当前位置: 首页 > news >正文

C++类与对象(三)-- 再谈构造函数(细嗦初始化列表)、static成员

目录

一、再谈构造函数

1.1 构造函数体赋值

 1.2 初始化列表

1.2.1 概念

1.2.2 为什么要有初始化列表

1.2.3 初始化列表的特性

 1.2.4 初始化列表解决的问题

 1.2.5 自定义类型的成员初始化的方法

 1.3 explicit关键字

1.3.1 什么是类型转换

1.3.1.1 单参数类型转换

1.3.1.2 多参数的类型转换

1.3.2 为什么内置类型对象能隐式转换为自定义类型对象

 1.3.3 explicit关键字的使用

二、 static成员

2.1 概念

 2.2 特性


一、再谈构造函数

1.1 构造函数体赋值

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

在之前我们使用的构造函数都是通过函数体来赋值的。

class Date
{
public:Date(int year, int month, int day){// 函数体赋值_year = year;_month = month;_day = day;}
private:// 变量的声明int _year;int _month;int _day;
}

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

初始化列表和函数体赋值的区别:

  1. 在构造函数中,一定是先经过初始列表初始化,才到函数体内赋值
  2. 所以在函数体内赋值就不叫作初始化,而是一个        赋值的过程,成员变量真正初始化的过程是在初始化列表中就已经完成
  3. 初始化列表只能初始化一次,而赋值能赋值很多次
  4. 初始化是在变量创建时进行的,赋值是在变量已存在的时候进行的

 1.2 初始化列表

1.2.1 概念

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

class A
{
public:// 初始化列表初始化A(int a = 0):_a(a){// 函数体}
private:int _a;
};

1.2.2 为什么要有初始化列表

为什么会有初始化列表的存在,凭什么函数体内赋值用的好好的,突然就多出个初始化列表初始化呢?

解释:

类中包含以下成员,必须放在初始化列表位置进行初始化:

  1. 引用成员变量
  2. const成员变量
  3. 自定义类型成员(且该类没有默认构造函数时) 

	Date(int year, int month, int day)//初始化列表//:_ref(year)//,_n(1)//,_a(10)//,_year(5){// 剩下3个成员没有在初始化列表显示写出来定义// 但是他也会定义,只是内置类型默认给的随机值// 如果是自定义类型成员会去调用它的默认构造函数//函数体内初始化_year = year;_month = month;_day = day;}
private://成员变量的声明//并没有开辟空间int _year = 1;int _month = 2;int _day;// 这三个成员变量只能在初始化列表初始化A _a; // 没有默认构造函数int& _ref; // 引用:在定义时就得初始化(引用必须一个实体)const int _n; // const:在定义时就得初始化
};int main()
{//对象整体的定义//而对象的成员变量在什么地方定义? --> 初始化列表Date d1(2024, 10, 5);
}

如果不在构造函数的初始化列表中初始化上面所述的三个类型的成员变量,系统在编译的时候就会报错,报错的原因正是没有在初始化列表中初始化这三个类型的成员变量。 

1.2.3 初始化列表的特性

1、每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)

2、类中包含以下成员,必须放在初始化列表位置进行初始化:

  1. 引用成员变量
  2. const成员变量
  3. 自定义类型成员(且该类没有默认构造函数时) 

3、尽量使用初始化列表初始化,因为不管你是否使用初始化列表,对于自定义类型成员变量,一定会先使用初始化列表初始化

4、成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

 1.2.4 初始化列表解决的问题

  1. 必须在定义的地方显式初始化:引用、const、没有默认构造的自定义类型的成员
  2. 有些自定义类型的成员想要显式的初始化:不想使用类的默认构造函数,或者类没有默认构造函数
  3. 尽量使用初始化列表初始化(初始化列表作为类成员定义的地方,所以在定义的地方初始化更方便些)
  4. 构造函数不能只有初始化列表,而不用函数体初始化

 1.2.5 自定义类型的成员初始化的方法

  1. 在初始化列表显式地写出来并定义
  2. 在该类的默认构造函数中给缺省值

 1.3 explicit关键字

构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用

class A
{
public://explicit A(int a) A(int a):_a(a){}A(int* p){}int _a = 0;
};int main()
{A aa1(1);A aa2(2);// 内置类型对象 隐式转换成自定义类型对象// 能支持这个转换,是有A的int单参数构造函数(支持传一个参数多参数带缺省也可以)支持// 不想让隐式类型转换发生,构造函数加explicitA aa3 = (A)3;const A& ra = (A)3;int* p = NULL;A aa4 = p;// 内置类型  整形和浮点 隐式互相转int i = 0;double d = i;const double& r = i;

1.3.1 什么是类型转换

 当发生类型转换时,就会产生一个临时变量来保存隐式转换后的对象。然后用这个临时对象拷贝构造aa。这就是类型转换

1.3.1.1 单参数类型转换
class A
{
public:A(int a):_a(a){}A(int* p){}int _a = 0;
};int main()
{A aa1(1);A aa2(2);A aa3 = (A)3;const A& ra = (A)3;int* p = NULL;A aa4 = p;

1.3.1.2 多参数的类型转换
class Date
{
public:Date(int year = 1, int month = 1, int day = 1): _year(year), _month(month), _day(day){}private:int _year;int _month;int _day;
};int main()
{Date d1(2023, 11, 2);Date d2 = (2023, 11, 3); // 等价于 Date d2 = 3;Date d3 = 2023;// C++11 支持这种写法Date d4 = { 2023, 11, 2 };const Date& d5 = { 2023, 11, 2 };
}

1.3.2 为什么内置类型对象能隐式转换为自定义类型对象

因为有类的单参数构造函数

  1. 如果类的单参数构造函数的参数类型为int,那int类型的内置类型变量就可以隐式转换为自定义类型对象
  2. 如果类的单参数构造函数的参数类型为double,那double类型的内置类型变量就可以隐式转换为自定义类型对象
  3. 以此类推,类的单参数构造函数的参数类型是哪种,哪种类型的变量就可以隐式转换为自定义类型对象

 1.3.3 explicit关键字的使用

不想让隐式类型转换发生就在构造函数名前加explicit关键字

class A
{
public:explicit A(int a):_a(a){}A(int* p){}int _a = 0;
};int main()
{A aa1(1);A aa2(2);// 内置类型对象 隐式转换成自定义类型对象// 能支持这个转换,是有A的int单参数构造函数(支持传一个参数多参数带缺省也可以)支持// 不想让隐式类型转换发生,构造函数加explicitA aa3 = (A)3;const A& ra = (A)3;int* p = NULL;A aa4 = p;


二、 static成员

2.1 概念

声明为static的类成员称为类的静态成员用static修饰成员变量,称之为静态成员变量用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化(类内声明,类外定义)

 2.2 特性

  1. 静态成员所有类对象所共享,不属于某个具体的对象,存放在静态区
  2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
  3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
  4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
  5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制


http://www.mrgr.cn/news/82020.html

相关文章:

  • Codigger集成Copilot:智能编程助手
  • Python性能分析深度解析:从`cProfile`到`line_profiler`的优化之路
  • UE5材质节点Camera Vector/Reflection Vector
  • 【计算机网络】第一章·计算机网络入门
  • ubuntu初始配置
  • Android 系统 AlarmManager 系统层深度定制
  • 《机器学习》从入门到实战——逻辑回归
  • 机器学习之逻辑回归算法、数据标准化处理及数据预测和数据的分类结果报告
  • JDK 21 的重要特性
  • Java方法使用详解:从基本概念到进阶技巧
  • 一个响应式的系统 具有黑白俩个主题
  • 学习vue3的笔记
  • Vue 中el-table-column 进行循环,页面没渲染成功
  • 基本算法——聚类
  • Android原生Widget使用步骤
  • Unity开发AR之Vuforia-MultiTarget笔记
  • 在React中引入tailwind css(图文详解)
  • 刷机TP-Link tp-link-WDR5660
  • 打印进度条
  • vue下载和上传的地址动态ip地址配置方法
  • sentinel-请求限流、线程隔离、本地回调、熔断
  • SAP SD信贷管理后台配置(上)
  • (一)开发环境搭建以及配置
  • K8S-LLM:用自然语言轻松操作 Kubernetes
  • [创业之路-225]:《华为闭环战略管理》-4-华为的商业智慧:在价值链中探索取舍之道与企业边界
  • 2024年中国新能源汽车用车发展怎么样 PaperGPT(二)