从基类调用派生类函数

26
class base
{
  public:
  virtual void start();
  virtual void stop();

  void doSomething() { start(); .... stop(); }
}

class derived : public base
{
  public:
   void start();
   void stop();
}

但是,当我在派生类中调用 doSomething() 时,它使用的是自己定义的 Start()Stop() - 而不是派生类中的定义。

我不想在派生类中重新编写 doSomething(),因为它与基类中的相同。我做错了什么?

如果不太清楚,请见谅。
派生类中的 Start()Stop() 的行为有所不同(它是另一台机器) - 但我想使用原始的基类中的 doSomething() ,因为它没有改变。只需要使用新的派生类代码启动()和停止()即可。


3
当然,它使用自己的定义;这就是多态的工作方式。你的问题不是很清楚,为什么不想调用被重写的实现? - Ed S.
4
欢迎来到 Stack Overflow。建议您花些时间准备您的问题,以便准确地展示您想要讨论的问题。这意味着发布一个简短、可编译和可运行的示例代码。当准备好后,请从您的代码编辑器中复制并粘贴到浏览器中;不要尝试在浏览器中编写新代码,因为您可能会犯错误,而读者可能无法确定这些错误是否真正是您测试用例的一部分。 - Rob Kennedy
1
@EdSwangren:我认为他想要扩展行为。在这种情况下,在派生类的start()和stop()实现中,分别调用base::start();和base::stop();并处理派生类的具体细节。或者将start和stop设置为纯虚函数,并封装受保护的公共行为函数。 - the_drow
1
@The_drow,如果问题是使用了错误的函数实现,那显然不是编译器错误。这在编译时是无法知道的,因此实际代码必须是可运行的。但总的来说,如果问题是无法编译,那么发布可运行代码的要求就被免除了。 - Rob Kennedy
2
@cpp: 你确定这句话表达了你想要表达的意思吗:"但是当我在__派生类__中调用doSomething()时,它使用的是自己定义的Start()Stop() - 而不是__派生类的__" [强调是我的]?因为如果是这样的话,我甚至不理解你的问题是什么。 - sbi
显示剩余3条评论
2个回答

42
你发布的代码应该可以按照你的意愿工作。在 derived 实例上调用 doSomething 会调用在 derived 中定义的重写的 startstop 函数。
但有一个例外情况。如果你在 base 的构造函数或析构函数中(直接或间接地)调用了 doSomething,那么被调用的 startstop 版本将是在 base 中定义的版本。因为在这些情况下,你实际上还没有一个有效的 derived 实例。它要么未完全构建,要么部分销毁,所以语言会阻止你调用使用部分对象的方法。
如果你不是从 base 构造函数或析构函数中调用它,则问题比此处显示的更复杂。

16

更新
根据您在下面的评论中提到,您正在尝试调用Derived类版本的start()和stop()函数,我的更新答案如下:

您定义Base和Derived的方式没有问题。您可能遇到了所谓的“代码切片”,在这种情况下,您正在对一个声明类型为“Base”的对象调用“doSomething()”,而不是“Base*”或“Base&”,这将导致对象转换为类型Base。

错误示例:

 Derived derived;
 Base base = derived;
 base.doSomething();  // This is Base's version of doSomething()

很好的例子:

 Base* base = new Derived;  // NOTE: declared type is "Base*"
 base->doSomething();  // This will call Derived version
 delete base;

顺便提一下:你应该使用scoped_ptr、shared_ptr、unique_ptr或其他智能指针类,而不是直接使用指针(就像我的示例中一样);然而,为了不影响问题的阐述,我选择在此示例中使用原始指针。有关"切片"的更多信息,请参见:

原始解决方案
你可以像这样做:

class Base {
    public:
        Base() {}
        virtual ~Base() {}

        virtual void start() {
           startInternal();
        }

        virtual void stop() {
            stopInternal();
        }

        void doSomething() {
            startInternal();
            // ...
            stopInternal();
        }
    private:
        void startInternal() {
          // ...
        } 
        void stopInternal() {
          // ...
        }
};

class Derived : public Base {
    public:
        Derived() {}
        virtual ~Derived() {}
        virtual void start() {
            // ...
        }
        virtual void stop() {
            // ...
        }
};
如果你这样做,那么doSomething()将使用内部版本的start/stop而不被覆盖。当构造函数/析构函数需要与虚拟方法共享逻辑时,您会经常遇到这种模式。
另外,与手头问题无关的是,不要忘记在创建具有虚拟方法的类时始终创建虚拟析构函数。

@Marton,无论Derived类中的start()和stop()是否声明为虚函数,它们都将是虚函数(在基类中声明为虚函数且签名相同的方法在派生类中自动成为虚函数)。就风格而言,我更喜欢重新声明它们为虚函数,以便清楚地表明它们是虚函数,而不必查阅基类的文档。 - Michael Aaron Safyan
注意:我上面的评论是针对Marton已删除的评论的回复,他问我是否也打算在派生类中将start()和stop()设置为虚函数。 - Michael Aaron Safyan
那正好相反了,我需要基类的doSomething()在调用派生类的doSomething()时能够同时执行派生类的start/stop方法。 - cpp help
@RageD,这完全是一种风格问题。我现在习惯使用Google C++风格(参见:http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Inheritance),其中指出:“当重新定义继承的虚函数时,在派生类的声明中显式声明它为虚函数。原因是:如果省略了virtual关键字,读者必须检查该类的所有祖先类以确定该函数是否为虚函数。”我认为给出的理由很有道理。 - Michael Aaron Safyan
@cpp 帮助,抱歉,您的问题没有表述清楚。稍后会进行更新。 - Michael Aaron Safyan
看了更新后的答案,我不认为这会起作用。Derived 没有 doSomething() 方法。 - iheanyi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接