繁体   English   中英

C ++复制赋值语法比较-哪个更好?

[英]c++ copy assignment syntax comparison - which is better?

我正在大学学习C ++,在休息时,我将通过Strousrtup的“ CPP编程语言第4版”来填补我的理解以及课堂上所学课程的空白。

在3.3.1节中,他详细介绍了矢量类的简化版本的代码段(特定于类型,仅针对double类型):

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;
}

现在,我已经写了自己的重写副本赋值运算符版本,以与简化的准矢量一起使用,然后才能看到它,这似乎可以正常工作,但是我想知道,删除分配给它的内存是否有问题与Stroustrup相比, elem指向然后重新初始化它,就像我在下面所做的那样?

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;
}

是的,Strousrtup的方式可以安全地进行自我分配。 也就是说,可以将实例分配给自己

a = a;

读完这本书后,您可能想遍历Meyer的“ Effective C ++”(2005),这也是一本很好的教材,它考虑了这些问题。

如果发生异常,Stroustrup的实现不会破坏现有的元素,并允许自我分配。

您的版本不正确,并且行为未定义。 如果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;
}

如果您可以编写非抛出交换成员函数, 此方法效果很好。 您经常可以,因为交换指针是一个空位(因此,在这种情况下,所有交换都将执行swap elem ),但这不是给定的。 每个案例都需要单独评估。

交换惯用法确实提供了“强”保证:要么分配完全成功,要么对象的状态不变。 但是,您通常不需要此保证。 通常,对象处于某种连贯状态就足够了(以便可以将其破坏)。

最后:如果您的类具有多个资源,则几乎可以肯定要将它们封装在某种RAII类(例如,智能指针)或单独的基类中,以便使构造函数异常安全,从而使它们不会如果分配第二个资源失败,则泄漏第一个资源。 即使在只有一种资源的情况下,这也是一种有用的技术。 在原始示例中,如果elemstd::unique_ptr<double[]> ,则无需在赋值运算符中删除,只需:

elem = new double[v.size()];
//  copy...

就是所需要的。 在实践中,如果使用的是真实代码,那么解决解决方案的情况很少见; 例如,在实际代码中,原始问题将通过std::vector<double>来解决(并且std::vector的要求使得std::unique_ptr并不是真正的解决方案)。 但是它们确实存在,并且std::unique_ptr类的类(或更简单的作用域指针)当然是值得在您的工具箱中使用的解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM