第2章 构造/析构/赋值运算
Constructors,Destructors,and Assignment Operator
几乎每一个 class 都会有一个或多个构造函数,一个析构函数,一个copy assignment 操作符.
条款05: 了解C++默默编写并调用哪些函数
Know what functions C++ silently writes and calls
什么时候empty class 不再是个empty class 呢?当C++处理过它之后.如果自己没有声明,编译器就会为它声明一个copy构造函数,一个copy assignment操作符和一个析构函数.此外如果没有声明任何构造函数,编译器也会声明一个default
构造函数.所有这些函数都是 public 且 inline.因此,如果写下:
class Empty {};
就好像写下这样的代码:
class Empty { public: Empty() { ... } // default构造函数 Empty(const Empty &rhs) { ... } // copy构造函数 ~Empty() { ... } // 析构函数 Empty& operator=(const Empty &rhs) { ... } // copy assignment操作符 };
唯有这些函数被调用,它们才会被编译器创建出来.程序中需要它们是很平常的事.下面代码造成上述每一个函数被编译器产出:
Empty e1; // default构造函数 Empty e2(e1); // copy构造函数 e2 = e1; // copy assignment操作符
编译器编写这些函数,这些函数做了什么呢?default 构造函数和析构函数主要是给编译器一个地方用来放置"藏身幕后"的代码,像是调用base classes 和non-static 成员变量的构造函数和析构函数.编译器产生的析构函数是个non-virtual,除非这个 class 的base class 自身声明有 virtual 析构函数.
至于copy构造函数和copy assignement操作符,编译器创建的版本只是单纯地将来源对象的每一个non-static 成员变量拷贝到目标对象.考虑一个NamedObject template,它允许将一个个名称和类型为T的对象产生关联:
templat <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 构造函数.这意味着如果设计一个 class,其构造函数要求实参,就无须担心编译器会毫无挂虑的添加一个无实参构造函数而遮掉自己的版本.
NamedObject即没有声明copy构造函数,也没有声明copy assignment操作符,所以编译器会为它创建那些函数.现在看看copy构造函数的用法:
NamedObject<int> no1("Smallest Prime Number", 2); NamedObject no2(no1); // 调用copy构造函数
编译器生成的copy构造函数必须以no1.nameValue和no1.objectValue为初值设定no2.nameValue和no2.objectValue.两者中,nameValue的类型是string,而标准string有个copy构造函数,所以no2.nameValue的初始化方式是调用string的copy构造函数并以no1.nameValue为实参.另一个成员NamedObject<int>::objectValue的类型是int,那是个内置类型,所以no2.objectValue会以"拷贝no1.objectValue内的每一个bits"来完成初始化.
编译器为NamedObject<int>所生成的copy assignment操作符,其行为基本上与copy构造函数一样,但一般而言只有当生出的代码合法且有适当机会证明它有意义,其表现才会如前所述.如果有条件不符合,编译器就拒绝为 class 生成 operator=.
例如,NamedObject定义如下,其中nameValue是个reference to string,objectValue是个 const T:
template <class T> class NamedObject { public: NamedObject(std::string& name, const T& value); private: std::string& nameValue; const T objectValue; };
现在考虑下面会发生什么事:
std::string newDog("Persephone"); std::string oldDog("Satch"); NamedObject<int> p(newDog, 2); NamedObject<int> s(odlDog, 36); p = s;
赋值之前,不论p.nameValue和s.nameValue都指向string对象.赋值动作该如何影响p.nameValue呢?赋值之后p.nameValue应该指向s.nameValue所指的那个string吗?
面对这个难题,C++的回应是拒绝编译那一行赋值动作.如果打算在一个"内含reference成员"的 class 内支持赋值操作,必须自定义copy assignment操作符.面对"内含const成员"的classes,编译器的反应也一样.
注意:
编译器可以暗自为 class 创建 default 构造函数,copy构造函数,copy assignment操作符,以及析构函数.
版权声明:本文为博主原创文章,未经博主允许不得转载。