【问题标题】:do I need a virtual function我需要一个虚函数吗
【发布时间】:2017-01-11 00:47:12
【问题描述】:

我有一个基类如下 -

Class Base {

    int a;
    int b;
    someInfo c;

  public:
    void setInfo(someInfo);
    someInfo getInfo();
};

someInfo 定义为 -

struct someInfo{
    std::string name;
    std::string school;
};

现在我有一个子类如下:

Class child: public Base{

    int e;
    int f;
    someMoreInfo g;

  public:
    void setInfo(someMoreInfo);
    someMoreInfo getInfo();
};

someMoreInfo 结构为 -

struct someMoreInfo{
    someInfo i;
    int j;
};

现在在实现子类的getInfo()setInfo 时,是否需要将基类的相应函数virtual 并在此处覆盖它们?因为每个人都这么说,而我不知道为什么要这样做以及为什么这已经不起作用了,因为两者都是不同的函数集,不同之处在于返回值或参数的值

对这位困惑的学生的任何帮助将不胜感激:)

【问题讨论】:

  • 如果你想要一个成员函数的运行时多态性,它需要是virtual
  • 和签名必须匹配:返回类型、参数类型及其顺序/限定符在母类和子类之间不能不同。
  • @CaptainObvlious 它发生在对象切片发生时,对吗?但是,如果我得到一个只有基类剩余属性的切片对象,我为什么还要那个:|。这是我没看懂的,老师也不会直接回答。
  • @hg_git “对象切片时会发生,对吧?” 不是。
  • 启动你的编译器,然后试一试。尝试Base b* = new child(). 然后尝试使用和不使用virtual 调用这两个函数。这应该花费您 5 分钟。

标签: c++ class oop c++11 inheritance


【解决方案1】:

看来您希望您的Derived 类支持以下接口:

someInfo si;
someMoreInfo smi;
Base* pB = new Derived;

// Setting info
pB->setInfo(si);       // Set the `Base::c` member of `*pB`
pB->setInfo(smi);      // Set the `Derived::g` member of `*pB`

// Getting info
someInfo pB_si = pB->getInfo();   // Get  `Base::c` from `*pB`
someMoreInfo pB_smi = pB->getInfo();   // Get  `Derived::g` from `*pB`

设置信息:重载

您实际上是正确,您不需要setInfo 改为virtual,因为Derived::setInfo 不会替换 strong> Base 方法; 两者都应该是可访问的。

不幸的是,正如我在评论中指出的那样,默认情况下,Base::setInfoDerived::setInfo隐藏不管的签名 (!)。是的,这是令人惊讶的行为; Scott Meyers 在Effective C++中指出,这“让每个 C++ 程序员第一次遇到它时都会感到惊讶”(第三版第 33 条)。

幸运的是,解决方案很简单。您需要using 关键字:

Class child: public Base{

  // ... private members...

  public:
    using Base::setInfo;
    void setInfo(someMoreInfo);

  // .. rest of class...
};

暴露原本隐藏的名称Base::setInfo,允许重载解析发生,就好像setInfo两个版本都在Derived中定义。

获取信息:依赖于返回的重载解析

C++ 不支持返回类型的重载。您可以轻松检查:

class Foo
{
  public:
    int foo() { return 3; }
    double foo() { return 83.4; }
};

Clang 给出以下错误(这比 GCC 错误更有帮助):

error: functions that differ only in their return type cannot be overloaded

现在,您可能会问:“为什么不呢?”事实上,我知道至少一种语言 Perl,它支持基于评估表达式的“上下文”的不同行为。

这实际上很令人困惑(我猜至少对于那些不是 Perl 粉丝的人来说)。一般来说,程序员期望根据调用函数的上下文来调用不同的函数。并且有很多上下文没有明确指出预期的返回类型:

std::cout << Foo().foo() << std::endl;  // AMBIGUOUS!

...所以根本没有解决方案:您不能仅在返回类型上区分两个函数。您必须为这两个函数提供不同的名称。

编辑:如果你真的想要,你可以返回-by-reference-argument:

void Base::getInfo(someInfo& si)
{
    si = c;
}
void Derived::getInfo(someMoreInfo& smi)
{
    smi = g;
}

在这里,将通过检查传递给getInfo 的参数类型来应用重载解析,并且不会有歧义。这是通过引用参数返回的好处之一,尽管这是否优于简单地更明确地命名这些是有争议的(我认为明确的名称会更可取,我认为大多数程序员都会同意)。

【讨论】:

    猜你喜欢
    • 2018-08-30
    • 2023-02-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-11
    相关资源
    最近更新 更多