zoukankan      html  css  js  c++  java
  • [c++primer][13]复制控制

    复制构造函数、赋值操作符和析构函数总称为复制控制。

    复制构造函数:特殊的构造函数,具有单个形参,该形参时对该类类型的const引用。定义新对象并用同类型对象初始化,显式调用了复制构造函数;将该类型对象传递给函数或从函数返回该类型的对象时,隐式调用了复制构造函数。

    析构函数:当对象超出作用域或动态分配的对象被删除时,自动调用析构函数,用于释放在造函数或在对象生命期内获取的资源。

    13.1 复制构造函数

    对类类型对象来说,

    1)直接初始化直接调用与实参匹配的构造函数

    2)复制初始化总是调用复制构造函数(复制初始化首先使用指定构造函数创建一个临时对象,然后用复制构造函数将临时对象复制到正在创建的对象)

    合成的复制构造函数

    如果我们没有定义复制构造,编译器会为我们合成。即使定义了其他构造函数,也会合成。

    合成复制构造函数的行为是,执行逐个成员初始化,将新对象初始化为原对象的副本。

    定义自己的复制构造函数

    只包含类类型成员和内置类型(不是指针)成员的类,无需显式定义复制构造函数;

    复制构造函数的定义也可以使用初始化列表,可以在函数体中做任何其他必要工作。

    禁止复制

    为防止复制,类必须显式声明其复制构造函数为private;如果想要连友元和成员中的复制也禁止,就可以声明为private而不定义。

    13.2 赋值操作符

    合成赋值操作符

    合成赋值操作符执行逐个成员赋值,返回*this,是对左操作数对象的引用。

    可以使用合成复制构造函数的类通常也可以使用合成赋值操作符

    13.3 析构函数

    何时调用析构函数

    撤销类对象(超出作用域)会自动调用析构函数。

    当对象的引用或指针超出作用域时,不会运行析构函数。只有删除指向动态分配对象的指针或实际对象(而不是引用)超出作用域时,才会运行析构函数。

    三法则:如果需要析构函数,则需要所有三个复制控制成员。 

    合成析构函数

    与复制构造和赋值操作符不同,编译器总是(即便自己编写了析构)会为我们合成一个析构函数。合成析构函数按成员在类中声明次序的逆序撤销成员。对于类类型成员,合成析构函数调用该成员的析构函数来撤销该对象。

    执行顺序:先执行类定义的析构,再运行合成析构函数

    13.5 管理指针成员

    管理指针成员的三种方法

    1)指针成员采取常规指针型行为。具有指针的所有缺陷但无需特殊的复制控制

    使用默认合成复制构造函数

    无法避免悬垂指针(指针指向的内存被释放,指针指向一个不复存在的对象)

    2)使用智能指针。指针所指向的对象是共享的,但类能够防止悬垂指针

    3)类采取值型行为。指针所指向的对象是唯一的。由每个类对象独立管理。

    定义智能指针类

    使用计数类

    // private class for use by HasPtr only
    class U_Ptr {
      friend class HasPtr;
      int *ip;
      size_t use;
      U_Ptr(int *p): ip(p), use(1) { }
      ~U_Ptr() { delete ip; }
    };

    使用计数类的使用

    /* smart pointer class: takes ownership of the dynamically allocated
    * object to which it is bound
    * User code must dynamically allocate an object to initialize a HasPtr
    * and must not delete that object; the HasPtr class will delete it
    */
    class HasPtr {
    public:
      // HasPtr owns the pointer; pmust have been dynamically allocated
      HasPtr(int *p, int i): ptr(new U_Ptr(p)), val(i) { }
      // copy members and increment the use count
      HasPtr(const HasPtr &orig):
      ptr(orig.ptr), val(orig.val) { ++ptr->use; }
      HasPtr& operator=(const HasPtr&);
      // if use count goes to zero, delete the U_Ptr object
      ~HasPtr() { if (--ptr->use == 0) delete ptr; }
    private:
      U_Ptr *ptr; // points to use-counted U_Ptr class
      int val;
    };

    赋值与使用计数

    HasPtr& HasPtr::operator=(const HasPtr &rhs)
    {
      ++rhs.ptr->use; // increment use count on rhs first
      if (--ptr->use == 0)
      delete ptr; // if use count goes to 0 on this object, delete it
      ptr = rhs.ptr; // copy the U_Ptr object
      val = rhs.val; // copy the int member
      return *this;
    }

    定义值型类

    给指针成员提供值语义,复制值型对象时,会得到一个不同的新副本;要使指针成员表现得像一个值,赋值对象时必须复制指针所指向的对象。

    /*
    * Valuelike behavior even though HasPtr has a pointer member:
    * Each time we copy a HasPtr object, we make a new copy of the
    * underlying int object to which ptr points.
    */
    class HasPtr {
    public:
      // no point to passing a pointer if we're going to copy it anyway
      // store pointer to a copy of the object we're given
      HasPtr(const int &p, int i): ptr(new int(p)), val(i) {}
      // copy members and increment the use count
      HasPtr(const HasPtr &orig):
      ptr(new int (*orig.ptr)), val(orig.val) { }
      HasPtr& operator=(const HasPtr&);
      ~HasPtr() { delete ptr; }
      // accessors must change to fetch value from Ptr object
      int get_ptr_val() const { return *ptr; }
      int get_int() const { return val; }
      // change the appropriate data member
      void set_ptr(int *p) { ptr = p; }
      void set_int(int i) { val = i; }
      // return or change the value pointed to, so ok for const objects
      int *get_ptr() const { return ptr; }
      void set_ptr_val(int p) const { *ptr = p; }
    private:
      int *ptr; // points to an int
      int val;
    };

    赋值操作符不需要分配新对象,它只是必须记得给其指针所指向的对象赋新值,而不是给指针本身赋值。(即使要将一个对象赋值给它本身,赋值操作符也必须总是保证正确。)

    HasPtr& HasPtr::operator=(const HasPtr &rhs)
    {
      // Note: Every HasPtr is guaranteed to point at an actual int;
      // We know that ptr cannot be a zero pointer
      *ptr = *rhs.ptr; // copy the value pointed to
      val = rhs.val; // copy the int
      return *this;
    }
  • 相关阅读:
    centos7配置java环境
    docker下安装vim
    小程序开发知识点总结
    response设置输出文件编码
    IDEA中,将文件夹加入classpath
    【问题排查】StringIndexOutOfBoundsException
    【问题排查记录】Field 'id' doesn't have a default value;
    http
    raw_input() 与 input() __ Python
    记一次eclipse无法启动的排查过程
  • 原文地址:https://www.cnblogs.com/itree/p/4903163.html
Copyright © 2011-2022 走看看