在看侯勇的深度探索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 个观点是有问题的:

  1. 没有 default consturctor 的 class,一定会合成 default consturctor
  2. 合成的 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 的条件是几乎一样的:

  1. class 里 member object && member object has copy consturctor;
  2. class derived from a base class that has copy consturctor;
  3. class 里有 virtual functions;
  4. 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 的顺序来初始化的.