安全覆盖C++虚拟函数

Safely override C++ virtual functions

我有一个带有虚函数的基类,我想在派生类中重写该函数。有没有办法让编译器检查我在派生类中声明的函数是否实际重写了基类中的函数?我想添加一些宏或确保不会意外地声明新函数的东西,而不是重写旧函数。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class parent {
public:
  virtual void handle_event(int something) const {
    // boring default code
  }
};

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

int main() {
  parent *p = new child();
  p->handle_event(1);
}

这里,调用parent::handle_event()而不是child::handle_event(),因为子方法缺少const声明,因此声明了一个新方法。这也可能是函数名的拼写错误,或者是参数类型的一些细微差别。如果基类的接口发生更改,并且某个派生类没有更新以反映更改,也很容易发生这种情况。

有没有办法避免这个问题,我能告诉编译器或者其他工具帮我检查一下吗?有什么有用的编译器标志(最好是g++)?你如何避免这些问题?


由于G++ 4.7,它确实理解了新的C++ 11 EDCOX1×0关键字:

1
2
3
4
5
6
class child : public parent {
    public:
      // force handle_event to override a existing function in parent
      // error out if the function with the correct signature does not exist
      void handle_event(int something) override;
};


C C的EDCOX1×0 }关键字不是C++的一部分。

在gcc中,-Woverloaded-virtual警告不要隐藏具有相同名称但签名完全不同的函数的基类虚拟函数,因为它不会重写它。但是,它不会保护您避免由于函数名本身拼写错误而未能重写函数。


据我所知,你不能把它抽象化吗?

1
2
3
4
5
6
class parent {
public:
  virtual void handle_event(int something) const = 0 {
    // boring default code
  }
};

我想我在www.parashift.com上看到过,实际上您可以实现一个抽象方法。这对我个人来说是有意义的,它所做的唯一一件事就是强制子类实现它,没有人说过它不被允许有一个实现本身。


在MSVC中,即使不是为clr编译,也可以使用clr override关键字。

在G++中,在所有情况下都没有直接的强制执行方法;其他人在如何使用-Woverloaded-virtual捕获签名差异方面给出了很好的答案。在将来的版本中,有人可能会使用EcOXX1×4的语法或使用C++0X语法的等价物来添加语法。


在msvc++中,可以使用关键字override

1
2
3
4
5
6
    class child : public parent {
    public:
      virtual void handle_event(int something) override {
        // new exciting code
      }
    };

override在msvc++中既适用于本机代码,也适用于clr代码。


使函数成为抽象的,这样派生类除了重写它之外别无选择。

@Ray您的代码无效。

1
2
3
4
5
6
class parent {
public:
  virtual void handle_event(int something) const = 0 {
    // boring default code
  }
};

抽象函数不能以内联方式定义实体。必须修改为

1
2
3
4
5
6
class parent {
public:
  virtual void handle_event(int something) const = 0;
};

void parent::handle_event( int something ) { /* do w/e you want here. */ }


我建议你稍微改变一下逻辑。它可能工作,也可能不工作,这取决于你需要完成什么。

handle_Event()仍然可以执行"无聊的默认代码",但它不是虚拟的,在您希望它执行"新的令人兴奋的代码"时,让基类调用一个抽象方法(即必须重写),该方法将由您的子类提供。

编辑:如果您稍后决定您的一些后代类不需要提供"新的令人兴奋的代码",那么您可以将抽象更改为虚拟的,并提供该"插入的"功能的空基类实现。


如果基类函数变为隐藏,编译器可能会发出警告。如果是,启用它。这将捕获参数列表中的常量冲突和差异。不幸的是,这不会发现拼写错误。

例如,这是微软Visual C++中的C4263警告。