简体   繁体   English

这是好的代码吗? (复制构造函数和赋值运算符)

[英]Is this good code? (copy constructor and assignment operator )

For one reason or another, I'm forced to provide both a copy constructor and an operator= for my class. 由于某种原因,我不得不为我的班级提供复制构造函数和operator =。 I thought I didn't need operator= if I defined a copy ctor, but QList wants one. 我以为我不需要operator=如果我定义了一个副本ctor,但QList想要一个。 Putting that aside, I hate code duplication, so is there anything wrong with doing it this way? 把它放在一边,我讨厌代码重复,所以这样做有什么不对吗?

Fixture::Fixture(const Fixture& f) {
    *this = f;
}

Fixture& Fixture::operator=(const Fixture& f) {
    m_shape         = f.m_shape;
    m_friction      = f.m_friction;
    m_restitution   = f.m_restitution;
    m_density       = f.m_density;
    m_isSensor      = f.m_isSensor;
    return *this;
}

And just out of curiosity, there's no way to switch it so that the bulk of the code is in the copy ctor and operator= somehow utilizes it? 只是出于好奇,没有办法切换它,以便大部分代码都在复制ctor和operator=以某种方式利用它? I tried return Fixture(f); 我试过return Fixture(f); but it didn't like that. 但它不喜欢那样。


It appears I need to make it more clear that the copy constructor and assignment operator have been implicitly disabled by the class I am inheriting from. 看来我需要更清楚地说明复制构造函数和赋值运算符已被我继承的类隐式禁用。 Why? 为什么? Because it's an abstract base class that shouldn't be instantiated on its own. 因为它是一个抽象的基类,不应该自己实例化。 This class, however, is meant to stand alone. 然而,这门课程独立的。

This is bad, because the operator= can't rely on a set-up object anymore. 这很糟糕,因为operator=不能再依赖于设置对象了。 You should do it the other way around, and can use the copy-swap idiom. 你应该反过来做,并可以使用复制交换习语。

In the case where you just have to copy over all elements, you can use the implicitly generated assignment operator. 如果您只需要复制所有元素,则可以使用隐式生成的赋值运算符。

In other cases, you will have to do something in addition, mostly freeing and copying memory. 在其他情况下,您还必须做一些事情,主要是释放和复制内存。 This is where the copy-swap idiom is good for. 这就是复制交换习惯用法的好处。 Not only is it elegant, but it also provide so an assignment doesn't throw exceptions if it only swaps primitives. 它不仅优雅,而且还提供如此分配,如果只交换原语,则不会抛出异常。 Let's a class pointing to a buffer that you need to copy: 让我们指向一个需要复制的缓冲区:

Fixture::Fixture():m_data(), m_size() { }

Fixture::Fixture(const Fixture& f) {
    m_data = new item[f.size()];
    m_size = f.size();
    std::copy(f.data(), f.data() + f.size(), m_data);
}

Fixture::~Fixture() { delete[] m_data; }

// note: the parameter is already the copy we would
// need to create anyway. 
Fixture& Fixture::operator=(Fixture f) {
    this->swap(f);
    return *this;
}

// efficient swap - exchanging pointers. 
void Fixture::swap(Fixture &f) {
    using std::swap;
    swap(m_data, f.m_data);
    swap(m_size, f.m_size);
}

// keep this in Fixture's namespace. Code doing swap(a, b)
// on two Fixtures will end up calling it. 
void swap(Fixture &a, Fixture &b) {
  a.swap(b);
}

That's how i write the assignment operator usually. 这就是我通常编写赋值运算符的方式。 Read Want speed? 读取想要的速度? Pass by value about the unusual assignment operator signature (pass by value). 传递有关异常赋值运算符签名的值(按值传递)。

