动态分配时,指向免费商店上的新地址是一种好做法吗? 是C++入门第五版的练习:
练习13.22:假设我们希望HasPtr表现得像一个值。 也就是说,每个对象都应该有自己的字符串副本 对象指向。我们将显示复制控件的定义 下一节的成员。然而,你已经知道你所知道的一切 需要知道如何实现这些成员。写下HasPtr副本 在阅读之前,构造函数和复制赋值运算符。(第511页) 类的代码动态分配时,指向免费商店上的新地址是一种好做法吗? 是C++入门第五版的练习:,c++,pointers,dynamic-memory-allocation,copy-assignment,C++,Pointers,Dynamic Memory Allocation,Copy Assignment,练习13.22:假设我们希望HasPtr表现得像一个值。 也就是说,每个对象都应该有自己的字符串副本 对象指向。我们将显示复制控件的定义 下一节的成员。然而,你已经知道你所知道的一切 需要知道如何实现这些成员。写下HasPtr副本 在阅读之前,构造函数和复制赋值运算符。(第511页) 类的代码HasPtr: class HasPtr { public: //! default constructor HasPtr(const std::string &s = std::s
HasPtr
:
class HasPtr
{
public:
//! default constructor
HasPtr(const std::string &s = std::string()):
ps(new std::string(s)), i(0) { }
//! copy constructor
HasPtr(const HasPtr& hp) : ps(new std::string(*hp.ps)), i(hp.i) { }
HasPtr&
operator = (const HasPtr& hp);
~HasPtr()
{
delete ps;
}
private:
std::string *ps;
int i;
};
此复制分配运算符的我的代码:
HasPtr&
HasPtr::operator = (const HasPtr &hp)
{
delete ps;
ps = new std::string(*hp.ps);
i = hp.i;
return *this;
}
本书以下章节中提供的代码:
HasPtr&
HasPtr::operator = (const HasPtr &rhs)
{
auto newp = new string(*rhs.ps); // copy the underlying string
delete ps; // free the old memory
ps = newp; // copy data from rhs into this object
i = rhs.i;
return *this; // return this object
}
通过一步一步地执行,我发现这两个代码之间有细微的差别。在我的代码中,它不会更改
ps
指向的地址,而书中的代码使ps
指向一个新地址。我想知道这种细微的差别是否有任何意义?在类似情况下,我是否应该始终更改指向新地址的指针?为什么?您的版本对于自我分配不安全
delete ps;
ps = new std::string(*hp.ps);
在这里,如果执行自赋值,您可能会同时删除源和目标中的ps,从而使其在新语句中的使用错误(尽管它在大多数情况下可能会起到欺骗性的作用)
您可以将新变量的值直接分配到您的成员变量中,但在知道是否需要它之前,您不能随意删除ps
如果您在编写代码之前测试了自我分配,例如,
If(this!=&hp)
,那么它会更好,但仍然不理想(请参阅其他地方的异常安全注释)。您的版本对于自我分配是不安全的
delete ps;
ps = new std::string(*hp.ps);
在这里,如果执行自赋值,您可能会同时删除源和目标中的ps,从而使其在新语句中的使用错误(尽管它在大多数情况下可能会起到欺骗性的作用)
您可以将新变量的值直接分配到您的成员变量中,但在知道是否需要它之前,您不能随意删除ps
如果您在编写代码之前测试了自分配,例如
If(this!=&hp)
,那么它会更好,但仍然不理想(请参阅其他地方的异常安全注释)。您的代码在自分配和异常方面存在问题:假设内存分配引发了std::bad_alloc
异常。在编码时,您应该始终假设内存分配可能出错,尽管实际很少出错。在代码中
delete ps;
ps = new std::string(*hp.ps);
当第二行代码引发异常时,ps
将指向过时成员。顺便说一句,如果您最终自行指定了对象,则实际上只有在访问对象之前才删除对象的内存。因此,最好先复制右侧的内容,然后将内容放置到位,最后释放资源
碰巧,这些正是
swap()
操作,通常用于任何类型的资源您的代码在自我分配和异常方面存在问题:假设内存分配引发
std::bad_alloc
异常。在编码时,您应该始终假设内存分配可能出错,尽管实际很少出错。在代码中
delete ps;
ps = new std::string(*hp.ps);
当第二行代码引发异常时,ps
将指向过时成员。顺便说一句,如果您最终自行指定了对象,则实际上只有在访问对象之前才删除对象的内存。因此,最好先复制右侧的内容,然后将内容放置到位,最后释放资源
碰巧,这些正是
swap()
操作,通常用于任何类型的资源在功能上,我只能看到一个区别
delete ps;
ps = new std::string(*hp.ps);
如果内存不足,调用new std::string
可能会引发异常。在您的例子中,ps
仍然具有旧的已删除字符串的地址,因此格式不正确。如果您从异常中恢复,可能会有人取消对ps的引用,坏事情就会发生
auto newp = new string(*rhs.ps); // copy the underlying string
delete ps; // free the old memory
ps = newp; // copy data from rhs into this object
在教科书代码中,ps
在分配新字符串之前不会被删除。在异常情况下,ps
仍然指向一个有效字符串,这样您就不会有格式错误的对象
一个问题的严重程度取决于几个不同的方面,但通常更好的做法是避免出现任何形式错误的对象。从功能上讲,我只能看到一个区别
delete ps;
ps = new std::string(*hp.ps);
如果内存不足,调用new std::string
可能会引发异常。在您的例子中,ps
仍然具有旧的已删除字符串的地址,因此格式不正确。如果您从异常中恢复,可能会有人取消对ps的引用,坏事情就会发生
auto newp = new string(*rhs.ps); // copy the underlying string
delete ps; // free the old memory
ps = newp; // copy data from rhs into this object
在教科书代码中,ps
在分配新字符串之前不会被删除。在异常情况下,ps
仍然指向一个有效字符串,这样您就不会有格式错误的对象
问题的严重程度取决于几个不同的方面,但通常更好的做法是避免出现格式错误的对象。您的代码中实际上有两个问题:
- 自我分配
- 例外安全
运算符new
或字符串的复制构造函数中),程序状态不会更改
我认为现代的做法是提供s