在C ++ Primer 5th,13.2.1 Class That Act Like Values中,作者创建了一个类似于值的类,这意味着每个对象都有自己的类所管理的资源副本。类在下面,它是一个非常简单的类,只包含一个指向字符串的指针,一个int,成员函数只是默认的构造函数和复制控制成员。
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 &rhs);
~HasPtr(){ delete ps; }
private:
std::string *ps;
int i;
};
以下是本书给出的运算符的实现
HasPtr &HasPtr::operator=(const HasPtr &rhs){
auto newp = new std::string(*rhs.ps);
delete ps;
ps = newp;
i = *rhs.i;
return *this;
}
这很好,但我认为我们可以使用以下工具来避免删除指针并分配新内存。
HasPtr &HasPtr::operator=(const HasPtr &rhs){
*ps = *rhs.ps;
i = rhs.i;
return *this;
}
我测试我的代码是否有效,甚至是自我赋值。但是,这段代码有什么问题吗?
答案 0 :(得分:3)
不,您的代码没有问题。
*ps
本身就是一种值类型,因此您可以直接指定它。如果您正在进行更改以改进代码,您可能希望进一步将ps更改为std :: string而不是std :: string *。然后,您可以从HasPtr类中消除对new和delete的需要。
如果
*ps
是一个指向HasPtr类管理的内存的原始指针,你必须编写代码,如本书中的例子。
答案 1 :(得分:0)
在您的原始示例中,您已将* ps的内容复制到自身,这没关系。但是,正如您对此所做的评论所说,情况可能并非总是如此。
至于其他解决方案,这里有一个(可能会在本书的后面解释):
#include <algorithm>
//...
HasPtr &HasPtr::operator=(const HasPtr &rhs)
{
HasPtr temp(rhs);
std::swap(ps, temp.ps);
std::swap(i, temp.i);
return *this;
}
这是copy/swap
成语。临时从rhs
创建,临时的内部与this
成员交换。临时死亡,最后,它与this
以前的价值观一致。
请注意,没有明确的内存分配,因为您最初依赖于复制构造函数来创建临时副本。这意味着这种技术需要一个工作的复制构造函数和一个工作的析构函数。一旦你有了这些,如果以这种方式完成,赋值操作符非常简单,因为没有真正的&#34; grunt工作&#34;编码要做 - 复制构造函数会处理它。
这比书的版本更容易吗?取决于你如何看待它 - 复制/交换几乎可以在任何情况下使用,你基本上只需要从&#34; 3&#34规则中描述的三个函数中的两个的工作实现开始;,即复制构造函数和析构函数。通过利用其他两个功能,以及执行简单的交换,赋值运算符就可以实现。