Copy ctor and assignment are entirely distinct -- assignment typically needs to free resources in the object that it's replacing, copy ctor is working on a not-yet-initialized object. 复制ctor和赋值完全不同 - 赋值通常需要释放它所替换的对象中的资源,复制ctor正在处理一个尚未初始化的对象。 Since here you apparently have no special requirements (no "releasing" needed on assignment), your approach is fine. 从那以后你显然没有特殊要求(转让时不需要“释放”),你的方法很好。 More in general, you might have a "free all resources the object is holding" auxiliary method (to be called in the dtor and at the start of assignment) as well as the "copy these other things into the object" part that's reasonably close to the work of a typical copy ctor (or mostly, anyway;-). 更一般地说,你可能有一个“对象所持有的所有资源”辅助方法(在dtor和分配开始时调用)以及“将这些其他东西复制到对象中”这一部分相当接近一个典型的复制ctor(或大部分,无论如何;-)的工作。

You're simply doing a member-wise copy and assignment in your examples. 您只是在示例中执行成员明确的复制和分配。 This is not something you need to write yourself. 这不是你自己需要写的东西。 The compiler can generate implicit copy and assignment operations that do exactly that. 编译器可以生成完全相同的隐式复制和赋值操作。 You only need to write your own copy constructor, assignment and/or destructor if the compiler-generated ones are not appropriate (ie in case you manage some resource through a pointer or something like that) 如果编译器生成的那些不合适,你只需要编写自己的拷贝构造函数,赋值和/或析构函数(例如,如果你通过指针管理某些资源或类似的东西)

I think you run into issues if your operator= ever becomes virtual. 如果您的运营商变得虚拟,我认为您会遇到问题。

I would recommend writing a function (maybe static) that does the copy then have the copy-constructor and operator= call that function instead. 我建议写一个函数(也许是静态的),然后复制然后让copy-constructor和operator =调用该函数。

Yes, this is good practice and should (almost) always be done. 是的,这是一种很好的做法,应该(几乎)总是这样做。 In addition toss in a destructor and default constructor (even if you make it private). 另外在析构函数和默认构造函数中抛出(即使你将其设为私有)。

In James Coplien's 1991 book Advanced C++ , this is described as part of "Orthodox Canonical Form". 在James Coplien 1991年出版的“ 高级C ++”一书中,这被描述为“正统规范形式”的一部分。 In it, he advocates for a default constructor, a copy constructor, the assignment operator and a destructor. 在其中,他提倡默认构造函数,复制构造函数,赋值运算符和析构函数。

In general, you must use the orthodox canonical form if: 通常,您必须使用正统的规范形式,如果:

  • You want to support assignment of object of the class, or want to pass those objects as call-by-value parameters to a function, and 您希望支持类的对象的赋值,或者希望将这些对象作为按值调用参数传递给函数, 以及
  • The object contains pointers to objects that are reference-counted, or the class destructor performs a delete on a data member of the object. 该对象包含指向引用计数的对象的指针,或者类析构函数对该对象的数据成员执行delete

You should use the orthodox canonical form for any nontrivial class in a program, for the sake of uniformity across classes and to manage the increasing complexity of each class over the course of program evolution. 对于程序中的任何非平凡类,您应该使用正统的规范形式,以便在各个类之间保持一致,并在程序演变过程中管理每个类的日益复杂。

Coplien offers pages of reasons for this pattern and I couldn't do them justice here. Coplien提供了这种模式的原因页面,我不能在这里做正义。 However, a key item that has already been touched on is the ability to clean up the object that is being overwritten. 但是,已经触及的关键项是能够清理被覆盖的对象。

I think you should initialize your object member variables using initializer list . 我认为你应该使用initializer list初始化你的对象成员变量。 If your variables are of primitive-types , then it doesn't matter. 如果你的变量是primitive-types ,那么它并不重要。 Otherwise, assignment is different from initialization. 否则,赋值与初始化不同。


You could do it with a little trick by initializing pointers inside the copy constructor to 0 , then you could call delete safely in the assignment operator : 您可以通过将copy constructor内的指针初始化为0来完成一个小技巧,然后您可以在assignment operator安全地调用delete:

Fixture::Fixture(const Fixture& f) : myptr(0) {
    *this = f;
}
Fixture& Fixture::operator=(const Fixture& f) {
    // if you have a dynamic array for example, delete other wise.
    delete[] myptr;
    myptr = new int[10];
    // initialize your array from the other object here.
    ......
    return *this;
}

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

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