C++-非平凡构造函数

C++ 非平凡构造函数

什么是非平凡(non-trival)构造函数?

简单来说,“平凡”意味着这些特殊的成员函数用很朴素的方式完成它们的工作。而”很朴素的方式“这个说法对不同的函数有不同的意义。

  1. 对默认构造函数和析构函数来说,“平凡”意味着什么也不做。
  2. 对拷贝构造函数和拷贝赋值函数来说,“平凡”意味着只做简单的内存拷贝。

下面是具体的规则:

规则一:如果你为类显式定义了一个构造函数,那么它就是非平凡的构造函数,即使这个构造函数里什么都没做。因此,平凡的构造函数一定是你没写构造函数,编译器为你生成的那个函数。这个规则对其他几类函数同样适用。

可以想象,如果一个类同时拥有平凡构造函数,平凡拷贝构造函数,平凡拷贝赋值函数,平凡析构函数,那么这个类应该是非常简单的结构(这些函数咱肯定都没写)。

规则二:规则一只是平凡的必要条件,除了不主动实现这些函数之外,“平凡构造函数”不允许在对象创建做任何隐式的初始化,“平凡拷贝构造函数”和“平凡拷贝赋值函数”不允许在对象拷贝的过程中做任何额外的操作。

规则二不太好理解,举两个例子:如果类有虚函数,那么相比没有虚函数的类,编译器为你生成的构造函数里需要进行额外的初始化(初始化虚指针等),因此这个类的构造函数不能认为是平凡的。同理,如果一个类有虚基类,这个类的构造函数也不是平凡的,因为这个类在构造的时候也需要进行一些隐式的额外操作来支持虚继承机制。

有虚函数和有虚基类这两种情况下,对象不能由简单的原始内存复制例程(如memcpy)进行复制,而需要进 行额外的操作才能正确地重新初始化副本中的隐藏指针。由于这个原因,上述两种类的拷贝构造函数和拷贝赋值操作符也都不是平凡的。

规则三:“平凡”是递归定义的,意味着类的每个组件都要是平凡的。这里说的组件,包括基类,成员变量。因为我们知道,类构造时会先调用基类的构造函数,调用成员变量的构造函数,如果它们不是平凡的,那么自己的构造函数怎么会是平凡的?

对于如下两个类的定义:

1
2
3
4
5
6
7
8
9
10
11
// 没有显式定义 ctor、copy、assignment、dtor 所以均为 trival
class T1 {
int a; // POD类型
};

// 没有显式定义 copy、assignment、dtor 所以均为 trival
class T2 {
T1() {} // 显示定义 ctor,为 non-trival ctor
int a; // POD
std::string b; // 非POD
};

如何判断一个类是否为trival构造函数呢?c++ STL提供了判断平凡性的类型萃取器,只要#include<type_traits>就可以使用。

note

在C++ 11中,有了 =delete 和 =default 关键字,如果使用这两个关键字,那么该函数就为 trival 类型,编译器会为其做出进一步优化。相对于空函数体而言,则更具有优势。

对于如下测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <vector>
using namespace std;

class TestA {
public:
TestA() {}
~TestA() {}

private:
int a;
};

class TestB {
public:
TestB() = default;
~TestB() = default;

private:
int a;
};

#define CLASS_NUM 10000000

int main() {
clock_t start, end;
start = clock();
TestA* a = new TestA[CLASS_NUM];
delete[] a;
end = clock();
cout << "non-trival: " << (double)(end - start) / CLOCKS_PER_SEC << endl;

start = clock();
TestB* b = new TestB[CLASS_NUM];
delete[] b;
end = clock();
cout << "trival: " << (double)(end - start) / CLOCKS_PER_SEC << endl;
return 0;
}

输出为:

1
2
non-trival: 0.205
trival: 0.025

参考:什么是非平凡(non-trivial)构造函数


C++-非平凡构造函数
https://gstarmin.github.io/2023/07/17/Cpp-非平凡构造函数/
作者
Starmin
发布于
2023年7月17日
许可协议