复制构造函数、赋值操做符和析构函数总称为复制控制。app
复制构造函数:特殊的构造函数,具备单个形参,该形参时对该类类型的const引用。定义新对象并用同类型对象初始化,显式调用了复制构造函数;将该类型对象传递给函数或从函数返回该类型的对象时,隐式调用了复制构造函数。函数
析构函数:当对象超出做用域或动态分配的对象被删除时,自动调用析构函数,用于释放在造函数或在对象生命期内获取的资源。fetch
对类类型对象来讲,this
1)直接初始化直接调用与实参匹配的构造函数指针
2)复制初始化老是调用复制构造函数(复制初始化首先使用指定构造函数建立一个临时对象,而后用复制构造函数将临时对象复制到正在建立的对象)code
若是咱们没有定义复制构造,编译器会为咱们合成。即便定义了其余构造函数,也会合成。对象
合成复制构造函数的行为是,执行逐个成员初始化,将新对象初始化为原对象的副本。blog
只包含类类型成员和内置类型(不是指针)成员的类,无需显式定义复制构造函数;ip
复制构造函数的定义也可使用初始化列表,能够在函数体中作任何其余必要工做。内存
为防止复制,类必须显式声明其复制构造函数为private;若是想要连友元和成员中的复制也禁止,就能够声明为private而不定义。
合成赋值操做符执行逐个成员赋值,返回*this,是对左操做数对象的引用。
可使用合成复制构造函数的类一般也可使用合成赋值操做符
撤销类对象(超出做用域)会自动调用析构函数。
当对象的引用或指针超出做用域时,不会运行析构函数。只有删除指向动态分配对象的指针或实际对象(而不是引用)超出做用域时,才会运行析构函数。
三法则:若是须要析构函数,则须要全部三个复制控制成员。
与复制构造和赋值操做符不一样,编译器老是(即使本身编写了析构)会为咱们合成一个析构函数。合成析构函数按成员在类中声明次序的逆序撤销成员。对于类类型成员,合成析构函数调用该成员的析构函数来撤销该对象。
执行顺序:先执行类定义的析构,再运行合成析构函数
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; }