欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

EffectiveC++笔记 第2章

程序员文章站 2022-07-02 13:56:47
我根据自己的理解,对原文的精华部分进行了提炼,并在一些难以理解的地方加上了自己的“可能比较准确”的「翻译」。 Chapter 2 构造 / 析构 / 赋值 条款 05:了解C++ 默默编写并调用哪些函数 如果你写下: class Empty{ }; 事实上编译器会帮你补全: 这些函数只有被需要时才会 ......

我根据自己的理解,对原文的精华部分进行了提炼,并在一些难以理解的地方加上了自己的“可能比较准确”的「翻译」。

Chapter 2 构造 / 析构 / 赋值

条款 05:了解C++ 默默编写并调用哪些函数

如果你写下:
class Empty{ };

事实上编译器会帮你补全:
class Empty{
public:
Empty() { }
Empty(const Empty& rhs) { }
~Empty() { }
Empty& operator=(const Empty& rhs) { }
} ;
这些函数只有被需要时才会被创建出来
默认构造函数只是单纯的将来源对象每个non-static成员变量拷贝到目标对象


条款 06: 若不想使用编译器自动生成的函数,就改明确拒绝

将拷贝构造函数和赋值重载函数显式声明为private吧

但有一个小缺陷,就是member函数和友元函数依然可以访问,但是会造成连接错误(linkage error)
编译的话连接器会报错


条款07: 为多态基类声明 virtual析构函数

不作为基类的类不要声明虚析构函数
有继承关系的基类一定加上virtual析构函数(带多态性质的base class)

pure virtual纯虚构函数: virtual ~XXX() = 0;


条款 08: 别让异常逃离析构函数

如果客户需要对某个函数运行期间抛出的异常作出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该操作


条款 09: 绝不在构造和析构过程中调用virtual 函数

若是有两个有继承关系的类,主类有一个virtual函数,并在构造函数里调用;子类定义了另一个版本的此函数,当在main里构造此子类对象时,实际上还是先调用基类的构造函数,然而基类构造函数调用的virtual
函数版本实际上还是基类的。若此函数会使用子类里的元素,然而之类成员都还未初始化,就会引发错误

当类中有多个构造函数,它们兴许有共同的部分。有一种较为出色的做法是定义一个void类型的init函数,然后将初始化中需要调用的函数放在里面,将init单独放在构造函数里就好了。

综上,在构造和析构期间不要调用虚函数,因为这类调用从不下降至derived class


条款 10: 令 operator= 返回一个 reference to *this

int x,y,z;
x = y = z = 15;
根据右结合律,可以解释为x = (y = (z = 15));
为了实现连锁赋值,赋值操作赋必须返回一个reference指向操作符左侧实参:

class Widget{
public:
    …
    Widget& operator=(const Widget& rhs){
        …
        return *this;
    }
};

此标准适用于所有与赋值相关运算


条款 11:在 operator=中处理自我赋值

类对象的赋值,看下面一种情况:

class Widget{
    …
private:
    Bitmap* pb;
};

Widget& Widget::operator=(const Widget& rhs)
{
    delete pb;
    pb = new Bitmap(*rhs.pb);
    return *this;
}

存在的问题是,如果*this和rhs是同一个对象,delete操作会同时删除两者的bitmap。可以这样操作:

Widget& Widget::operator=(const Widget& rhs)
{
    if(this==&rhs) return *this;  //如果是自我赋值则不做任何事
    delete pb;
    pb = new Bitmap(*rhs.pb);
    return *this;
}

条款 12: 复制对象时勿忘其每一个成分

如果对于拷贝函数,你的复制成员没有写全,编译器是不会报错的,因为这符合逻辑。所以记得将需要拷贝的成员变量都放进copying函数

另外,当手动撰写继承类的copying函数时,一定记得在初始化列表复制其base class的成分.否则在复制构造函数中base class成分会由base class的default构造函数初始化,而copy assignment函数根本不会管base class的成分
可以这样写:(A extends B)
在copy函数中:

A::A(const A& rhs):B(rhs)
{
    ……
}

在copy assignment函数中:

A& A::operator=(const A& rhs)
{
    if(this==&rhs)
        return *this;
    B::operator=(rhs); //对base class成分进行赋值动作
    ……
    return *this;
}

现在很清楚了,当你编写一个copying函数,请确保(1)复制所有local成员变量(2)调用所有base classes内适当的copying函数


OVER