• ### 学习《C++ Primer》- 6


    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;
       }
  • 相关阅读:
    React Native 开发环境安装和配置使用报错: -bash: react-native: command not found
    ios开发不能不知的动态修复bug补丁第三方库JSPatch 使用学习:JSPatch导入、和使用、.js文件传输加解密
    ios开发视频播放后台下载功能实现 :1,ios播放视频 ,包含基于AVPlayer播放器,2,实现下载,iOS后台下载(多任务同时下载,单任务下载,下载进度,下载百分比,文件大小,下载状态)(真机调试功能正常)
    ios开发总结:Utils常用方法等收集,添加扩展类,工具类方法,拥有很多方便快捷功能(不断更新中。。。)
    alloc init初始化后对象依然还在父视图
    第二篇:_UICascadingTextStorage attributesAtIndex:effectiveRange:]: Range or index out of bounds
    Linux运维入门到高级全套常用要点
    高级Linux SA需要会做的事情
    squid源码安装下的conf文件默认值和提示
    将博客搬至CSDN
  • 原文地址:https://www.cnblogs.com/gr-nick/p/4224692.html
Copyright © 2020-2023  润新知