代码之家  ›  专栏  ›  技术社区  ›  sagi

在派生类中使用基类复制构造函数

  •  8
  • sagi  · 技术社区  · 8 年前

    我知道有很多关于这个问题的帖子,但是我找不到任何可以完全回答我问题的帖子。

    假设我有一个基类和一个派生类,我为它实现了一个cctor和一个赋值运算符,如下所示:

    class Base {
        char * name;
        ....
        Base(const Base& other) :name(nullptr) { *this = other }
        void operator=(const Base& other) { ... Deep copy of name } 
    }
    
    
    class Derived : public Base {
    .... 
        Derived(const Derived& other) { *this = other ; }
        void operator=(const Derived& other) {
            Base::operator=(other);
            .....
        }
    

    现在我有一些关于这个设计的问题。

    1. 这是适合这种情况的设计吗?
    2. 如果我有第三个类,在基类和派生类之间,但它只包含基元类型,我应该在哪里复制它们?例如,使用第二类的默认赋值运算符?建一个新的?只在第三层复制?
    3. 我可以类似地调用派生类cctor中的基类cctor,而不是赋值运算符。有什么区别?如果我将它们放在两个方法中,它会尝试复制两次这些值吗?

    编辑:为了澄清,设计是我在项目中得到的。我有指针,所以我必须用一个深拷贝。

    1 回复  |  直到 8 年前
        1
  •  8
  •   Jonathan Wakely    8 年前

    这是适合这种情况的设计吗?

    不,通常不会。更惯用的方法是停止像 char* name 使用 std::string 或者是另一种做正确事情的类型:

    Class Base {
        std::string name;
        ....
        Base(const Base& other) = default;
        Base& operator=(const Base& other) = default;
    };
    

    (注意赋值运算符应该返回对类的引用,而不是 void )

    或者将内存管理封装到专门为此目的而设计的类中(但是 STD::字符串 已经是那种类型了)。

    如果你真的 真的? 需要以容易出错的方式进行复制,然后实现复制构造函数来进行复制:

        Base(const Base& other) { / * deep copy of name */ }
    

    然后以复制和交换的方式执行分配:

        Base& operator=(const Base& other)
        {
          Base tmp(other);
          this->swap(tmp);
          return *this;
        }
    

    这意味着你需要一个便宜的,不扔的 swap(Base&) 成员函数。

    无论如何,派生类型复制构造函数都是愚蠢的。基类的复制构造函数正确,因此应该是:

    Derived(const Derived& other) : Base(other) { }
    

    并且赋值可以使用基赋值:

    Derived& operator=(const Derived& other)
    {
       Base::operator=(other);
       return *this;
    }
    

    但是手动编写这些文件是不必要的,您可以默认它的复制操作,这样做无论如何都是正确的:

    class Derived : public Base {
    public:
        Derived(const Derived&) = default;
        Derived& operator=(const Derived& ) = default;
    

    如果我有第三个类,在基类和派生类之间,但它只包含基元类型,我应该在哪里复制它们?例如,使用第二类的默认赋值运算符?建一个新的?只在第三层复制?

    您应该使用 = default 也。一旦你做到了 Base 使用正确的行为进行安全的复制,编写其他类来使用它是微不足道的。默认行为将做正确的事情。如果需要对动态分配的内存进行特殊处理,而raii类型无法正确管理这些内存,则只需手动定义这些操作。

    我可以类似地调用派生类cctor中的基类cctor,而不是赋值运算符。有什么区别?