C++ 构造函数语义学
- what is explicit
- implicit trivial default consturctor
- implicit non-trivial default consturctor
- copy consturctor
- 程序转换语义学
- member initialization list
在看侯勇的深度探索C++对象模型
.觉得很有意思, 记录一下
what is explicit
显示的意思是告诉编译器 不要为我自己调用该构造函数,除非代码里显示使用了该调用.
implicit trivial default consturctor
trivial 就是没啥用的意思..如果一个类没定义过 any consturctor, 那么编译器会为这类生成一个 trivial default consturctor.
class A {
public:
int c;
}
A a;
implicit non-trivial default consturctor
场景 1 带有 default consturctor 的 member object
A 中包含 B,B 定义了 default consturctor, A 没有.
class B { public: B(){cout << "consturct B!"<< endl;}}
class A {
public:
B b;
int c;
}
编译器为满足类能正确编译
需求,会构造一个 A 的 default consturctor.
注意. 其实 c 是不太管的,(一般是 0),因为 c 不是类
A::A() { b.B::B();}
如果 A 定义了 default consturctor,但是里面没有构造 B,同样地,为了类正确编译
,仍然合成一个新的 constructor.包括原定义的和 B 的构造.
class B { public: B(){cout << "consturct B!"<< endl;}}
class A {
public:
A() { c = 100;}
B b;
int c;
}
合成的 constructor:
A::A() {
b.B::B();
c = 100;
}
场景 2 带有 default consturctor 的 Base class
B 派生自 A, B 没有定义 consturctor, A 定义了,那么 B 会被生成 non-trivial default consturctor.
场景 3 带有 virtual function 的 class
带有虚函数的类, 因为其 object 首地址,必须包含一个 vptr,指向一个 vtbl(虚函数表) 对这样的类的,编译器都会安插相应的代码到 object 里设定 vptr 初值.
如果自己定义了 construtors,就在 construtors 里初始 vptr,如果没有,则在合成的 default consturctor 里初始 vptr,(此时合成的也叫 non-trivial default consturctor)
看完上面的,可以理解下面 2 个观点是有问题的:
- 没有 default consturctor 的 class,一定会合成 default consturctor
- 合成的 default consturctor 会给所有的 data member 一个固定的默认值?
copy consturctor
4 个场景会用到:
以对象构造时, 定义对象初始化时,函数返回,当做函数参数时:
class A {
public:
//copy consturctor
A(const & a) { m_b = a.m_b;}
int m_b;
}
//1
A objA1;
A objA2 = objA1;
//2
A objA3 = A(objA1);
//3
void A func(A a) { ...}
func(objA1);
//4
void A func() { A objA1; return objA1;}
func();
如果没有定义 copy consturctor 怎么办
在编译器觉得有必要
的时候,自然是合成一个 copy consturctor 了,所谓必要,就是看 class 是否展现出bitwise copy semantics
.
其实 bitwise copy,就是源类中的成员变量 中的每一位 都逐次 复制到 目标类中.最简单的情况是某个类没有 member object,此时直接执行 data member 的一一赋值即可:
a = m.a;
b = m.b;
ptr = m.ptr
但是会有 1 个问题,如果是指针赋值, 比如 m.ptr 来自全局变量,而 ptr 是局部 object 的成员, 局部释放后,全局也释放了,这是有问题的. 应该和浅拷贝
遇到的问题是一样的.
哪些情况不会展现 bitwise copy
(即需要合成 copy consturctor) ?其实和合成 non-travial default consturctor 的条件是几乎一样的:
- class 里 member object && member object has copy consturctor;
- class derived from a base class that has copy consturctor;
- class 里有 virtual functions;
- class derived from one or more than one base class;
程序转换语义学
先看代码,下面的代码可能被优化成什么样子?
X foo() {
X xx;
...
return xx;
}
X x = foo();
如果没有优化, return 时 1 次 copy constructor, 赋值时又 1 次, 略效率低.
实际编译器都会优化掉只剩 1 次拷贝,叫做Named Return Value
优化,如:
X __temp;
//增加额外参数
void foo(X & t) {
X xx;
...
//1次copy
t.X::X(xx)
return ;
}
foo(__temp);
结论: 面对以 1 个 class object 作为另 1 个 object 的初值时,编译器多较大的空间去发挥优化,可能带来效率的提升,副作用是你不能安全的规划 copy constructor,不知道什么时候具体的用了,必须视执行情况而定.
member initialization list
member initialization list 就是构造函数的 data member 初始化的简略写法,如:
class X {
public:
X(int a, int b, int c): m_a(a),m_b(b),m_c(c) {
m_d
}
int xval(int x) { return 10*x;}
private:
int m_a;
char m_b;
int m_c;
int m_d;
}
m_a(a),m_b(b),m_c(c)
在真正插入 constructor 里时(在 constructor 的第 1 行代码之前插入), 不一定是按 a,b,c 的顺序来初始化的.