【问题标题】:copy constructor for derived class changing base class派生类更改基类的复制构造函数
【发布时间】:2018-09-24 18:49:27
【问题描述】:

假设我有以下情况:

class NamedObject{
public:
    NamedObject(const std::string &name):name_{std::move(name)}{}
private: 
    const std::string name_;
}

class Person: public NamedObject{
public:
    Person(const std::string &name, int age): NamedObject(name), age_{age}{}
private:
    int age_;
}

我想创建一个“复制构造函数”,在其中复制来自Person 的所有成员,但更改名称(无论出于何种原因)。

class Person: public NamedObject{
public:
    Person(const std::string &name, int age): NamedObject(name), age_{age}{}
    Person(const std::string &newName, const Person &other): NamedObject(name){
        age_ = other.age;
    }
private:
    int age_;
}

现在假设我不仅有一个像age 这样的属性,而且还有许多属性,并且它们在开发过程中发生了很大变化。是否可以轻松地创建像 Person(const std::string &newName, const Person &other) 这样的函数,而无需手动复制像 age_ = other.age; 这样的所有属性。这个想法是,在开发过程中,如果我添加一个新属性,我不必总是记得更改这个构造函数。

请注意,我不能简单地更改名称,因为它是 const

【问题讨论】:

  • 不要让它成为常量。
  • 你可以有一个虚函数getName(),还有一个额外的string Person::nameReplacement,在getName中你可以返回Person::nameReplacement或者NamedObject::name_
  • 除此之外,您使用 std::move 不正确。
  • std::move 错误感到抱歉...我知道。
  • 作为设计决策,您不需要创建成员变量const,因为它实际上不具有您在 C++ 中想要的不变性语义。相反,只需将其设为私有的,使用公共 getter 而没有 setter,它实际上是不可变的。

标签: c++ inheritance copy-constructor derived-class


【解决方案1】:

您可以在NamedObject 上定义一个不执行任何操作的赋值运算符。然后你就可以在“复制构造函数”中使用*this = other

class NamedObject{
public:
    NamedObject(const std::string &name):name_{name}{}
    NamedObject &operator= (const NamedObject &) {
        return *this;
    }
    const std::string name_;
};


class Person: public NamedObject{
public:
    Person(const std::string &name, int age): NamedObject(name), age_{age}{}
    Person(const std::string &newName, const Person &other): NamedObject(newName){
        *this = other;
    }
    int age_;
};

现场示例:https://onlinegdb.com/rJ4J5oy3M

为什么会这样:

C++ 将自动为 eligible 类定义一个复制赋值运算符(查看以下链接了解 eligible 含义的详细信息)。来自cppreference

如果隐式声明的复制赋值运算符没有被删除 也不是微不足道的,它被定义(即生成一个函数体并 编译)由编译器(如果使用了 odr)。对于联合类型, 隐式定义的复制赋值复制对象表示 (如 std::memmove )。对于非联合类类型(类和结构), 运算符执行对象的逐个成员复制分配 基类和非静态成员,按照它们的初始化顺序,使用 标量的内置赋值和复制赋值运算符 类类型。

因此,对于您的示例,隐式定义的复制赋值运算符(在我们执行 *this = other; 时调用的运算符)将定义如下:

  • 它将使用NamedObject的复制赋值运算符复制基类(我们定义它什么都不做,所以它不会复制name_),
  • 它将为age_ 和您添加到类的任何其他成员使用复制赋值运算符。

【讨论】:

  • 谢谢,你能解释一下为什么我需要NamedObject中的赋值运算符吗?
  • @user7431005 • 因为const std::string name_;
  • @user7431005 当然,我更新了答案以包含解释
  • 另外,正如@Eljay 所指出的,如果您不定义自己的赋值运算符,编译器将不会为NamedObject 合成一个(因为name_ 是常量,因此无法赋值to),并且通过扩展它不会为Person 合成一个(因为NamedObject 没有一个)。
  • 以这种方式重新定义运算符的功能是主要的代码异味。请不要这样做。当你想到这样做时,小猫就会死去。
猜你喜欢
  • 2013-06-23
  • 1970-01-01
  • 2016-07-19
  • 2012-09-28
  • 2018-07-16
  • 1970-01-01
  • 2012-03-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多