《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操作符 被产出


2. 类中这些东西的作用?

<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********************************************

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。