C++prime读书笔记(三)拷贝控制、运算重载与类型转换、对象继承、模板与泛型
创始人
2024-02-13 02:33:52
0

layout: post
title: C++prime读书笔记(三)拷贝控制、运算重载与类型转换、对象继承、模板与泛型
description: C++prime读书笔记(三)拷贝控制、运算重载与类型转换、对象继承、模板与泛型
tag: 读书笔记


C++Prime读书笔记

  • 第13章:拷贝控制
    • 拷贝、赋值与销毁
      • 拷贝构造函数
      • 拷贝初始化与直接初始化的区别
      • 拷贝赋值运算符
      • 析构函数
      • 三/五法则
      • 使用=default
      • 阻止拷贝=delete
    • 拷贝控制和资源管理

第13章:拷贝控制

当定义一个类时,我们显式或隐式地指定在此类型的对象拷贝、移动、赋值和销毁时做什么,这些操作统称为拷贝控制操作,一个类通过定义5中特殊的成员函数来控制这些操作,包括拷贝构造函数、拷贝赋值函数、移动构造函数、移动赋值运算符和析构函数

拷贝、赋值与销毁

拷贝构造函数

如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数

class Foo
{
public:Foo();  // 默认构造Foo(const Foo&); // 拷贝构造
};

注意:
1、当我们没有为类声明任何构造函数时,编译器为我们合成默认构造函数。
但拷贝构造不同,即使我们定义了其他构造函数,编译器也会为我们合成一个拷贝构造函数

2、拷贝构造接收的参数必须是自身类类型的引用,如果参数不是引用类型,则调用永远也不会成功,为了调用拷贝构造函数,我们必须拷贝它的实参,但为了拷贝实参,我们又要调用拷贝构造函数,如此无限循环。

拷贝初始化与直接初始化的区别

直接初始化时,我们实际上要求编译器使用普通的函数匹配,而使用拷贝初始化时,我们要求编译器将右侧运算对象拷贝到正在创建的对象,如果需要的话还会进行类型转换。

string dots(10, ','); //直接初始化
string s(dots);  // 直接初始化
string s2 = dots; // 拷贝初始化
string null-book = "99999=99999"; // 拷贝初始化
string nines = string(100, '9'); // 拷贝初始化

拷贝初始化不仅在使用=定义变量时会发生,在下列情况下也会发生:

  • 将一个对象作为实参传递给一个非引用类型的形参
  • 从一个返回类型为非引用类型的函数返回一个对象
  • 用花括号列表初始化一个数组中的元素或一个聚合类中的成员。

注: 1、某些类型还会对他们所分配的对象使用拷贝初始化,例如当我们使用insert或者push成员时,容器会对元素进行拷贝初始化,与之对应用emplace成员创建的元素都进行直接初始化
2、如果我们希望使用explicit构造函数就必须显式地使用直接初始化,不可使用隐式地拷贝初始化转换。

拷贝赋值运算符

与类控制对象如何初始化一样,类也可以控制对象如果进行赋值。
与处理拷贝构造函数一样,如果一个类未定义自己的拷贝赋值运算符,编译器会为它生成一个合成拷贝赋值运算符。作为一个例子,下边的代码等价于Sales_data的合成拷贝赋值运算符,合成拷贝赋值运算符返回一个指向其左侧运算对象的引用。

Sales_data&
Sales_data::operator=(const Sales_data &rhs)
{bookNo = rhs.bookNo;units_sold = rhs.units_sold;revenue = rhs.revenue;return *this;
}

析构函数

析构函数释放对象使用的资源,销毁对象的非static数据成员。与普通指针不同,智能指针是类类型,所以具有析构函数。
以下情况会调用析构函数:

  • 变量在离开其作用域时被销毁
  • 当一个对象被销毁时,其成员被销毁
  • 容器被销毁时,其元素被销毁
  • 对于动态分配的对象,当指向它的指针应用delete运算符时被销毁。
  • 对于临时对象,当创建它的完整表达式结束时被销毁。

当一个类未定义自己的析构函数时,编译器会为它定义一个合成析构函数,类似拷贝构造函数和拷贝赋值运算符,在(空)析构函数体执行完毕后,成员会被自动销毁,析构函数体自身并不直接销毁成员,成员是在析构函数体之后隐含的析构阶段中被销毁的,在整个对象销毁的过程中,析构函数体是作为成员销毁步骤之外的另一部分而进行的

三/五法则

C++中有三个基本操作可以控制类的拷贝操作:拷贝构造函数、拷贝赋值运算符和析构函数,在C++11新标准下,一个类还可以定义一个移动构造函数和一个移动赋值函数
当我们决定一个类是否要定义它自己版本的拷贝控制成员时,一个基本原则是首先确定这个类是否需要一个析构函数,通常对于析构函数的需求要比对拷贝构造函数或者赋值运算符的需求更加明显。如果这个类需要一个析构函数,我们几乎可以确定它也需要一个拷贝构造函数和一个拷贝赋值运算符

例如下边这个例子:HasPtr中有指针数据成员,因此需要析构函数来释放指针。另一方面,由于包含指针成员,如果使用合成的拷贝构造和拷贝赋值运算符,这些函数简单拷贝指针成员,意味着多个HasPtr对象可能指向相同的内存。

class HasPtr{
public:HasPtr(const std::string &s = std::string()): ps(new std::string(s)), i(0) {}~HasPtr(){delete ps;}// 错误:HasPtr还需要一个拷贝构造函数和一个拷贝赋值运算符HasPtr& operator=(const HasPtr &hasptr){ps = new std::string(*(hasptr.ps));i = hasptr.i;return *this;} HasPtr(const &hasptr){ps = new std::string(*(hasptr.ps));i = hasptr.i;return *this;}
private:std::string *ps;int i;	
};

