1.构造函数还有初始化另一种方式,初始化列表
初始化列表和构造函数体内 {} 赋值的区别:
初始化列表,是对象成员初始化的地方,当函数栈帧创建时,为对象分配内存空间,编译器会先执行初始化列表,直接对对象进行初始化,即创建对象后立即初始化。
构造函数体内赋值,是对象成员赋值的地方,即在所有成员都完成定义和初始化之后,对已经初始化后的成员进行赋值。
两者的区别不是是否为对象开辟空间(函数栈帧创建就已经开辟),而是初始化时机 和是否能修改已经初始化的值
对不同类型成员的影响
1.const成员,不能修改内容,意味着只有在定义的地方,直接初始化,所以走必须初始化列表,而不能 "赋值"
2.引用成员,必须定义的时候初始化,不能是空引用,必须初始化列表
3.自定义类型作为成员变量,对于没有默认构造函数的类,(也就是我们自己为了这个类显示写了带参数的构造函数,编译器不再生成默认的无参构造),那么在创建外层类对象的时候,编译器就没办法自动调用这个成员变量的无参构造,(因为没有),此时就需要在 外层类 的初始化列表中,显式调用这个自定义类型的带参构造。
class Time { public: // 手动写了带参构造,编译器不再生成 Time() 默认构造 Time(int hour) :_hour(hour) {} private: int _hour; }; class Date { public: Date(int year, int month, int day) :_year(year), _month(month), _day(day), _t(1) // ✅必须在这里显式初始化Time成员_t,调用Time(int) {} private: int _year=1; int _month=1; int _day; Time _t; // Time没有默认构造函数! };初始化列表会直接调用该类型的有参构造函数,完成初始化。构造函数会先调用默认构造,再调用赋值运算符完成赋值。
函数体内赋值或初始化列表可以混着用
尽量使用初始化列表,而不是在构造函数体内赋值,一部分原因是以上特殊的3类只能通过初始化列表进行初始化。
注意,
- 1.初始化列表中成员变量初始化的顺序,按照声明的顺序初始化,声明的顺序是在内存中存放的数据,建议声明的顺序与初始化列表中的顺序一致。
- 2.如果类中声明了自定义类型的成员,但是在初始化列表中没有显示初始化,会自动调用默认构造(无参)来完成初始化,情况1,如果该自定义类型有默认构造,那就自动调用其默认构造,情况2,如果没有默认构造,无法自动初始化,会报错。
- 3.无论是否在初始化列表中显示成员初始化,每个成员都要走初始化列表初始化
- C++中,对象的成员变量一定会在进入构造函数{}之前完成初始化。
- 这个初始化的本质就是通过初始化列表的机制完成的,即使没显示写初始化列表,编译器也会自动生成默认的初始化列表,只是初始化的值不符合预期罢了
class Time { public: Time(int hour) : _hour(hour) { } private: int _hour; }; class Date { public: Date(int year, int month, int day) :_year(year), _month(month), _day(day), _t(1) { } private: int _year=1; //这里给的是缺省值, // 如果初始化列表没有显示初始化, // 默认会用这个缺省值初始化 int _month=1; int _day; Time _t; }; int main() { Date d1(0,0,0); return 0; }在类中直接为成员变量赋值,如,在private中的成员变量,_year=1;作用是为成员变量提供一个默认的初始化值,当没有在初始化列表中显示初始化该成员变量,编译器会自动使用这个默认值。
2.static成员变量,static修饰
被当前类的所有对象共享,不放在对象中,存在静态区,不属于对象,属于类,在类外面初始化static int _s_d;Date::_s_d=1,类外初始化
静态成员变量不能在声明的位置给初始化,因为这个缺省值是给初始化列表用的,静态成员变量不属于某个对象,不走构造函数初始化列表。
静态成员函数,没有this指针, 可以访问静态成员,但不能访问非静态的
静态成员在类里面声明,类外面初始化,也是类的成员,受访问限定符的限制,可突破类域访问
3.友元,我要访问你,我得成为你的朋友,friend声明
可突破类访问限定符,如果需要访问私有,所以用友元,包括友元类和友元函数
友元仅仅是声明 friend func,不是类的成员函数
友元关系是单项的,会破坏封装,不建议多用
4.内部类
一个类定义在另一个类的内部,内部类独立。跟定义在全局的类相比,仅受类域和访问限定符的限制
默认内部类是外部类的友元,内部类主要给外部类使用
5.匿名对象,用类型定义出的对象,之前的有名对象不仅需要类型,还需要名字
A aa1;//有名对象
A ();//匿名对象
Solution st;st.sum(4);//长期用or Solution().sum(4);//一次性,生命周期仅在这一行
匿名对象的生命周期只在当前一行
6.对象拷贝时编译器的优化
为了提高效率,编译器进行优化(主流是对于一个表达式中的连续拷贝进行合并优化)来减少一些传参和传返回值中可以省略的拷贝
A aa1=1;构造+拷贝构造(编译器优化)
const A& aa2=1;构造出一个临时对象,直接引用,无拷贝构造
临时对象的产生场景:类型转换,传值传参(需要拷贝构造,一般不优化),传返回值
传值传参返回会产生临时对象,因为aa出了作用域就销毁
有时候语义上要用传值传参,但是有缺点是要拷贝,这时候编译器就要对此进行优化
类和对象,完。