今天在《程序员面试宝典》中看到了很有意思的一道题。问题是这样的:
C++中的空类默认产生哪些成员函数?(p104,面试例题2)
这道题的答案是默认构造函数,析构函数,拷贝(复制)构造函数以及赋值函数。构造函数和析构函数对于我们来说是比较熟悉的,但是拷贝构造函数和赋值函数就比较少见了。
拷贝构造函数指的是以一个对象作为参数来初始化同类型的另一个对象。
赋值函数相当于重载了“=”号,使得“=”可以作为赋值运算符进行赋值操作。
举个例子:
class A
{
public:
A(){} //默认构造函数
A(A& a){i=a.i;} //拷贝构造函数,将形参对象里的成员变量拷贝到新的对象当中。一般系统会默认给出,不需要自己写出来。
A& operator=(const A& a); //赋值函数,相当于运算符的重载。
private:
int i;
}
void main()
{
A a; //调用默认构造函数。
a.i = 10;
A b(a); //调用拷贝构造函数。将a作为形参初始化b对象。
A c;
c=b; //调用赋值函数。类似于拷贝构造函数。
}
对于一个C++的空类,即
class empty
{
public:
};
而言,相当于系统为我们编写了这样的一个类:
class empty
{
public:
empty(){}; //系统自动生成默认构造函数。
~empty(){}; //系统自动生成默认析构函数。
empty(empty& e){}; //系统自动生成拷贝构造函数。
empty& operator=(const empty& e); //系统自动生成赋值函数。
}
另外,在网上还看到了一道相关的题目,一并记录到这里。原题如下:
请问以下代码中一共调用了几次复制构造函数。
#include<iostream.h>
class A
{
public:
A(int n){i=n;}
A(A&other){i=other.i;
cout<<"copy";}
private:
int i;
};
A f(A a)
{
A temp(a);
return temp;
}
void main()
{
A a1(1),a2(2);
A a3(a1);
a2=f(a3);
}
答案是 4次。
其中,执行函数A a3(a1)时调用一次,执行函数f(a3)时调用了3次。由于f(a3)是值传递,因此系统会使用复制构造函数把参数的对象实例复制一个副本放入堆栈中,这是f(a3)第一次调用复制构造函数;第二次调用发生在函数体A temp(a)中,这里很明显,不多做解释;第三次调用发生在函数体return temp语句中,因为返回的是一个对象,所以系统再次调用复制构造函数生成返回对象的实例。
注:main函数中的a2 = xxx一句并不会调用复制构造函数,因为a2本来已经存在,这里采用memcpy策略将a3的值赋给a2。