有很多同学搞不清类的构造顺序,到底是基类先构造还是子类先构造,什么左值,什么是右值,构造函数为什么不能是虚基类,虚构函数为什么的是虚基类,且听AIR老师为你们徐徐道来
第一个基类与派生类的构造顺序
class TestA
{
public:
TestA(){ printf("construction TestA\n"); }
~TestA(){ printf("destroy TestA\n"); }
};
class TestB : public TestA
{
public:
TestB(){ printf("construction TestB\n"); }
~TestB(){ printf("destroy TestB\n"); };
};
test:
{TestB testb;}
打印顺序:
construction TestA
construction TestB
destroy TestB
destroy TestA
即是先构造基类,后构造子类,先析构子类,后析构基类
第二:要是在子类的初始化列表中初始化基类,是怎么个调用顺序呢
调试可以发现,可以根据初始化列表中基类的初始化调用相应的构造函数,要是不用初始化列表初始化基类则调用默认的构造函数,不能动态的给基类成员赋值,这个时候同学们可能会有疑问到底是先调用基类的构造函数呢还是子类的初始化列表先初始化呢,其实这个的了解一个类的初始化过程,先初始化静态成员变量,这个是编译的时候就已经赋值在一个全局堆栈区内,静态成员是跟类绑定,并不是对象绑定(对象即实例,他是由类实例化来的),对象的创建是在运行时(静态成员除外),在这个阶段会先扫描初始化列表,看有没有调用基类的初始化,有则调用相应的构造函数,没有则用默认构造函数创建基类,然后执行初始化列表,然后子类构造函数
第三:构造函数为何不能是虚函数,析构函数为何需要时虚函数
要理解这个的对虚函数进行了解,在对类实现继承的时候,每个带有虚函数的类都带有一张虚表,指向虚函数的首地址,通过虚表实现类的多态,构造函数为何不能是虚函数很好理解,虚表的创建是在对象构造的时候创建的,对象都没有构造,怎么调用虚函数,就不能创建对象了。
析构函数为何需要是虚函数,很多时候我们给外部提供接口的时候,不想他们知道我们内部是怎么操作的,只给他们一个接口类的返回,但是实际的操作是子类实现的,返回的时候将子类转化为基类返回,若析构函数不是虚函数,变量析构的时候就只调用基类的析构函数,不会去调用子类的,就会造成内存泄漏
第四:左值与右值
左值就是赋值号(=)左边的叫做左值,右边的叫做右值
有些既可以是左值又可以是右值,但是有些只能是左值或者只能是右值
普通变量可以是左值也可是右值,但是有些特殊的
比如说引用它就是个左值,不能放到赋值号的右边
a++就是一个右值,不能作为左值
左值与右值的区别在于:
左值是可寻址的,知道其存储位置
右值是可以从地址中取得数据
为何说A++是一个右值呢,这个就得解释一下++a与a++的实现,即前置++与后置++重载
TestA& operator++() { //前置++
++a;
return *this;
}
cont TestA operator++(int) { //后置++
A a = *this; ++*this; returnn a; }
注意为了区分前置与后置,在后置的参数中加了int,只是为了区分而已,可以看出前置++返回的是一个引用,表示是可寻址的,后置++返回的是一个零时变量,不可寻址