【问题标题】:Calling derived class function from base class从基类调用派生类函数
【发布时间】:2011-06-19 15:30:09
【问题描述】:
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() 因为它没有改变。它只需要使用新的派生类代码 start() 和 stop()。

【问题讨论】:

  • 当然使用自己的定义;这就是工作中的多态性。你的问题不是很清楚,为什么不想调用覆盖的实现?
  • 欢迎来到 Stack Overflow。请考虑花更多时间准备您的问题,以便准确地展示您希望讨论的问题。这意味着发布一个简短的、可编译的和可运行的示例。准备好后,从代码编辑器复制并粘贴到浏览器中;不要尝试在浏览器中编写新代码,因为你会犯错误,而且读者也不清楚这些错误是否真的是你的测试用例的一部分。
  • @EdSwangren:我相信他想扩展行为。在这种情况下,在派生类调用 base::start() 中的 start() 和 stop() 的实现中;和 base::stop();分别处理派生的细节。要么,要么使启动和停止纯虚拟,并将常见行为封装在受保护的函数中。
  • @The_drow,如果问题是它使用了错误的函数实现,这显然不是编译器错误。这在编译时不可能知道,因此实际代码必须是可运行的。但总的来说,你是对的——如果问题是它无法编译,那么发布可运行代码的要求就被放弃了。
  • @cpp:你确定这句话说的是你想要它说的话:“但是当我在派生类中调用doSomething()时,它使用的是它自己的定义Start()Stop() - 不是派生的。” [强调我的]?因为如果是这样,我什至不明白你的问题是什么。

标签: c++ inheritance virtual


【解决方案1】:

您发布的代码应该按照您想要的方式工作。在derived 的实例上调用doSomething 将调用derived 中定义的重写startstop 函数。

不过,有一个例外。如果您在base 的构造函数或析构函数中调用doSomething(无论是直接还是间接),那么被调用的startstop 的版本将是base 中定义的版本。那是因为在这种情况下,您实际上还没有有效的 derived 实例。它要么未完全构造,要么部分破坏,因此该语言会阻止您调用将使用部分对象的方法。

如果您不是从 base 构造函数或析构函数调用它,那么问题比这里显示的要多。

【讨论】:

    【解决方案2】:

    更新
    根据您在下面的评论,您试图让 doSomething() 调用派生类的 start() 和 stop() 版本,我对您的问题的更新答案如下:

    您定义 Base 和 Derived 的方式没有任何问题。您可能正在经历所谓的“代码切片”,您在声明类型为“Base”而不是“Base*”或“Base&”的对象上调用“doSomething()”,这将导致对象被转换为 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() 将使用未覆盖的内部版本的启动/停止。当构造函数/析构函数需要与虚方法共享逻辑时,您会发现这种模式很多。

    此外,与手头的问题无关,不要忘记,无论何时创建具有虚拟方法的类时,都应始终创建虚拟析构函数。

    【讨论】:

    • @Marton,无论 start() 和 stop() 是否在 Derived 中声明为虚拟,它们都将是虚拟的(在具有相同签名的基类中声明为虚拟的方法在派生类)。就风格而言,我更喜欢将它们重新声明为虚拟,以明确它们是虚拟的,而无需查找基类的文档。
    • 注意:我上面的评论是对 Marton 删除评论的回应,该评论询问我是否打算在派生类中也将 start() 和 stop() 设为虚拟。
    • 这与我所需要的相反 - 我需要基类 doSomething() 在派生类中调用 doSomething() 时获取派生的开始/停止。
    • @RageD,这完全是风格问题。我现在习惯于使用 Google C++ 风格(参见:google-styleguide.googlecode.com/svn/trunk/…),其中声明“重新定义继承的虚函数时,请在派生类的声明中明确声明它为 virtual。理由:如果省略 virtual,读者必须检查相关类的所有祖先以确定该函数是否是虚拟的。”我认为给出的理由是有道理的。
    • @cpp 帮助,对不起,你的问题没有说清楚。很快就会更新。
    猜你喜欢
    • 1970-01-01
    • 2019-07-16
    • 1970-01-01
    • 1970-01-01
    • 2014-01-01
    • 2011-05-03
    • 1970-01-01
    • 2011-09-27
    • 1970-01-01
    相关资源
    最近更新 更多