看代码如下:
#include
using namespace std;
class B {
public:
B() {
cout << "B construction --- address is :" << this << endl;
}
~B() {
cout << "B destruction --- address is :" << this << endl;
}
B fool(const B& b1) {
cout << "fool func" << endl;
return b1;
}
};
int main() {
B b1, b2;
b1 = b1.fool(b2); // 21
return 0;
}
问在第21行结束后,会执行几次几次析构函数,是否会执行构造函数?
b1 = b1.fool(b2); // 21
先说答案:执行一次析构函数,析构的是给 b1赋值的临时变量
加代码检验:
这里的return 0这句话还没执行,所以b1,和b2还没有执行析构,上面的代码中单单32行这段代码执行完毕之后,就有了下列输出:
fool func
复制构造函数 — address if003AFCFF
operator=
B destruction — address is :003AFCFF
过程如下:
如果24行返回值类型是一个引用,则不会产生临时变量,因为直接引用了return *this的地址,将其直接转给b1,如下图:
但是返回局部变量的引用,并不是一个好习惯,这里还是没问题的,因为fool传尽力的就是全局变量的引用,并没有因为函数的结束而消失,如果是如下代码:
B& fool(const B b1)
{
cout << "fool func" << endl;
return *this;
}
就会返回局部变量的引用 (形参b1的地址)b1在函数结束时就被释放。
二、返回引用
返回引用是返回一个地址,且指向该类的引用可以直接调用成员函数来修改成员变量,
另外尽量不要返回局部对象的地址,因为当函数调用完成后,局部对象会被销毁,此时返回的地址不存在,进而导致后续产生错误。
三、返回对象
返回对象则是通过调用默认复制构造函数,来在另一地址重新生成了另一个对象。
用下列代码来进行测试,A是B的基类,B的类成员中有一个C类的对象,在主函数中实例化一个B类的对象,并打上断点来查看机型顺序:
#include
using namespace std;
class A {
public:
A() {
cout << "Base Constructor" << endl;
}
virtual ~A() {
cout << "Base Destruction" << endl;
}
};
class C {
public:
C() {
cout << "Class Member Construction" << endl;
}
virtual ~C() {
cout << "Class Member Destruction" << endl;
}
};
class B : public A {
public:
B() {
cout << "Derived Construction" << endl;
}
virtual ~B(){
cout << "Derived Destruction" << endl;
}
class C c;
};
int main() {
B b1;
return 0;
}
在38行打上断点以后,函数的执行顺序为:
38->27->7 8(Base Con) 9-> 27 -> 17 18(Member Con) 19 -> 27 28(Derived Con) 29->39
以上为主函数中第38行执行的内容,输出如下:
继续执行39行
39-> 30 31(Derived Des) 32-> 20 21(Member Des) 22 -> 32 -> 10 11(Base Des) 12 -> 32 -> 39 ->40
以上为主函数39行(return 0)所析构的内容顺序:
说明了派生类中构造函数的执行顺序:
析构循序倒过来即可。
以上几点基本问题的补充: