索引
C++【对象模型】| 【01】简单了解C++对象模型的布局
C++【对象模型】|【02】构造函数何时才会被编译器自动生成?
C++【对象模型】|【03】拷贝构造是如何工作的,何时才会用到呢?
C++【对象模型】 | 【04】程序在内部被编译器如何转化?
C++【对象模型】 | 【05】类与类之间各种关系下对数据成员的存取、绑定、布局
C++【对象模型】| 【06】类中各种函数的刨析
C++【对象模型】| 【07】构造、析构、拷贝做了哪些事?
C++【对象模型】| 【08】类在执行期会处理哪些事呢?
C++【对象模型】| 【09】类模板、异常处理及执行期类型识别
一、默认构造函数
1、何时默认构造函数会自动生成
当类没有提供默认构造时,如果编译器需要它,则将会为其添加上;如果是程序的需要,则需要我们手动为它添加;
- 并不是没有声明默认构造时,编译器就自动生成;只在编译器真正需要的时候;
如何区分编译器需要还是程序需要
当使用`foo f;`创建一个对象时,
- 【编译器】由于类中没有提供默认构造函数,编译器会自动为其生成使之能被创建成功;
- 【程序】类中的数据成员初始化交给构造函数,当想要默认的构造函数能够初始化数据成员(a)时,则我们需要手动提供;
class foo{
public:
void func();
private:
int a;
}
2、编译器合成有用的构造函数四种情况
2.1 类中内含带有默认构造的类成员
当一个类内含一个类成员(带默认构造),此时编译器需要为类合成一个默认构造函数(在真正需要被调用时才发生);
class B{}
class A{
private:
B b;
}
编译器(不同的文件)如何避免合成多个默认构造?
其编译器合成的函数都是按内联的方式完成,具有静态链接,不会被文件外使用、看到;
案例
class Foo { public: Foo(), Foo(int) }
class Bar { public: Foo foo; char *str; }
void func() {
Bar bar;
}
【如果没有Bar没有提供默认构造】
当创建一个Bar对象时,由于内含一个Foo成员,其foo必须要在它构造的时候初始化;
而Bar没有提供默认构造,故编译器需要为它生成一个能够调用Foo的默认构造来处理成员Bar::foo;【编译器责任】
但生成的默认构造不为str做初始化【程序员责任】
===> 编译器合成的默认构造
inline Bar::Bar() { foo.Foo::Foo() }
【如果Bar提供默认构造】
====> Bar::Bar() { str = 0; }
此时程序的需求被满足,但没有对foo进行初始化提供,而此时应该有默认构造,故编译器不会再次生成默认构造;
那编译器会怎么做呢?
编译器会将初始化foo的代码插入在默认构造的用户代码前(str之前);
如果类中内含多个其他类,则将按照声明顺序将其插入;
2.2 带有默认构造的基类
【当子类没有默认构造】
当继承的基类含有默认构造时,若子类没有默认构造,则编译器会自动生成【用来调用基类的构造函数】;
【子类提供构造函数】
当子类提供构造函数但没有默认构造,则编译器会扩张每一个构造函数(添加基类的默认构造代码),不会合成新的默认构造;
【注意】成员类的调用次序在继承后;
class A{
public:
A() { cout << "A" << endl; }
~A() { cout << "~A" << endl; }
};
class B {
public:
B() { cout << "B" << endl; }
~B() { cout << "~B" << endl; }
};
class C : public A{
public:
C() { cout << "C" << endl; }
C(int v) {}
private:
B b;
};
int main() {
C c;
cout << "------------" << endl;
C cc(1);
cout << "------------" << endl;
return 0;
}
2.3 带有虚函数的类
- 当class声明/继承一个virtual function;
- 当class派生自一个继承串链,其中一个或更多的virtual base classes;
以上两种情况也会合成virtual constructor;
由于虚函数是通过虚表来进行存放,而虚表通过类中的vptr存储该地址,发生在编译期间,编译器必须给vptr设定初值,且放
置地址,这些都在构造函数中完成,故当函数有以上两种情况时,编译器会合成;
2.4 带有一个虚基类的类
编译器必须让虚基类在每个子类中的位置,能够在执行期准备好,在编译期不能够确定真正的类;
编译器会在构造中安插允许虚基类的执行器存取操作代码,故若没有构造函数,则编译器会自动合成;