Call default copy constructor from within overloaded copy constructor

给你一囗甜甜゛ 提交于 2020-01-13 07:55:45

问题


I need to write a copy constructor that deep copies the contents of a std::shared_ptr. However, there are a bunch of variable 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;
}

回答1:


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

  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.

With std::shared_ptr this would have to be C++11 code in order to be formally correct language-wise. 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.

You can easily leverage the built-in (generated) copy initialization, even in C++98, e.g.

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.




回答2:


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:

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.




回答3:


It would be easier to write a variation on shared_ptr that has deep copying built into it. That way, you don't have to write a copy constructor for your main class; just for this special deep_copy_shared_ptr type. Your deep_copy_shared_ptr would have a copy constructor, and it would store a shared_ptr itself. It could even have an implicit conversion to shared_ptr, to make it a bit easier to use.




回答4:


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. Why do you need a shared_ptr, if you make a deep copy anyway. In this case a unique_ptr might be more appropriate. 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.




回答5:


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<>. The point of a shared_ptr<> is to allow multiple pointers to safely point at the same object. 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.




回答6:


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 :(



来源:https://stackoverflow.com/questions/7297024/call-default-copy-constructor-from-within-overloaded-copy-constructor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!