拷贝控制与资源管理

管理类外资源的类必须定义拷贝控制成员。为了定义这些成员,我们必须确定此类型的拷贝语义。 类的行为可以定义为类值行为类指针行为


类值行为:类的行为像一个值,当我们拷贝一个像值的对象时,副本和原对象是完全独立的。以HasPtr类为实例(C++primer P453),为了实现类值行为,我们需要
(1)定义一个拷贝构造函数,完成string的拷贝,而不是拷贝指针
(2)定义一个折构函数来释放string
(3)定义一个拷贝赋值运算符来释放对象当前的string,并从右侧对象拷贝string.

#include <string>

class HasPtr {
public:
    HasPtr(const std::string &s = std::string()) : ps(new std::string(s)), i(0) { }//构造函数
    HasPtr(const HasPtr &hp) : ps(new std::string(*hp.ps)), i(hp.i) { }//ps指向的string,每个HasPtr对象都有自己的拷贝
    //赋值运算符通常组合了折构函数和构造函数的操作。在处理时,需要考虑自赋值的情况。当编写一个赋值运算符时,一个好的模式是(1)先将右侧运算对象拷贝到一个局部临时对象。(2)然后摧毁左侧对象的成员,(3)最后将临时对象的数据拷贝到左侧运算对象的成员中
    HasPtr& operator=(const HasPtr &hp) {
        auto new_p = new std::string(*hp.ps);//构造函数操作 1
        delete ps;//折构函数操作 2
        //3
        ps = new_p;
        i = hp.i;
        return *this;
    }
    ~HasPtr() {
        delete ps;
    } 
private:
    std::string *ps;
    int i;
};

类指针行为:行为像指针的类则共享状态。当我们拷贝一个类指针行为的对象时,副本和原对象使用相同的底层数据。设计此类对象可以使用引用计数,类似于shared_ptr的use_count.
引用计数工作的方式如下,计数器保存于动态内存当中,实现计数器的共享:

这里写图片描述

class HasPtr
{
public:
    HasPtr(const string &s=string()):ps(new string(s)),i(0),use(new std::size_t(1)){}//构造函数,增加一个存于动态内存的计数器use
    HasPtr(const HasPtr& p):ps(p.ps),i(p.i),use(p.use){++*use;}//拷贝构造函数需要递增计数器
    HasPtr& operator=(const HasPtr& p);//拷贝赋值函数
    ~HasPtr();//折构函数
private:
    string *ps;//string指针
    int i;
    std::size_t *use;//计数器,记录多少个对象共享*ps成员
};
HasPtr& HasPtr::operator=(const HasPtr& rhs)//拷贝赋值函数
{
    ++*rhs.use;//递增右侧运算对象的引用计数
    if(--*use==0)//然后递减左侧运算对象引用计数,如果计数器为0,释放内存
    {
        delete ps;
        delete use;
    }
    ps=rhs.ps;//拷贝rhs的数据到本对象
    i=rhs.i;
    use=rhs.use;
    return *this;
}
HasPtr::~HasPtr()//递减左侧运算对象引用计数,如果计数器为0,释放内存
{
    if(--*use==0)
    {
        delete ps;
        delete use;
    }
}

交换操作:

标准库的swap操作需要进行一次拷贝和两次赋值,效率较低。我们可以通过自己在类定义一个自定义版本的swap函数,来优化代码。可以将自定义版本的swap函数用于赋值运算符中,其能正确处理自赋值,自动是异常安全的。

//标准库swap操作
HasPtr temp=v1;//创建一个v1的值的临时副本
v1=v2;//将v2的值赋予v1
v2=temp;//将保存的v1的值赋予v2
#include <vector>
#include <algorithm>
#include <string>
#include <iostream>
class HasPtr
{
public:
    friend void swap(HasPtr&, HasPtr&);
    friend bool operator<(const HasPtr &lhs, const HasPtr &rhs);

    HasPtr(const std::string &s = std::string()) 
        : ps(new std::string(s)), i(0) 
    { }//std::cout << " HasPtr(const std::string &s = std::string()) " << std::endl;}

    HasPtr(const HasPtr &hp) 
        : ps(new std::string(*hp.ps)), i(hp.i) 
    {}//std::cout << " HasPtr(const HasPtr &hp) " << std::endl; }

    HasPtr& operator=(HasPtr rhs) 
    {
        swap(*this,rhs);
        std::cout << " HasPtr& operator=(HasPtr rhs)  " << std::endl; 
        return *this;
    }

    ~HasPtr() 
    {
        delete ps;
    }


    void show() const
    { 
        std::cout << *ps << std::endl; 
    }
private:
    std::string *ps;
    int i;
};
void swap(HasPtr &lhs,HasPtr &rhs) 
{
    using std::swap;
    swap(lhs.ps, rhs.ps);
    swap(lhs.i, rhs.i);
    std::cout << "call swap(HasPtr &lhs,HasPtr &rhs) " << std::endl;
}

bool operator<(const HasPtr &lhs, const HasPtr &rhs)
{
    return *lhs.ps < *rhs.ps;
}
int main(void)
{
    HasPtr s{ "s" },a{"a"},c{"c"};
    std::vector<HasPtr> vec{s,a,c};
    std::sort(vec.begin(), vec.end());

    for (auto const& elem : vec) elem.show();
    return 0;
}

输出:
这里写图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值