这是一道外企的C++笔试题,要求写出结果并分析:
#include <iostream.h>
class A
{
public:
static int num;
A() {cout<<num++;}
~A(){cout<<num--;}
};
int A::num=0;
class B: public A
{
public:
B(){cout<<num++;}
~B(){cout<<num--;}
A f(A a) {return a;}
};
int main()
{
A a;
B b;
b.f(b);
return 0;
}
输出结果是 0123210-1-2,下面对于每次输出给出分析:
0 在A a, 调用了A的构造函数,输出0(注意对num使用的是后减)
1 在B b, 先调用A的构造函数,输出1
2 在B b, 再调用B的构造函数,输出2
在b.f(b), 首先传参时,A temp_a = (A)b, 这里通过拷贝构造函数生成temp_a ,再将 temp_a 赋给 f中的a,也是通过拷贝构造
数, 所以这里没有输出,即没有构造函数的调用,大家不要认为生成对象都要经过构造函数。
3 f返回时, temp_a 析构,调用A的析构函数 输出3
2 f返回时, a析构,调用A的析构函数 输出2
在return a,虽然main中没有定义变量接收 f 的返回值,但是实际上 f 返回的时候生成了一个临时值,如temp_a
1 f返回后, 返回的temp_a 析构,调用A的析构函数 输出1
0 main返回 b析构,先调用B的析构函数, 输出0
-1 main返回 b析构,再调用A的析构函数, 输出-1
-2 main返回 a析构,调用A的析构函数, 输出-2
构造函数的调用顺序: 先基类的构造函数,后派生类的构造函数。
析构函数的调用顺序: 先派生类的析构函数,后基类的析构函数。
先构造的对象后析构,后构造的对象先析构。
拷贝构造函数被调用的三种情况:
1.程序中需要建立一个新的对象,并用另一个同类对象对它初始化的时候。 例如: A a; A a_1 = a;
2.当函数的参数为类的对象的时候。 例如 :
void f(A a){}
void main()
{
A a_1;
f(a_1);
}
3.当函数的返回值类型为类的对象的时候。 例如:
A f()
{
A a_1;
return a_1;
}
void main()
{
A a;
a = f();
}
当自己定义的类中没有定义拷贝构造函数的时候,系统在需要调用拷贝构造函数的时候自动调用默认的拷贝构造函数。
这时候需要注意一个问题:
当类的成员变量中有一个指针变量,而在生成类对象后,对象的这个指针变量指向了一块堆空间,这时候,如果使用这个对象去初始化一个新的对象的时候,调用了默认的拷贝构造函数。这样,两个对象的指针都指向了同一块对空间,如果类的析构函数里对这个指针进行了delete操作,将导致这两个对象析构的时候,同一块堆空间被释放两次,进而出现错误。
这其实就是传说中的对象的深拷贝和浅拷贝的问题,要解决这个问题,就要定义自己的拷贝构造函数,在复制对象中指向堆空间的指针的时候,申请新的堆空间,并进行内容的复制。
从今天开始我要做一个自由的人,喂马、劈柴,神游世界。