问题的背景:
在经典C++中,类成员只能在构造函数的初始化列表中进行初始化。从C++11开始,可以直接在类内声明中指定默认值(成员初始化器),以提高代码的可读性和安全性。
问题:
有几种方法可以给类成员设置值:直接在声明中(类内)、通过构造函数的初始化列表和在构造函数体内。不同的方法会影响性能和语义;理解不当会导致不必要的复制或默认析构函数的问题,以及常量和引用的错误。
解决方案:
class MyClass { int x = 42; };
class MyClass { const int y; MyClass(int val) : y(val) {} // 否则会报编译错误 };
class MyClass { std::string s; MyClass() { s = "hello"; } // 首先是默认构造,然后赋值 };
关键特点:
成员的初始化顺序是按照在类中声明的顺序,还是按照初始化列表的顺序?
初始化顺序始终是按照成员在类中声明的顺序,而不是按照初始化列表的顺序。顺序混乱对依赖成员是危险的。
class A { int x = 1; int y = 2; A() : y(10), x(20) {} }; // x在y之前初始化,尽管在列表中的顺序不同
如果构造函数体内尝试初始化常量成员,但没有在初始化列表中初始化,可以吗?
不可以。常量只能在初始化列表中初始化。在构造函数体内赋值将导致编译错误。
如果通过类的类内初始化器为成员设置默认值,并在构造函数的初始化列表中重写该值,会发生什么?
将使用初始化列表中的值。默认值仅在列表中未指明时使用。
class C { int x = 10; C() : x(20) {} // x将等于20 };
类处理文件。文件被声明为std::ofstream,并在构造函数体内初始化。风险:在默认构造函数中可能会创建无效的std::ofstream,导致文件操作错误。
优点:
缺点:
文件在初始化列表中初始化,防止错误地使用处于无效状态的文件,而使用类内初始化器的默认数据成员。
优点:
缺点: