ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Effective C++ 第二章 构造/析构/赋值运算

2021-12-19 12:33:24  阅读:200  来源: 互联网

标签:Widget 函数 Effective rhs C++ pb 析构 基类 赋值


第二章 构造/析构/赋值运算 (Constructors,Destructors,and Assignment Operators)

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

请记住:

  • 编译器可以暗自为 class 创建 default 构造函数、copy 构造函数、copy 赋值运算符和析构函数

C++11中有6个:

1、构造

2、析构

3-4、拷贝构造和赋值操作符

5-6、移动构造和赋值操作符

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

请记住:

  • 为驳回编译器自动(暗自)提供的机能,可将相应的成员函数声明为private并且不予实现。使用像Uncopyable这样的base class也是一种做法

C++11中可以直接使用 = delete 来声明拷贝构造函数,显示禁止编译器生成该函数。

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

带有多态性质的基类必须将析构函数声明为虚函数,防止指向子类的基类指针在被释放时只局部销毁了基类对象。

请记住:

  • polymorphic(带多态性质的)base classes应该声明一个virtual析构函数。如果class带有任何virtual函数,它就应该拥有一个virtual析构函数
  • Classes 的设计目的如果不是作为base classes使用,或不是为了具备多态性(polymorphically),就不该声明virtual析构函数

C++11可以用 final 定义不想作为基类的类。

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

析构函数中抛出异常,会让析构动作停止,这可能会导致资源泄漏。

尽量在析构函数中解决所有异常。

请记住:

  • 析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序

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

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

有继承的构造顺序:先构造基类,再构造子类(和打好地基再建房子一个道理)

有继承的析构顺序:先析构子类,再析构基类(一点点拆)

在基类构造期间,虚函数绝对不会下降到子类层(即:在基类构造期间,虚函数不是虚函数)。

请记住:

  • 在构造和析构期间不要调用 virtual 函数,因为这类调用从不下降至 derived class (比起当前执行构造函数和析构函数的那一层)

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

因为赋值支持连锁操作(采用右结合率),比如: a = b = c;,所以重载赋值运算符必须返回一个指向操作数左侧对象的指针,也就是 *this

这不仅适用于标准赋值运算符,也适用于任何赋值相关的运算符重载,如 +=-=

请记住:

  • 令赋值(assignment)返回一个 reference to *this

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

缘起于一个憨批操作:将自己赋给自己,即自我赋值。

一般不会有人这么写,但有些时候会比较隐蔽,比如:

a[i] = a[j]; // i == j
*px = *py;   // px == py

若有如下类(持有堆上内存的一个类):

class B {...};
class Widget {
    ...
private:
    B* pb;
};

Widget& Widget::operator=(const Widget& rhs)
{
    delete pb;  // pb 是 Wdiget 类中的一个指针对象
    pb = new B(*rhs.pb);
    return *this;
}

这个代码中在自我赋值便是错的。

证同测试

即判断是不是自我赋值,如果是,不做任何事。

Widget& Widget::operator=(const Widget& rhs) {
  if (this == &rhs) return *this;
  
  delete pb;	
  pb = new B(*rhs.pb);				//如果此处抛出异常,pb将指向一块已经被删除的内存。
  return *this;
}

使用临时对象

复制pb所指东西前不要删除pb:

Widget& Widget::operator=(const Widget& rhs)
{
    B* temp = pb;
    pb = new B(*rhs.pb); // 此处可能抛出异常
    delete temp;
    return *this;
}

Copy and Swap

使用交换技术代替赋值(异常安全性更好)。

Copy and Swap技术:为你打算修改的对象做出一份副本,然后在副本上做一切必要的修改。若有任何修改动作抛出异常,原对象任保持未改变状态;待所有改变都成功后,再将修改过的那个副本与原对象在一个不抛出异常的操作中交换(swap)

class Widget {
...
void swap(Widget& rhs);        // 用于交换 rhs 和 *this 的数据
...
};

//此方法更好
Widget& Widget::operator=(const Widget& rhs)
{
  	// 显式调用拷贝构造函数
    Widget temp(rhs);         // 仍然需要临时变量
    swap(temp);
    return *this;
}

// 此方法过于隐晦
// 隐含了一个拷贝动作
Widget& Widget::operator=(Widget rhs) // 另一种变体,传值方式会复制一份副本
{
    swap(rhs);               // ths 本身是副本,直接交换
    return *this;
}

请记住:

  • 确保当对象自我赋值时 operator= 有良好行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及 copy-and-swap
  • 确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确

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

自定义了构造函数和 copy 函数,随后又新增了成员变量,那么需要自己留意在所有这些构造函数和 copy 函数中添加这个新的成员变量的操作,编译器不会提醒你。一旦忘记了,初始化或赋值操作就是不完整的。

如果是一个派生类中的 copy 函数,除了处理好派生类内的成员对象的 copy 操作,还要负责基类中对象的 copy 操作,也就是在初始化列表中完成对基类的 copy 操作。如下代码:

class Derived : public Base {
public:
  ...
  Derived(const Derived& rhs);
  Derived& operator=(const Derived& rhs);
private:
  int p;
};

Derived::Derived(const Derived& rhs)
  : Base(rhs),  // 注意这里,手动调用基类的 copy 构造函数
    p(rhs.p)
{
  ...
}

Derived& Derived::operator=(const Derived& rhs)
{
  ...
  Base::operator=(rhs);  // 注意这里,手动调用基类的 copy 赋值运算符函数
  p = rhs.p;
  return *this;
}   

相同代码可以抽象为一个成员函数init();

请记住:

  • Copying 函数应该确保复制 “对象内的所有成员变量” 及 “所有 base class 成分”
  • 不要尝试以某个 copying 函数实现另一个 copying 函数。应该将共同机能放进第三个函数中,并由两个 copying 函数共同调用

reference

[1] Effective C++ · Scott Meyers

[2] 重述Effective C++

标签:Widget,函数,Effective,rhs,C++,pb,析构,基类,赋值
来源: https://www.cnblogs.com/cxl-/p/15707039.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有