【问题标题】:Override initial value of a member variable of base class覆盖基类成员变量的初始值
【发布时间】:2016-01-04 13:33:47
【问题描述】:

我现在对继承很困惑。我计划简单地覆盖变量的初始值。在下面的代码中,我只是继承了基类并尝试获取它的名称,该名称与类一起保存为字符串。我希望派生类可以覆盖这个值,但它没有这样做。

我的预期输出是

Derived
Derived

但是我得到了

Base
Base

以下实现的正确方法是什么?

#include <iostream>
#include <string>

struct Base {
    virtual ~Base() = default;
    virtual void id(){
        std::cout << id_ << std::endl;
    }
    std::string id_ = "Base";
};   



struct Derived : public Base {
    virtual ~Derived() = default;
    std::string id_ = "Derived";
};   


int main(){
    Base* b = new Derived();
    Derived* d = new Derived();
    b->id();
    d->id();
    delete d;
    delete b;                                                                                                          
    return 0;
}

【问题讨论】:

  • 成员变量不能是虚拟的(即可覆盖),只能是方法。

标签: c++ inheritance


【解决方案1】:

以下实现的正确方法是什么?

听起来很难,这实际上取决于您想要实现的目标。

我猜测我们只是在询问一个对象它是什么类型,而不管我们调用的是哪个接口:

struct Base {
  virtual ~Base() = default;

  virtual const std::string id() const {
    return "Base";
  }
};

struct Derived : Base {
  virtual const std::string id() const override {
    return "Derived";
  }
};

这是另一种方式:

struct Base {
  virtual Base(std::string ident = "Base") 
  : _id(std::move(ident))
  {}

  virtual ~Base() = default;

  std::string& id() const {
    return _id;
  }

private:
  std::string _id;

};

struct Derived : Base {
  Derived() : Base("Derived") {}
};

另一个,使用值是接口,但注意这将禁用赋值运算符

struct Base {
  virtual Base(std::string ident = "Base") 
  : id(std::move(ident))
  {}

  virtual ~Base() = default;

  const std::string id;

};

struct Derived : Base {
  Derived() : Base("Derived") {}
};

这个列表绝不是详尽的。

【讨论】:

  • 但这是一个非常好的开始 :) 它为 OP 提供了几种实现这一目标的方法,我相信实验会证明哪一种最适合他们。
【解决方案2】:

存在虚拟函数,而不是虚拟成员变量。

您在Derived 类中所做的是定义 一个隐藏其父级的新成员变量id_。但是,id() 函数指的是在该上下文中定义的id_

如果你想覆盖行为,你应该覆盖id()函数:

class Derived: public Base {
    std::string id() const override { return "Derived"; }
};

【讨论】:

  • 没有其他更通用的方法吗?
  • 我推荐了一个。这个或那个更好取决于你的情况,所以权衡一下。
【解决方案3】:

为什么不简单地给Base 一个构造函数来设置id 并让Derived 用它的“覆盖”值调用这个构造函数? Boom:根据派生类型的“虚拟数据”。

话虽如此,虚拟方法可能更类似于封装,并为以后更精细的行为提供机会。

编辑:我将其作为评论发布,然后将其转换,此时理查德刚刚击败我发布答案。没有复制!

【讨论】:

    【解决方案4】:

    由于函数 id() 是虚函数,因此在这两种情况下对象指针都是派生类型。

    Base* b = new Derived(); // object type is Derived
    b->id();
    

    这里 b 是 Derived 类型的指针 但是“派生”类没有 id() 的实现,因为派生类对象的虚拟查找表中没有 id() 条目,因此它将调用基类函数 id()

    其他情况也类似

    Derived* d = new Derived(); //object type is Derived
    d->id();
    

    由于派生类中没有定义 id() 方法,它只会调用基类 id()。

    还有一个疑问是您如何在类中初始化变量“id_”?

    【讨论】:

    • 不,该指针非常类似于 Base 类型,而不是 Derived,尽管它可能在运行时通过 vtable 到达。您对初始化id_ 有什么疑问(问题?)?这是大括号或等号初始化,其中成员变量可以被赋予默认值,而不会出现在构造函数初始化列表中。
    • 你的意思是std::string id_ = "Base";在课堂上是正确的??是否可以在类中初始化数据成员(不是静态的)?我在 VS 2012 中尝试过,但它抛出错误“不允许数据成员初始化”
    • VS 经常落后于 C++ 标准的一些新特性,而且这个特性是在 C++11 之后不久出现的,其中添加了这个特性。是的,这是允许的。只是搜索它。 (顺便说一句,这使得类型非 POD)
    猜你喜欢
    • 2020-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-02
    • 1970-01-01
    相关资源
    最近更新 更多