【问题标题】:override c++ virtual method覆盖 C++ 虚方法
【发布时间】:2012-02-19 13:03:21
【问题描述】:

我有一个类模板,其中一些方法被定义为虚拟方法,以便我的类的用户能够在他的派生类中为它们提供实现。请注意,在我的模板类中,有一些使用虚拟方法的非虚拟方法(应该返回值的虚拟类在非虚拟类中被调用)。

你能给我一个正确代码的简单示例,其中父类的虚拟方法应该返回一个值(但它的实现是在子类中提供的)并且父类中的虚拟方法返回的值是在该类的其他方法中使用。因为我在某个地方(例如这里:Safely override C++ virtual functions)看到这可能会导致一些问题,并且用户定义的方法会注意覆盖父类的虚拟方法。

注意:我使用 g++ 编译器使用 Code::Blocks 进行编程。

编辑:按照这里的要求,我想要一个简单的例子:

template<typename T>
class parent {
public:
  // Public methods that user can call
  int getSomething(T t);
  void putSomething(T t, int x);

  // public method that user should implement in his code
  virtual float compute(T t) { }

  // protected or private methods and attributes used internally by putSomething ...
  float doComplexeThings(...); // this can call
};

compute() 方法应该由用户(子类)实现。但是,例如 putSomething() 和 doComplexeThings() 调用了这个方法 compute()。

【问题讨论】:

  • 对于您所描述的,可能不需要虚拟方法(静态多态性可能对您有用)-很难根据您的描述来判断。请发布您编写的示例代码,以便我们更好地了解您正在尝试做什么。
  • 链接问题中的示例是关于您认为是覆盖但实际上由于错误而声明重载的内容,例如一个 const 缺失,与返回值无关。你认为到底有什么危险?
  • 在经典模板代码中,您甚至不需要声明virtual float compute(T t)。如果您有实例的句柄,您只需通过t.compute() 在 putSomething() 和 doComplexeThings() 中使用它(调用它)。如果你的类 T 没有实现计算,编译器会出错。这样,parent 和 T 实际上甚至不必生活在同一个继承层次结构中:即 T 是 parent 关系的 child 是不需要的。这样做也可以让你有机会给父母一个更有意义的名字(因为 is-a 关系不是必需的)。
  • @kfmfe04 我不明白你为什么要谈论 T 和父母之间的关系。 T 不一定是类,它只是类模板(父)使用的类型,因为我想让它独立于操作数据的类型。忘了T,我的问题只是:在哪里以及如何定义doComplexeThings(...)putSomething(...)调用的方法float compute(...),而方法float compute(...)的代码应该由用户给出。

标签: c++ inheritance overriding virtual-functions


【解决方案1】:

您只需确保方法具有相同的签名(包括 const/mutable 修饰符和参数类型)。如果您未能在子类中覆盖该函数,则可以使用纯虚拟定义引发编译器错误。

class parent {
public:
  // pure virtual method must be provided in subclass
  virtual void handle_event(int something) = 0; 
};

class child : public parent {
public:
  virtual void handle_event(int something) {
    // new exciting code
  }
};

class incomplete_child : public parent {
public:
  virtual void handle_event(int something) const {
    // does not override the pure virtual method
  }
};

int main() {
  parent *p = new child();
  p->handle_event(1); // will call child::handle_event
  parent *p = new incomplete_child(); // will not compile because handle_event
                                      // was not correctly overridden
}

【讨论】:

  • 协变返回类型允许用于虚拟方法。
  • @als,只是“允许协变返回类型”的一个小细节,只有当函数返回引用或指针时才允许协变类型。我知道你知道这一点,但我要把这条评论留给其他人。
  • @mschneider 您的答案的问题是您强制更改语义只是为了避免潜在的问题。在原始情况下,该函数存在但可能被重载,在您提出的解决方案中,它必须被重载。这不会在重载现有函数时解决错误,而是强制派生类重载。
【解决方案2】:

如果您可以在编译器中使用 C++11 功能,则可以使用 override 特殊标识符对覆盖进行标记:

 float compute() override;

派生类中的上述行将导致编译器错误,因为该函数没有覆盖基类中的成员函数(签名不正确,缺少参数)。但请注意,这必须在每个派生类中完成,这不是您可以从基类强加的解决方案。

从基类中,您只能强制通过使函数成为纯虚拟函数来进行覆盖,但这会改变语义。它不会在覆盖时避免问题,而是在所有情况下强制覆盖。我会避免这种方法,如果您要遵循它并且对基本类型有一个合理的实现,请让函数 virtual 提供一个定义,以便您的派生类的实现可以只调用函数基本类型(即您强制实现,但在最简单的情况下,它只会将调用转发给父级)

【讨论】:

  • +1 获得了一个有趣的 C++11 功能 - 有助于确保您的方法确实覆盖了某些东西
【解决方案3】:

这个问题是在 2013 年提出的。它已经很老了,但我发现了一些答案中不存在的新问题。

我们需要了解三个概念是overloadoverwritehide

简短的回答,你想从基类重载继承函数。 但是,重载是为需要所有这些功能在同一规模下的功能添加多种行为的机制。但是虚函数显然在 Base 类中。

class A {
public:
  virtual void print() {
    cout << id_ << std::endl;
  }
private:
  string id_ = "A";
};

class B : A {
public:
  using A::print;
  void print(string id) {
    std::cout << id << std::endl;
  }
};

int main(int argc, char const *argv[]) {
  /* code */
  A a;
  a.print();
  B b;
  b.print();
  b.print("B");
  return 0;
}

在派生类中添加 using A::print; 即可!

虽然我不认为这是一个好主意,因为重载继承背后的哲学是不同的,但将它们嵌套在一起可能不是一个好主意。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-21
    • 2013-01-15
    • 2018-01-28
    • 1970-01-01
    • 2012-04-02
    • 1970-01-01
    • 2012-06-19
    • 1970-01-01
    相关资源
    最近更新 更多