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

发布于 2024-12-02 17:33:26 字数 586 浏览 1 评论 0原文

我需要编写一个复制构造函数来深度复制 std::shared_ptr 的内容。然而,类中还定义了一堆变量int a, b, c, d, e;。有没有一种方法可以在我的新重载代码中生成默认的复制构造函数代码(或调用默认的复制构造函数)。

这是一个带有注释的代码片段,希望能够澄清该问题。

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

帝王念 2024-12-09 17:33:26

我一直认为像这样的问题应该至少有一个来自标准的答案引用,供未来的读者参考,所以就在这里。

该标准第 12.8.4 条规定:

如果类定义没有显式声明复制构造函数,则会隐式声明一个复制构造函数。

这意味着,当类定义确实显式声明复制构造函数时,不会隐式声明复制构造函数。因此,如果您显式声明一个,则隐式的不存在,因此您无法调用它。

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.

笙痞 2024-12-09 17:33:26

这是我写这篇文章时的问题代码:

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. 默认构造函数留下abcde 未初始化,并且

  2. 代码不负责赋值复制,并且

  3. < p>表达式 new Bla(other.p) 要求 Bla 有一个采用 std::shared_ptr 的构造函数,即极不可能。

对于 std::shared_ptr ,这必须是 C++11 代码才能在形式上正确的语言。但是,我相信这只是使用编译器可用内容的代码。因此我认为相关的 C++ 标准是 C++98,并进行了 C++03 修订案的技术修正。

即使在 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;
};

注意,此代码确实在默认构造函数中正确初始化,并且负责复制分配(使用 交换惯用法),并且不需要特殊的智能指针感知Bla 构造函数,而是使用普通的 Bla 复制构造函数进行复制。

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.

杀手六號 2024-12-09 17:33:26

编写内置深度复制的 shared_ptr 变体会更容易。这样,您不必为主类编写复制构造函数;只是针对这个特殊的 deep_copy_shared_ptr 类型。您的deep_copy_shared_ptr将有一个复制构造函数,并且它本身会存储一个shared_ptr。它甚至可以隐式转换为 shared_ptr,以使其更易于使用。

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.

心的憧憬 2024-12-09 17:33:26

那是不可能的。要么您编写一个自定义复制构造函数(完全由您自己编写),要么由编译器为您编写。

请注意,如果您编写复制构造函数,那么您可能还需要复制赋值和析构函数,因为编写这三个资源管理函数中的任何一个都意味着您正在管理资源。然而,使用复制和交换习惯用法,您只需在复制构造函数中编写一次复制逻辑,然后根据复制构造函数定义赋值运算符。

除此之外,我不完全确定您为什么使用 shared_ptrshared_ptr 的要点是允许多个指针安全地指向同一个对象。但你不是共享被指者,而是深度复制它。也许您应该使用原始指针,并在析构函数中释放它。或者,更好的是,用 clone_ptr 替换 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<>. 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.

貪欢 2024-12-09 17:33:26

据我所知,但无论如何,您可以(并且应该)做的是使用初始值设定项列表:

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))
{}

这不会使您免于编写,但它可以使您免于分配(不必要的)默认构造的可能的性能损失成员(尽管在这种情况下可能没问题)以及这可能带来的许多其他陷阱。如果可能,请始终在构造函数中使用初始值设定项列表。

顺便说一下,Kerrek 的评论是正确的。如果您无论如何都进行深层复制,为什么还需要 shared_ptr 呢?在这种情况下,unique_ptr 可能更合适。除了它的好处之外,shared_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. 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.

永言不败 2024-12-09 17:33:26

默认赋值运算符与默认复制构造函数具有相同的代码。尽管您无法从重载中调用默认的复制构造函数,但在 allocateemnt so 中存在相同的代码。

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

应该给你你需要的东西。

编辑:没关系,它实际上是相同的代码,并且显式声明复制构造函数可以防止它生成:(

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

热情消退 2024-12-09 17:33:26

一个技巧可以是将这些“简单”字段放入基类中

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

,然后,如果向 Foo 添加任何简单字段,则可以将它们放入 FooBase 中,而无需担心更新复制构造函数。

如果需要,不要忘记添加类似的复制赋值运算符。在现代编译器上,还添加移动构造函数和移动赋值运算符。这两个可能可以默认,因为您不需要在移动操作上进行深层复制。

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文