简体   繁体   English

从重载的复制构造函数中调用默认复制构造函数

[英]Call default copy constructor from within overloaded copy constructor

I need to write a copy constructor that deep copies the contents of a std::shared_ptr .我需要编写一个复制构造函数来深度复制std::shared_ptr的内容。 However, there are a bunch of variable int a, b, c, d, e;但是,有一堆变量int a, b, c, d, e; also defined in the class.类中也有定义。 Is there a way to generate the default copy constructor code (or call the default copy constructor) inside my new overloaded one.有没有办法在我的新重载代码中生成默认的复制构造函数代码(或调用默认的复制构造函数)。

Here is a code snippet with a comment that hopefully clarifies the issue.这是一个带有注释的代码片段,希望能澄清这个问题。

class Foo {
public:
     Foo() {}
     Foo(Foo const & other);
     ...
private:
     int a, b, c, d, e;
     std::shared_ptr<Bla> p;
};

Foo::Foo(Foo const & other) {
    p.reset(new Bla(*other.p));

    // Can I avoid having to write the default copy constructor code below
    a = other.a;
    b = other.b;
    c = other.c;
    d = other.d;
    e = other.e;
}

I always think that questions like this should have at least one answer quote from the standard for future readers, so here it is.我一直认为像这样的问题应该至少有一个来自标准的答案引用给未来的读者,所以就在这里。

§12.8.4 of the standard states that:该标准的 §12.8.4 规定:

If the class definition does not explicitly declare a copy constructor, one is declared implicitly.如果类定义未显式声明复制构造函数,则隐式声明一个。

This implies that when a class definition does explicitly declare a copy constructor, one is not declared implicitly.这意味着,当一个类定义明确声明拷贝构造函数,一个隐含声明。 So if you declare one explicitly, the implicit one does not exist, so you can't call it.所以如果你显式声明一个,隐式一个不存在,所以你不能调用它。

Here's the question code as I'm writing this:这是我写这篇文章时的问题代码:

class Foo {
public:
     Foo() {}
     Foo(Foo const & other);
     ...
private:
     int a, b, c, d, e;
     std::shared_ptr<Bla> p;
};

Foo::Foo(Foo const & other) {
    p.reset(new Bla(other.p));

    // Can I avoid having to write the default copy constructor code below
    a = other.a;
    b = other.b;
    c = other.c;
    d = other.d;
    e = other.e;
}

The above code is most likely wrong , because上面的代码很可能是错误的,因为

  1. the default constructor leaves a , b , c , d and e uninitialized, and默认构造函数使abcde未初始化,并且

  2. the code does not take charge of assignment copying, and该代码不负责作业复制,并且

  3. the expression new Bla(other.p) requires that Bla has a constructor taking a std::shared_ptr<Bla> , which is extremely unlikely.表达式new Bla(other.p)要求Bla有一个采用std::shared_ptr<Bla>的构造函数,这是极不可能的。

With std::shared_ptr this would have to be C++11 code in order to be formally correct language-wise.使用std::shared_ptr这将必须是 C++11 代码才能正式正确的语言。 However, I believe that it's just code that uses what's available with your compiler.但是,我相信它只是使用编译器可用内容的代码。 And so I believe that the relevant C++ standard is C++98, with the technical corrections of the C++03 amendment.所以我认为相关的C++标准是C++98,加上C++03修正的技术修正。

You can easily leverage the built-in (generated) copy initialization, even in C++98, eg即使在 C++98 中,您也可以轻松利用内置(生成的)复制初始化,例如

namespace detail {
    struct AutoClonedBla {
        std::shared_ptr<Bla> p;

        AutoClonedBla( Bla* pNew ): p( pNew ) {}

        AutoClonedBla( AutoClonedBla const& other )
            : p( new Bla( *other.p ) )
        {}

        void swap( AutoClonedBla& other )
        {
            using std::swap;
            swap( p, other.p );
        }

        AutoClonedBla& operator=( AutoClonedBla other )
        {
            other.swap( *this );
            return *this;
        }
    };
}

class Foo {
public:
     Foo(): a(), b(), c(), d(), e(), autoP( new Bla ) {}
     // Copy constructor generated by compiler, OK.

private:
     int                      a, b, c, d, e;
     detail::AutoClonedBla    autoP;
};

Note that this code does initialize correctly in the default constructor, does take charge of copy assignment (employing the swap idiom for that), and does not require a special smart-pointer-aware Bla constructor, but instead just uses the ordinary Bla copy constructor to copy.请注意,此代码确实在默认构造函数中正确初始化,确实负责复制分配(为此使用交换习语),并且不需要特殊的智能指针感知Bla构造函数,而是仅使用普通的Bla复制构造函数复制。

It would be easier to write a variation on shared_ptr that has deep copying built into it.shared_ptr上编写一个内置深度复制的变shared_ptr更容易。 That way, you don't have to write a copy constructor for your main class;这样,您就不必为主类编写复制构造函数; just for this special deep_copy_shared_ptr type.仅针对这种特殊的deep_copy_shared_ptr类型。 Your deep_copy_shared_ptr would have a copy constructor, and it would store a shared_ptr itself.您的deep_copy_shared_ptr将有一个复制构造函数,它本身会存储一个shared_ptr It could even have an implicit conversion to shared_ptr , to make it a bit easier to use.它甚至可以隐式转换为shared_ptr ,以使其更易于使用。

