-当你创建一个类的时候,如果你没有声明任何函数的时候,C++编译器就会自动为你声明一个default 构造函数、 copy 构造函数、一个 copy assignment 操作符和一个析构函数。这些函数都是 public 的。例如,你定义了一个 person 类:
class Person {
int age;
string name;
};
当 peroson 对象被创建的时候,类中的函数被调用时,其中的构造函数、析构函数等才会被编译器创建出来。
class Person{
//Person(){...} 编译器创建的构造函数
//Person(const Person& p){...} copy构造函数
//~Person(){...} 析构函数
//Person& operator=(const Person& p){...} copy assigment操作符
int age;
string name;
}
例如:
Person p1; //default构造函数被调用
Person p2(p1); //copy构造函数被调用
p2 = p1; //copy assignment操作符
要注意到, copy 构造函数和 copy assignment 操作符,编译器创建的版本只是单纯地将来源对象的每一个 non-static 成员变量拷贝到目标对象。考虑一个 NamedObject template:
template<typename T>
class NamedObject{
public:
NamedObject(const char* name, const T& value);
NamedObject(const std::string& name, const T& value);
private:
std::string nameValue;
T objectValue;
};
因为已经声明了一个构造函数,编译器不会再创建 default 构造函数。但是由于没有声明 copy 构造函数和 copy assignment 操作符,所以编译器会创建这些函数。现在,看看 copy 构造函数地用法:
NamedObject<int> n1("Smallest Prime Number", 2);
NamedObject<int> n2(n1);
编译器生成的 copy 构造函数必须以 n1.nameValue 和 n1.objectValue 为初值设定 n2.nameValue 和 n2.objectValue 。其中, nameValue 的类型是 string,而标准 string 有个copy 构造函数,所以 n2.nameValue 的初始化方式是调用 string 的 copy 构造函数并以 n1.nameValue 为实参传入。至于另一个成员类型,因为创建对象实例的时候指定了 NamedObject<int> ,所以 objectValue 的类型是 int。因为 int 是个内置类型,所以 n2.objectValue 会以“拷贝 n1.objectValue" 的每一个bits ”来完成初始化。
至于 copy assignment 操作符,当只有编写的代码合法且有适当机会证明它有意义的时候才被编译器所创建。一旦不符合这两个条件其中之一,编译器就会拒绝为 class 生出 operator=。
以下面代码为例:
template<class T>
class NamedObject{
public:
NamedObject(std::string& name, const T& value);
private:
std::string& nameValue; //nameValue是个reference
const T objectValue; //objectValue是个const
};
如果我们创建两个实例:
std::string newDog("Mike");
std::string oldDog("Jhon");
NamedObject<int> p(newDog, 2);
NamedObject<int> s(oldDog, 20);
p = s; //如今p会发生什么?
进行 p=s 赋值操作前,p.nameValue 和 s.nameValue 都指向它们各自的 string 对象。赋值之后, p.nameValue 会指向 s.nameValue 所指的那个 string 吗?显然不会。因为C++并不允许“让 reference 改指向不同对象”。所以,理所当然编译器会拒绝那一行的赋值动作。如果你打算在一个内含 reference 成员的 class 内支持赋值操作,你就必须自己定义 copy assignment 操作符。
面对内含 const 成员的classes,编译器的反应也是一致的。更改const 成员是不合法的,所以编译器不知道如何在它自己生成的赋值函数内面对它们。最后还有一种情况:如果某个 base classes 将 copy assignment 操作符声明为 private ,编译器将拒绝为其 derived classes 生成一个 copy assignment 操作符。
总之,编译器可以为 class 创建 default 构造函数、copy 构造函数、 copy assignment 操作符和析构函数,但是在成员变量特殊的时候,编译器将无法自动生成 copy assignment 操作符,需要自己为你的 class 进行定义。