拷贝控制和资源管理

类的行为像一个值,意味着它应该也有自己的状态。当我们拷贝一个像值的对象时,副本和元对象是相互独立的。改变副本不会对原对象有任何影响,反之亦然。

行为像指针的类则共享状态。当我们拷贝一个这样的对象时,副本和原对象使用相同的底层数据。改变副本也会改变原对象,反之亦然。

行为像值的类

  为了提供类值的行为,对于类管理的资源,每个对象都应该拥有一份自己的拷贝。这意味着对于ps指向的string,每个HasPtr对象都必须拥有自己的拷贝。为了实现类值行为,HasPtr需要

  • 定义一个拷贝构造函数,完成string的拷贝,而不是拷贝指针
  • 定义一个析构函数用于释放指针
  • 定义一个赋值运算符来释放对象当前的string,并从右侧运算对象拷贝string
class 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) {}
    HasPtr& operator=(const HasPtr &);
    ~HasPtr() {delete ps;}
private:
    std::string *ps;
    int i;
};

HasPtr& HasPtr::operator=(const HasPtr &rhs)
{
    auto newp = new string(*rhs.ps);
    delete ps;
    ps = newp;
    i = rhs.i;
    return *this;
}

行为像指针的类

  对于行为类似指针的类,我们需要为其定义拷贝构造函数和拷贝赋值运算符,来拷贝指针成员本身而不是它指向的string。我们的类仍需要自己的析构函数来释放接收string参数的构造函数分配的内存。但是,在本例中,析构函数不能单方面地释放关联的string。只有当最后一个指向string的HasPtr销毁时,它才可以释放string。

  令一个类展现类似指针的行为的最好办法是使用shared_ptr来管理类中的资源。拷贝(或赋值)一个shared_ptr会拷贝(或赋值)shared_ptr所指向的指针。shared_ptr类自己记录有多少用户共享它指向的对象。当没有用户使用对象时,shared_ptr类负责释放资源。

  但是,有时我们希望直接管理资源。在这种情况下,使用引用计数(reference count)就很有用了。为了说明引用计数如何工作,我们将重新定义HasPtr,令其行为像指针一样,但我们不使用shared_ptr,而是设计自己的引用计数。

引用计数

引用计数的工作方式如下:

  • 除了初始化对象外,每个构造函数(拷贝构造函数除外)还要创建一个引用计数,用来记录有多少对象与正在创建的对象共享状态。当我们创建一个对象时,只有一个对象共享状态,因此将计数器初始化为1。
  • 拷贝构造函数不分配新的计数器,而是拷贝给定对象的数据成员,包括计数器。拷贝构造函数递增共享的计数器,指定给定对象的状态又被一个新用户所共享。
  • 析构函数递减计数器,指出共享状态的用户少了一个。如果计数器变为0,则析构函数释放状态。
  • 拷贝赋值运算符递增右侧运算对象的计数器,递减左侧运算对象的计数器。如果左侧运算对象的计数器变味0,意味着它的共享状态没有用户了,拷贝赋值运算符就必须销毁状态。
class HasPtr
{
public:
    //构造函数分配新的string和新的计数器,并将计数器置为1
    HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0), use(std::size_t(1)) {}
    //拷贝构造函数拷贝所有三个数据成员,并递增计数器
    HasPtr(const HasPtr &p) : ps(p.ps), i(p.i), use(p.use) {++*use;}
    HasPtr& operator=(const HasPtr &p);
    ~HasPtr();
private:
    std::string *ps;
    int i;
    std::size_t *use; //用来记录有多少个对象共享*ps的成员
};

HasPtr& HasPtr::operator=(const HasPtr &rhs)
{
    ++*rhs.use;
    if (--*use == 0)
    {
        delete ps;
        delete use;
    }
    ps = rhs.ps;
    i = rhs.i;
    use = rhs.use;
    return *this;
}

 

转载于:https://www.cnblogs.com/bootblack/p/11348207.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值