我正在大学学习C ++,在休息时我正在学习Strousrtup" CPP编程语言第4版"填补我理解的空白以及我们在课堂上所教的内容。
在3.3.1节中,他详细介绍了矢量类的简化版本的代码片段(特定于类型,仅用于加倍):
Vector& Vector::operator=(const Vector& a) {
double* p = new double[a.sz];
for (int i=0; i!=a.sz; ++i)
p[i] = a.elem[i];
delete[] elem;
elem = p;
sz = a.sz;
return *this;
}
现在,我已经编写了我自己版本的重写复制赋值运算符,以便在看到这个之前使用这个简化的准矢量,这似乎工作正常,但我想知道,删除是否有任何问题分配给elem
分配的内存,然后重新初始化,如下所示,与Stroustrup的做法相比较?
vectoR& vectoR::operator=(const vectoR& v) {
delete[] elem;
elem = new double[v.size()];
sz = v.size();
for (int i = 0; i != sz; ++i)
elem[i] = v[i];
return *this;
}
答案 0 :(得分:5)
是的,Strousrtup的方式是自我安排。也就是说,可以将实例分配给自己
a = a;
一旦你完成了这本书,你可能想要通过迈耶的#34;有效的C ++" (2005)这也是一个很好的文本,并考虑这些问题。
答案 1 :(得分:5)
如果发生异常,Stroustrup的实现不会破坏现有元素,并允许自我分配。
答案 2 :(得分:2)
您的版本不正确,并且有未定义的行为。什么
如果new double[v.size()]
抛出异常会发生什么?
一般情况下,你不应该做任何可能导致失效的事情
对象,直到你完成了所有可能抛出的东西
例外。将一个点留给删除的内存导致一个
无效的对象,new
总是可以扔,所以你不应该
在您完成elem
之后删除new
。
编辑:
更明确:从原始海报的建议 实现:
delete[] elem;
elem = new double[v.size()];
第一行使指针elem
无效,如果存在则无效
第二行中的异常(new
总是可以抛出一个异常
异常),然后赋值运算符离开对象
无效指针;进一步访问此指针,
包括在对象的析构函数中,是未定义的
行为。
delete[] elem;
elem = nullptr;
elem = new double[v.size()];
例如(假设在对象上调用任何函数 可以处理空指针),或(实际上是什么相同 物):
delete[] elem;
elem = new (std::nothrow) double[v.size()];
if ( elem == nullptr )
throw std::bad_alloc();
然而,这些解决方案在很多方面都很特别
一般不适用。他们也把对象留在了
一个特殊的州,可能需要额外的处理。该
通常的解决方案是在修改之前做任何可以抛出的事情
任何状态的对象。在这种情况下,唯一的事情
可以抛出的是new
,我们最终得到了Stroustrup的
解。在更复杂的对象中,必要的解决方案
可能更复杂;一个常见的简单解决方案是交换
成语:
MyType& MyType::operator=( MyType const& other )
{
MyType tmp( other ); // Copy constructor
swap( tmp ); // Member function, guaranteed nothrow
return *this;
}
这很好用如果你可以写一个非挖掘交换成员
功能。你经常可以,因为交换指针是一个无关紧要的
(所以在这种情况下,所有交换都会交换elem
),但确实如此
不是给定的。每个案例都需要单独评估。
交换习语确实给出了“强有力”的保证:要么是 赋值完全成功,或者对象的状态不变。 但是,您通常不需要这种保证;通常是这样的 足以使物体处于某种连贯状态(这样它 可以被破坏了。
最后:如果你的班级有几个资源,你几乎就可以了
当然希望将它们封装在某种RAII类中
(例如智能指针)或单独的基类,以便你
可以使构造函数异常安全,这样他们就不会
如果分配第二个资源失败,则泄漏第一个资源。这个
即使在只有一个的情况下,它也是一种有用的技术
资源;在原始示例中,如果elem
是一个
std::unique_ptr<double[]>
,不需要删除
在赋值运算符中,只是:
elem = new double[v.size()];
// copy...
就是所需要的一切。在实践中,如果真实的代码,案例
解决问题的方法相当少见;在实际代码中,
例如,原始问题将通过解决
std::vector<double>
(以及std::vector
的要求是
这样std::unique_ptr
并不是真正的解决方案。但是他们
确实存在,像std::unique_ptr
这样的类(甚至更简单
范围指针)肯定是一个值得拥有的解决方案
工具包。