博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
### 学习《C++ Primer》- 6
阅读量:4597 次
发布时间:2019-06-09

本文共 3167 字,大约阅读时间需要 10 分钟。

Part 6: 拷贝控制(第13章)

// @author:       gr// @date:         2015-01-08// @email:        forgerui@gmail.com

一、拷贝、赋值与销毁

  1. 拷贝构造函数
  2. 拷贝赋值运算符
  3. 析构函数
    析构函数自身并不直接销毁成员,而是在析构函数之后隐含的析构阶段中被销毁的。
  4. 使用=default修饰成员时,要求编译器生成合成的版本。
  5. 阻止拷贝

    在新标准下,使用删除的函数来阻止拷贝,虽然声明了它,但不能以任何方式使用它。

    struct NoCopy{     NoCopy() = delete;     Nocopy(const NoCopy&) = delete;                 //阻止拷贝     NoCopy& operator=(const Nocopy&) = delete;      //阻止赋值 };

    将拷贝函数声明为private,阻止调用,并且只声明不实现。

二、拷贝控制和资源管理

  1. 一旦一个类需要析构函数,那么它几乎肯定也需要一个拷贝构造函数和一个拷贝赋值运算符。
  2. 定义拷贝操作,使类的行为看起来像一个值或指针。像值的类拥有自己的状态,像指针的类则共享状态(当拷贝一个这种类的对象时,副本和原对象使用相同的底层数据)。
  3. 行为像值的类,有两个数据成员stringint

    class HasPtr{     public:         HasPtr(const std::string &s = std::string()) : ps(new string(s)), i(0){}         HasPtr(const HasPtr& p) : ps(new string(*p.ps)), i(p.i){}         HasPtr& operator= (const HasPtr&);         ~HasPtr(){  delete ps;}     private:         int i;         std::string* ps; }; HasPtr& HasPtr::operator=(const HasPtr& rhs){     std::string* newp = new std::string(*rhs.ps);       //先创建一个新拷贝之后再delete,否则如果传入自己,delete会使rhs.ps失效,再拷贝就什么也没有了。     delete ps;           ps = newp;     i = rhs.i;     return *this; }
    注意:
    1. 如果一个对象赋予它自身,赋值运算符必须能正确工作。
    2. 大多数赋值运算符组合了析构函数构造函数的工作。
  4. 行为像指针的类,多个对象共享一份资源,需要使用引用计数。可以用shared_ptr,也可以自己实现。

    class HasPtr{     public:         HasPtr(const std::string& s = std::string()) : ps(new string(s)), use(new std::size_t(1)), i(0){}         HasPtr(const HasPtr& rhs) : ps(rhs.ps), use(rhs.use), i(rhs.i){ ++*use; }         HasPtr& operator=(const HasPtr&);         ~HasPtr();     private:         int i;         string* ps;         std::size_t* use; }; //析构函数 HasPtr::~HasPtr(){     if (--*use == 0){         delete ps;         delete use;     } } //拷贝运算符 HasPtr& HasPtr::operator=(const HasPtr& rhs){     ++*rhs.use;     //先判断是否是最后一个拥有资源的类,如果是,则删除资源     if (--*use){         delete ps;         delete use;     }     ps = rhs.ps;     use = rhs.use;     i = rhs.i;     return *this; }

三、交换操作SWAP

  1. swap函数应该调用swap,而不是std::swap

    定义类自己的swap函数,如下:

    class HasPtr{     public:         friend void swap(HasPtr& lhs, HasPtr& rhs); }; inline void swap(HasPtr& lhs, HasPtr& rhs){     using std::swap;     swap(lhs.ps, rhs.ps);     swap(lhs.i, rhs.i); }

    每个swap函数都应该是未加限制的,这样如果存在类型特定的swap版本,则优先于特定的swap版本。

  2. 在赋值中使用swap

    HasPtr& HasPtr::operator=(HasPtr rhs){     //参数是一个值,而不是引用     swap(*this, rhs);     return *this; }

四、对象移动

  1. 新标准中的一个特性是可以移动而非拷贝对象的能力。这样会大幅度提升性能。
  2. 为了支持移动操作,新标准引入了一种新的引用类型,右值引用,就是必须绑定到右值的引用。通过&&而不是&来获得引用。

    int &&rr = i * 42;
    int &&rr1 = 42; //正确:字面常量是右值
    int &&rr2 = rr1; //错误:变量是左值

  3. 标准move函数

    可以通过move显式地将右值引用绑定到一个左值上。

    int &&rr3 = std::move(rr1);
  4. 移动构造函数和移动赋值运算符

    移动构造函数的参数是该类类型的一个右值引用。

    StrVec::StrVec(StrVec &&s) noexcept     //移动操作不应抛出任何异常     : elements(s.elements), first_free(s.first_free), cap(s.cap) {     //令s进入这样的状态,对其运行析构函数是安全的     s.elements = s.first_free = s.cap = nullptr; //置为nullptr后,便可析构rhs,不会影响 } //移动赋值运算符 StrVec& StrVec::operator=(StrVec&& rhs) noexcept{     if (this != &rhs){         free();         elements = rhs.elements;         first_free = rhs.first_free;         cap = rhs.cap;         rhs.elements = rhs.first_free = rhs.cap = nullptr;       }     return *this; }

转载于:https://www.cnblogs.com/gr-nick/p/4224692.html

你可能感兴趣的文章
doubango框架阅读计划
查看>>
c# winform 操作oracle数据库,把图片存储到数据库,保存图片到数据库
查看>>
php mysql 中文乱码解决,数据库显示正常,php调用不正常
查看>>
JSP概述
查看>>
解决写不出代码的方法
查看>>
Linux 添加PPA源
查看>>
用正则表达式匹配用rdf3x处理过后的TTL格式文档
查看>>
Quartz定时器+Spring + @Autowired注入 空指针异常
查看>>
repeater练习
查看>>
BBC micro:bit 学习资源汇总(最近更新2019年1月6日....)
查看>>
QEMU-KVM中的多线程压缩迁移技术
查看>>
Druid的简介
查看>>
JAVA父类引用指向子类的对象意思
查看>>
(贪心)加油站绕圈问题
查看>>
【LeetCode 237】Delete Node in a Linked List
查看>>
C++primer 14.1节练习
查看>>
基于perl面向对象开发的微信机器人
查看>>
组合索引和单列索引效率对比
查看>>
修改一行和修改全表的TX锁
查看>>
genymotion无法下载解决方法
查看>>