繁体   English   中英

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

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

我需要编写一个复制构造函数来深度复制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;
}

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

该标准的 §12.8.4 规定:

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

这意味着,当一个类定义明确声明拷贝构造函数,一个隐含声明。 所以如果你显式声明一个,隐式一个不存在,所以你不能调用它。

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

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. 表达式new Bla(other.p)要求Bla有一个采用std::shared_ptr<Bla>的构造函数,这是极不可能的。

使用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复制构造函数复制。

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

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

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

除此之外,我不完全确定您为什么使用shared_ptr<> shared_ptr<>在于允许多个指针安全地指向同一个对象。 但是您并没有共享指针对象,而是深度复制了它。 也许您应该改用原始指针,并在析构函数中释放它。 或者,更好的是,将shared_ptr<>替换为clone_ptr ,然后完全消除复制构造函数、复制赋值和析构函数。

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

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不是一个通用的无需再考虑解除分配的解决方案,您应该始终考虑是否需要智能指针以及哪种类型的智能指针最合适。

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

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

应该给你你需要的东西。

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

一个技巧是将那些“简单”的字段放入基类

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,而无需担心更新复制构造函数。

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

暂无
暂无

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

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