That's not possible.那是不可能的。 It's either you write a custom copy constructor (entirely on your own) or the compiler writes it for you.要么您编写自定义复制构造函数(完全由您自己编写),要么编译器为您编写。

Note that if you write a copy constructor then you probably need a copy assignment and a destructor as well, because writing any of these three resource-management functions implies you're managing a resource.请注意,如果您编写了一个复制构造函数,那么您可能还需要一个复制赋值和一个析构函数,因为编写这三个资源管理函数中的任何一个都意味着您正在管理一个资源。 With the copy-and-swap idiom, however, you only need to write the copy logic once, in the copy constructor, and you then define the assignment operator in terms of the copy constructor.然而,使用复制和交换习语,您只需在复制构造函数中编写一次复制逻辑,然后根据复制构造函数定义赋值运算符。

Aside from that, I'm not entirely sure why you're using a shared_ptr<> .除此之外,我不完全确定您为什么使用shared_ptr<> The point of a shared_ptr<> is to allow multiple pointers to safely point at the same object. shared_ptr<>在于允许多个指针安全地指向同一个对象。 But you're not sharing the pointee, you deep-copy it.但是您并没有共享指针对象,而是深度复制了它。 Maybe you should use a raw pointer instead, and free it in the destructor.也许您应该改用原始指针,并在析构函数中释放它。 Or, better yet, replace the shared_ptr<> with a clone_ptr , and then eliminate the copy constructor, copy assignment and destructor altogether.或者,更好的是,将shared_ptr<>替换为clone_ptr ,然后完全消除复制构造函数、复制赋值和析构函数。

Not to my knowledge, but what you can (and should) do is use an initializer list, anyway:据我所知,您可以(并且应该)做的是使用初始化列表,无论如何:

Foo::Foo(Foo const & other)
    : a(other.a), b(other.b), c(other.c), 
      d(other.d), e(other.e), p(new Bla(other.p))
{}

This won't save you from the writing, but it will save you from the possible performance penalty of assigning (unneccessarily) default-constructed members (although in this case it might be fine) and the many other pitfalls this could bring.这不会使您免于写作,但它会使您免于分配(不必要)默认构造的成员(尽管在这种情况下可能没问题)可能带来的性能损失以及这可能带来的许多其他陷阱。 Always use initializer lists in constructors, if possible.如果可能,请始终在构造函数中使用初始化列表。

And by the way, Kerrek's comment is right.顺便说一下,Kerrek 的评论是正确的。 Why do you need a shared_ptr , if you make a deep copy anyway.如果无论如何都要进行深层复制,为什么还需要shared_ptr In this case a unique_ptr might be more appropriate.在这种情况下, unique_ptr可能更合适。 Besides it's benefits shared_ptr is not a general no-more-to-think-about-deallocation solution and you should always think if you need a smart pointer and what type of smart pointer is most appropriate.除了它的好处shared_ptr不是一个通用的无需再考虑解除分配的解决方案,您应该始终考虑是否需要智能指针以及哪种类型的智能指针最合适。

The default assignment operator has the same code as the default copy constructor.默认赋值运算符与默认复制构造函数具有相同的代码。 Though you can't call the default copy constructor from your overload, the same code exists in the assignemnt so.尽管您不能从重载中调用默认的复制构造函数,但分配中存在相同的代码。

Foo::Foo(Foo const & other) 
{
    *this = other;
    p.reset(new Bla(other.p));
}

should get you what you need.应该给你你需要的东西。

Edit: Nevermind it is in fact the same code, and explicitly declaring the copy construtor prevents it generation :(编辑:没关系它实际上是相同的代码,并且显式声明复制构造函数可以防止它生成:(

One trick can be to put those "simple" fields into a base class一个技巧是将那些“简单”的字段放入基类

class FooBase {
protected:
    int a, b, c, d, e;
};

class Foo : public FooBase {
public:
     Foo() {}
     Foo(const Foo& other)
       : FooBase(other),         // This copies all the simple fields
         p(new Bla(other.p))     // Manually do the hard part
     {}
     ...
private:
     std::shared_ptr<Bla> p;
};

Then later, if you add any simple fields to Foo, you can put them to FooBase and don't need to worry about updating the copy constructor.之后,如果您向 Foo 添加任何简单字段,则可以将它们放入 FooBase,而无需担心更新复制构造函数。

Don't forget to add a similiar copy assignment operator, if needed.如果需要,不要忘记添加类似的复制赋值运算符。 And on modern compilers, also add move constructor and move assignement operator.在现代编译器上,还添加了移动构造函数和移动赋值运算符。 These two can probably be defaulted, as you don't need deep copy on move operations.这两个可能是默认的,因为您不需要移动操作的深层复制。

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

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