【C++】类与对象—— 初始化列表 、static 静态成员、
类与对象
- 1 再谈构造函数
- 1.1 构造函数体赋值
- 1.2 初始化列表
- 语法:
- 建议:
- 初始化顺序:
- 注意:
- 1.3 explicit关键字
- 2 static 静态成员
- 2.1 概念
- 2.2 声明成员变量
- 2.3 使用类的静态成员
- 2.4 定义静态成员
- 总结
- Thanks♪(・ω・)ノ谢谢阅读!!!
- 下一���文章见!!!
1 再谈构造函数
1.1 构造函数体赋值
在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值,以我们之前实现的Date类对象为例。
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 初始化列表
语法:
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
class Date { public: Date(int year, int month, int day) :_year(year) ,_month(month) ,_day(day) { } private: int _year; int _month; int _day; };
建议:
有时我们可以忽略数据成员初始化的和赋值之间的差异,但并非总是这样。
如果成员是const 或 引用变量的话,必须将其初始化。类似的如果存在自定义类型并且该类不存在构造函数时,也必须将其初始化。例如:
class A { private: int a; }; class Date { public: Date(int year, int month, int day) { _year = year; _month = month; _day = day; } private: const int _year; int& _month; int _day; A a; }; int main(){ const Date a1(2024,2,24); return 0; }
来看看报错:
显然我们需要初始化来帮助我们解决这些问题。
在很多类中初始化和赋值的区别事关底层效率的问题:前者直接初始化数据成员,后者则先初始化再赋值。除了效率问题外更重要的是,一些数据成员必须初始化。所以一般建议养成使用初始化列表的习惯,这样可以避免某些意想不到的编译错误,特别是遇到类包含构造函数初始值的成员时。
初始化顺序:
显然在构造函数中每个成员只能出现一次。否则给同一个成员赋两个不同初始值有什么意义呢?
需要注意的是初始化列表不限定初始化的执行顺序,因为成员初始化的顺序与他们在类出现顺序一致,第一个成员先初始化,然后第二个,以此类推,因此构造函数初始化列表的前后位置并不影响实际的初始化顺序。
但是下面这个例子不同:
class A { public: A(int a) :_a1(a) ,_a2(_a1) {} void Print() { cout A aa(1); aa.Print(); } public: // 1. 单参构造函数,没有使用explicit修饰,具有类型转换作用 // explicit修饰构造函数,禁止类型转换---explicit去掉之后,代码可以通过编译 explicit Date(int year) :_year(year) {} private: int _year; int _month; int _day; }; void Test() { Date d1(2022); // 用一个整形变量给日期类型对象赋值 // 实际编译器背后会用2023构造一个无名对象, //最后用无名对象给d1对象进行赋值 } public: // 2. 虽然有多个参数,但是创建对象时后两个参数可以不传递, //没有使用explicit修饰,具有类型转换作用 // explicit修饰构造函数,禁止类型转换 explicit Date(int year, int month = 1, int day = 1) : _year(year) , _month(month) , _day(day) {} Date& operator=(const Date& d) { if (this != &d) { _year = d._year; _month = d._month; _day = d._day; } return *this; } private: int _year; int _month; int _day; }; void Test() { Date d1(2022); // 用一个整形变量给日期类型对象赋值 // 实际编译器背后会用2023构造一个无名对象, //最后用无名对象给d1对象进行赋值 d1 = 2023; // 将 1 屏蔽掉,2放开时则编译失败,因为explicit修饰构造函数, //禁止了单参构造函数类型转换的作用 } public: Date(int year, int month, int day) :_year(year) ,_month(month) ,_day(day) { } static void count(); private: int _year; int _month; int _day; }; void count(){ }; int main(){ Date d1; Date* d2 = &d1; d2-count(); d1.count(); return 0; }
The End