c ++ copy assignment语法比较 - 哪个更好?

时间:2014-04-15 05:13:36

标签: c++ c++11 operator-overloading

我正在大学学习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;
}

3 个答案:

答案 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这样的类(甚至更简单 范围指针)肯定是一个值得拥有的解决方案 工具包。

相关问题