实施有价值的复制赋值运算符

时间:2015-02-24 19:55:11

标签: c++ memory copy-assignment

在C ++ Primer中,有一个使用复制控制成员来使一个类行为的例子" valuelike&#34 ;;也就是说,复制对象时,副本是独立的。它提出以下代码:

class HasPtrValue
{
public:
    HasPtrValue(const std::string &s = std::string()) : ps(new std::string(s)), i(0) {  }
    HasPtrValue(const HasPtrValue &orig) : ps(new std::string(*orig.ps)), i(orig.i) { }
    HasPtrValue& operator=(const HasPtrValue&);
    ~HasPtrValue() { delete ps; };

    std::string *ps;
    int i;
};

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

我的问题是关于复制赋值运算符。据我所知,它的作用是在堆上创建一个新字符串,删除旧字符串,并使lhs指向新字符串。这真的有必要吗?通过简单地分配堆上的现有字符串,下面的代码是不是完全相同的东西?

HasPtrValue& HasPtrValue::operator=(const HasPtrValue &rhs)
{
    *ps = *rhs.ps;
    i = rhs.i;
    return *this;
}

2 个答案:

答案 0 :(得分:2)

你是对的。以下列方式定义复制赋值运算符就足够了

HasPtrValue& HasPtrValue::operator=(const HasPtrValue &rhs)
{
    *ps = *rhs.ps;
    i = rhs.i;
    return *this;
}

唯一的区别(除了分配新的内存)是在这种情况下,字符串可以包含很多保留的内存,尽管rhs对象的字符串可以足够小。

当使用复制赋值运算符时,C ++标准并未说明目标字符串将缩小为原始字符串的大小。它只是说

size() str.size()
capacity() a value at least as large as size()

对于原始版本,它应检查是否存在自我分配以避免冗余内存分配。那应该是

HasPtrValue& HasPtrValue::operator=(const HasPtrValue &rhs)
{
    if ( this != &rhs )
    {
        auto newp = new std::string(*rhs.ps);
        delete ps;
        ps = newp;
        i = rhs.i;
    }

    return *this;
}

答案 1 :(得分:2)

你是对的。 您的版本不仅有效,而且效率更高,因为ps->capacity() >= rhs.ps->capacity()可以重用现有内存。

如果您想提供强大的异常保证,请使用copy-and-swap idiom

HasPtrValue& HasPtrValue::operator=(HasPtrValue copy) // notice the by value
{
    swap(*this, copy);
    return *this;
}

// integrating tip from link posted by WhozCraig 
friend void swap(HasPtrValue &lhs, HasPtrValue &rhs)
{
    using std::swap;
    swap(lhs.ps, rhs.ps);
    swap(lhs.i, rhs.i);
}

虽然对代码的更改应该已经提供了强大的异常保证,只要编译器没有重新排序i的赋值。