Constructor and Destructor¶
约 1019 个字 98 行代码 预计阅读时间 5 分钟
When is a Ctor/Dtor called?¶
- 全局变量的构造函数在
main
函数之前被调用,析构函数在main
函数之后,整个程序结束时被调用。 - 静态局部变量的构造函数在第一次运行到这一行代码时被调用,析构函数在程序结束时被调用。静态局部变量的构造函数只会被调用一次。
- 局部变量的构造函数在每一次运行到这一行代码的时候被调用,析构函数在离开作用域时被调用。
- 对象的构造函数在对象被创建时被调用,析构函数在对象被销毁时被调用。
RAII¶
RAII(Resource Acquisition Is Initialization) 是一种编程习惯,指的是在对象的构造函数中分配资源,在析构函数中释放资源。RAII 的好处是可以避免内存泄漏和资源泄漏。
把资源的生命周期和对象的生命周期绑定在一起,确保资源在对象的构造函数中被分配,在析构函数中被释放。这样可以避免内存泄漏和资源泄漏的问题。
Initializer List¶
Constant objects¶
我们可以给对象的 member function
加上 const
关键字,与普通的 member function
加以区分,这样如果我们的对象是 const
的话,程序会自动调用 const
版本的 member function
,从而可以保护对象的成员变量不被修改。
C++ | |
---|---|
如果对象中有一个字段是 const
的话,那么这个字段的初始化必须在 initializer list
中进行。
C++ | |
---|---|
Static members¶
C++ | |
---|---|
静态成员变量的初始化必须在类外进行,不能在类内进行。
C++ | |
---|---|
运行上边的代码,我们期望的输出结果是 1, 0
,但是实际上输出是 1, 1
,这是由于 i
是 static
的,它放在了全局数据区,所以可以把它当成是一个全局变量。也就是说,static
变量是所有对象共享的,所以它的值是共享的。
同时注意 static function
不跟某个对象绑定在一起,这意味着我们不能在 static function
中使用 this
指针。我们可以把 static function
当成是一个全局函数,只不过它的作用域被限制在了类里面而已。
C++ | |
---|---|
所以我们可以有两种调用的方式
C++ | |
---|---|
Compile-time constants in class¶
C++ | |
---|---|
上边的代码是错误的,因为 size
不是在编译时刻确定的,我们可以用 static
来修正这个问题。
C++ | |
---|---|
Multiple definition¶
C++ | |
---|---|
我们之前讲过,如果在头文件中定义了一个类,那么在每一个引用这个头文件的源文件中都会生成一个类的定义,这样就会导致重复定义的问题。但是上边的代码是没有问题的,要想知道为什么,我们可以先了解一些事情。
我们的函数调用是要往栈里边写东西的:
- Push arguments
- Push return address
- Push frame pointer
- Prepare return values
- Pop all the pushed items
函数的调用是有代价的,我们想要通过某种办法,让这个代价消失,这就引入了 inline
关键字,它可以直接在调用的时候把函数的代码放到调用的地方,这样就可以避免函数调用的开销了。
C++ | |
---|---|
inline
类似于我们之前学过的宏,但是宏是预处理,相当于直接文本替换。
C++ | |
---|---|
而 inline
则不存在上边的问题,它是编译器帮我们优化的。
C++ | |
---|---|
Warning
inline
只是一个请求,并不是强制的,由编译器自己决定。
比如如果一个递归函数太深了,编译器就会选择不 inline
了。
由于我们调用 inline
的时候需要看到函数体,不然无法进行替换,所以如果函数定义在头文件中,inline
会有一个机制,multiple definitions are permitted
,也就是说可以重定义,inline
会自动找一份合适的定义保留。
回到上面的问题,为什么那段代码没有问题呢?因为定义在 class
里边的 member function
默认就是 inline
的,所以我们可以在头文件中定义它们,而不会导致重复定义的问题。