三/五法则的第二条是“如果一个类需要一个拷贝构造函数,几乎可以肯定它也需要一个拷贝赋值运算符,反之亦然——如果一个类需要一个拷贝赋值运算符,几乎可以肯定它也需要一个拷贝构造函数。”

使用=default

我们可以通过将拷贝控制成员定义为=default来显式地要求编译器生成合成的版本,当我们在类内使用=default修饰成员时,合成的函数将隐式地声明为内联的(就像任何其他类内声明的成员函数一样)。如果不希望合成的成员是内联函数,应该只对成员的类外定义使用=default,就像下面例子中对于拷贝赋值运算符=所做的那样。

class Sales_data
{
public:// 拷贝控制成员使用defaultSales_data() = default;Sales_data(const Sales_data &) = default;Sales_data & operator= (const Sales_data &);~Sales_data() = default;
};
Sales_data& Sales_data::operator=(const Sales_data&) = default;

如果我们不希望合成的成员是内联函数,应该只对成员的类外定义使用=default。

阻止拷贝=delete

对于某些类来讲拷贝和赋值没有合理的意义,因此在定义这些类时必须采用某种机制阻止拷贝或赋值。例如iostream类阻止了拷贝,以避免多个对象写入或读取相同的IO缓冲。为了阻止拷贝,可以通过将拷贝构造函数和拷贝赋值运算符定义为删除的函数,这种删除函数的意思是:我们虽然声明了它们,但不能以任何方式使用它们。=delete通知编译器,我们不希望定义这些成员。与=default不同,=delete必须出现在函数第一次声明时,且析构函数不能是删除的成员

struct NoCopy
{NoCopy() = default(); // 使用合成默认构造函数NoCopy(const NoCopy&) = delete; // 阻止拷贝NoCopy &operator=(const NoCopy&) = delete; // 阻止赋值~NoCopy() = default;  // 使用合成的析构函数
};

注:合成的拷贝控制成员可能是删除的,如果一个类有数据成员不能默认构造、拷贝、复制或销毁,则对应的成员函数将被定义为删除的。

拷贝控制和资源管理

相关内容

热门资讯

诺安积极回报混合A净值下跌3.... 诺安积极回报灵活配置混合型证券投资基金(简称:诺安积极回报混合A,代码001706)公布5月15日最...
易方达先锋成长混合C净值下跌3... 易方达先锋成长混合型证券投资基金(简称:易方达先锋成长混合C,代码011892)公布5月15日最新净...
永赢数字经济智选混合发起A净值... 永赢数字经济智选混合型发起式证券投资基金(简称:永赢数字经济智选混合发起A,代码018122)公布5...
中银证券优选行业龙头混合C净值... 中银证券优选行业龙头混合型证券投资基金(简称:中银证券优选行业龙头混合C,代码009641)公布5月...
恒越成长精选混合C净值下跌3.... 恒越成长精选混合型证券投资基金(简称:恒越成长精选混合C,代码010623)公布5月15日最新净值,...
新民发现解放战争时期烈士墓碑 转自:沈阳日报  近日,新民市兴隆堡镇西高力村村民在农田里发现一烈士墓碑,烈士名字叫李义,家乡为宾县...
国泰黄金ETF联接C净值下跌3... 国泰黄金交易型开放式证券投资基金联接基金(简称:国泰黄金ETF联接C,代码004253)公布5月15...
先锋聚优A净值下跌3.42% 先锋聚优灵活配置混合型证券投资基金(简称:先锋聚优A,代码004726)公布5月15日最新净值,下跌...
财通匠心优选一年持有期混合C净... 财通匠心优选一年持有期混合型证券投资基金(简称:财通匠心优选一年持有期混合C,代码014916)公布...
“工字号”体检车进企业送健康 转自:沈阳日报  5月15日一大早,一辆崭新的大巴车就停在顺丰快递沈阳分拨中心,大巴车的车身上印着“...
易方达产业机遇混合C净值下跌3... 易方达产业机遇混合型证券投资基金(简称:易方达产业机遇混合C,代码021180)公布5月15日最新净...
华夏中证云计算与大数据主题ET... 华夏中证云计算与大数据主题交易型开放式指数证券投资基金发起式联接基金(简称:华夏中证云计算与大数据主...
东兴数字经济混合发起A净值下跌... 东兴数字经济混合型发起式证券投资基金(简称:东兴数字经济混合发起A,代码020440)公布5月15日...
国泰优势行业混合A净值下跌3.... 国泰优势行业混合型证券投资基金(简称:国泰优势行业混合A,代码005819)公布5月15日最新净值,...
南方中证全指计算机ETF净值下... 南方中证全指计算机交易型开放式指数证券投资基金(简称:南方中证全指计算机ETF,代码159586)公...
合煦智远金融科技指数(LOF)... 合煦智远国证香蜜湖金融科技指数证券投资基金(LOF)(简称:合煦智远金融科技指数(LOF)C,代码1...
信澳业绩驱动混合A净值下跌3.... 信澳业绩驱动混合型证券投资基金(简称:信澳业绩驱动混合A,代码016370)公布5月15日最新净值,...
财通景气甄选一年持有期混合C净... 财通景气甄选一年持有期混合型证券投资基金(简称:财通景气甄选一年持有期混合C,代码017491)公布...
中航机遇领航混合发起C净值下跌... 中航机遇领航混合型发起式证券投资基金(简称:中航机遇领航混合发起C,代码018957)公布5月15日...
相信中国就是相信明天   近日,国家主席习近平复信中国丹麦商会负责人,勉励中国丹麦商会及会员企业为增进中丹、中欧友好和深化...