《Effective C++ 》学习笔记——条款05
***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************
二、Constructors,Destructors and Assignment Operators
从第一章认识了C++后,开始进入第二章节的学习了,这一章主要讲述的就是 构造、析构及赋值运算。对于每一个类,都会有 一个或多个 构造函数,一个析构函数 和 一个 copy assignment 运算符。这些东西对于一个C++程序员,是最基本的谋生工具,这些函数的行为将会对你的程序造成巨大的影响,因此,这一章提供良好的引导,让你将这些函数良好的结合在一起,形成classes的支柱。
Rule 05:Know what functions C++ silently writes and calls
规则05:了解C++默默编写并调用哪些程序
1. 关于一个空类——empty class
首先要明白,如果我们写了一个空类,当C++处理它时,会产生什么或者调用什么函数呢?
比如,我们写一个类:
class Empty {};
当C++处理它时,虽然你什么都没有写,但是里面相当于这样的:
class Empty{ public: Empty() {...}<span style="white-space:pre"> </span>// default 构造函数 Empty( const Empty& rhs ) {...}<span style="white-space:pre"> </span>// copy 构造函数 ~Empty() {...}<span style="white-space:pre"> </span>// 析构函数(是否为virtual,稍后论证) Empty& operator=( const Empty& rhs) {...}<span style="white-space:pre"> </span>// copy assignment操作符 };
没错,就是这样,即使你什么都没写,C++会自动给这个空类,填上这些东西。当然这里面有些东西是可以通过我们来覆盖,或者有些东西会根据类成员内容而不产生,这些后面都会讲述到。
我们要知道,对于大多数的类,这几个东西是必备的:default构造函数,copy构造函数,析构函数,copy assignment操作符,而且这些函数都是 public 且 inline的。
还要明确一点,虽然类中“添加”了这么多东西,但并不是一调用对象就会使用这个函数,比如:
当新建一个Empty类的对象
Empty e1;
这时,default构造函数 和 析构函数 会被编译器产出。
而这样时:
Empty e2(e1); // copy构造函数被产出 e2 = e1; // copy assignment操作符 被产出
<1> 对于 default构造函数 和 析构函数
主要是给编译器一个地方用来放置“幕后”代码,比如,调用 base classes 和 non-static 成员变量的构造函数和析构函数。(注意:编译器产出的析构函数是 non-virtual 的, 除非这个类的基类自身声明有virtual 析构函数)
<2> 对于 copy构造函数 和 copy assignment 操作符
编译器创建的版本只是单纯的将来源对象的每一个non-static 成员变量拷贝到目标对象。
3. copy 构造函数 and copy assignment操作符
对于一个 NamedObject template 类,它允许你将一个个名称和类型为T的对象产生关联
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; };☆PS:由于这个类中声明了一个构造函数,所以编译器不会再为它创建 default构造函数。☆
可以看到,上面的类中并没有声明 copy 构造函数,也没有声明 copy assignment操作符,所以如果它们被调用,编译器就会为它创造这些函数。
copy函数调用:
NamedObject<int> no1("Smallest Prime Number",2); NamedObject<int> no2(no1); // 调用copy 构造函数
下面看编译器对这个函数如何操作,
首先,编译器生成的构造函数 必须 以no1的两个成员变量 nameValue 和 objectValue为初值 来设定 no2 的两个成员变量。
在两者之中,nameValue类型是 string 并不是 内置类型,而标准的string有copy构造函数,所以,no2.nameValue初始化是调用 string 的copy构造函数,并以no1.nameValue为实参。
而另一个成员的类型是内置类型 int ,所以no2.objectValue会以“拷贝no1.objectValue内的每一个bits”来进行初始化。
对于copy assignment操作符,在满足下两个条件下,会同copy构造函数一致。
① 生成的代码合法
②有适当的机会证明它有意义
否则 编译器 会拒绝为 class 生出 operator =
4.接下来,继续看个例子:
template<class T> class NamedObject { public: NamedObject( std::string& name, const T& value ); ... pirvate:' std::string& nameValue; const T objectValue; };
看到,nameValue成了 引用类型, objectValue成了 const,
所以,如果这样定以后,执行下列操作:
std::string newDog("Persephone"); std::string oldDog("Satch"); NamedObject<int> p(newDog,2); NamedObject<int> s(oldDog,36); p=s;
最重要的是,执行 p=s 这一行时会发生什么?
p.nameValue 指向 s.nameValue的那个string吗?
当然不行!
C++ 不允许“让reference改指向不同对象”
所以,这种情况下,如何解决呢?
C++的响应是——拒绝编译那一行赋值动作。
其实,下列三种情况,C++会“无能为力”
<1> 打算在一个“内涵reference成员”的 class 内支持 赋值操作,这样情况,必须自己定义copy assignment操作符
<2> 面对“内含const成员”的classes,编译器的反应也一样,因为更改const成员是不合法的。
<3> 再有就是 如果某个 base classes 将copy assignment操作符声明为 private,编译器将拒绝为其 derived classes 生成 copy assignment 操作符。
5.Please Remember
编译器可以暗自为 class 创建 default构造函数、copy构造函数、copy assignment操作符 以及析构函数。
***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。