C++拷贝控制与资源管理

C++拷贝控制与资源管理

参考《C++ Primer》P452 拷贝控制与资源管理

当一个类需要自定义析构函数,那么它几乎肯定也需要定义拷贝构造函数和拷贝复制运算符。 如下例子:

class HasPtr {
public:
  HasPtr(const std::string &s = std::string()):
    ps(new std::string(s)),i(0) {}
  ~HasPtr() {delete ps;}
  //此时需要定义一个拷贝构造函数和一个拷贝赋值运算符
};

如果我们没有定义拷贝构造函数和拷贝赋值运算符,那么会调用类的合成拷贝构造函数和合成拷贝赋值运算符,这些函数简单拷贝指针成员,意味着多个HasPtr对象可能指向相同的内存。

HasPtr f(HasPtr hp)
{
  HasPtr ret = hp;    //拷贝给定的HasPtr
  //处理ret
  return ret;         //ret和hp被销毁
}

当f返回时,hp和ret都被销毁,在这两个对象上都会调用HasPtr的析构函数,析构函数会调用delete ret和hp的指针成员,但这两个指针指向相同的内存块,则导致了指针被delete两次的错误。

定义行为像值的类

为了提供类值的行为,需要:
- 定义一个拷贝构造函数,完成string的拷贝,而不是指针拷贝;
- 定义一个析构函数来释放string;
- 定义一个拷贝复制运算符来释放对象当前的string,并从右侧运算对象拷贝string;

clas HasPtr {
public:
    HasPtr(const std::string &s=std::string()):
      ps(new std::string(s)),i(0) {}
    HasPtr(const HasPtr &p):
      ps(new std::string(*p.ps)),i(p.i) {}  //拷贝string的值,而不是ps指针
    HasPtr& operator=(const HasPtr&);
    ~HasPtr() {delete ps;}
private:
    std::string *ps;
    int i;
};
HasPtr& HasPtr::operator=(const HasPtr &rhs)
{
    auto newp = string(*rhs.ps); //首先将右侧运算对象拷贝到局部临时变量中。
    delete ps;                   //再删除左侧原始成员
    ps = newp;                   //赋予新的成员值
    i = rhs.i;
    return *this;                //返回对象本身
}

需要注意的是:
如果将一个对象赋予它自身,赋值运算符必须保证正确。
大多数赋值运算符组合了析构函数和拷贝构造函数的工作。

定义行为像指针的类

相比值行为类,指针行为类需要拷贝的是指针,会出现多个对象指向同一内存块的行为,则我们需要加入引用计数,计数器需要放置在动态内存中,当创建一个新的对象时,我们分配一个新的计数器,当拷贝或赋值对象时,我们拷贝指向计数器的指针。

class HasPtr{
public:
  HasPtr(const string &s=string()):
    ps(new string(s)),i(0),use(new size_t(1)){}
  HasPtr(const HasPtr &p):        //拷贝指针,计数器加1
    ps(p.ps),i(p.i),use(p.use){++ *use}
  HasPtr& operator=(const HasPtr &);
  ~HasPtr();
private:
  string *ps;
  int i;
  size_t *use;
};

HasPtr::~Hasptr() {
  if(-- *use==0) {   //计数器减1,如果引用计数变为0,则释放string和计数器内存
    delete ps;
    delete use;
  }
}
HasPtr& HasPtr::operator=(const HasPtr &rhs) {
  ++ *rhs.user;      //右值对象计数器加1
  if(--*use==0) {    //左值对象计数器减1,如果计数器为0,则释放内存
    delete ps;
    delete use;
  }
  ps = rhs.ps;
  i = rhs.i;
  use = rhs.use;
  return *this;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章