基类中抽象方法的包装器实现

Posted

技术标签:

【中文标题】基类中抽象方法的包装器实现【英文标题】:Wrapper implementation for an abstract method in the base class 【发布时间】:2015-07-17 02:50:30 【问题描述】:

我有一个继承了许多子类的基类。我需要为一个抽象方法定义一个新的签名,它主要是一个包装器。我试过这个

class B 
 public:
  virtual void f() = 0;
  void f(string msg)  /* print msg and call f() */ 
;

class D : public B 
 public:
  void f() override  /* implementatation */
;

int main() 
  D d;
  d.f("msg");

但它没有编译并给出以下错误

error: no matching function for call to 'D::f(string)

我的问题是:

    为什么不能解析D::f(string)? 为什么以下任何一项都可以解决问题? 将f(string) 重命名为f2(string)(丑陋) 定义D::f(string x) B::f(x)(丑陋,因为它必须在每个子类中定义) 删除抽象方法B::f()(不可接受)

有更好的解决方案吗?

【问题讨论】:

派生的f 覆盖隐藏了基础f(std::string)。尝试将using B::f; 放在派生类中,以将它们从B 引入 @Alejandro 答案属于答案,而不是 cmets。 鉴于一个f 调用另一个f,他们有微妙的不同角色。因此,为了清楚起见(除了解决问题),重命名它们以区分更高级别的发起者和实现可能是值得的。 【参考方案1】:

问题是你隐藏 B::f(std::string)这里:

class D : public B 
public:
  void f() override;
;

当您调用D.f("msg") 时,名称查找将在D 中找到f() 并停止查找。我们首先找到候选函数然后执行重载决议。因为那个恰好没有参数,所以你会得到编译错误。

如果你想使用f的另一个重载,你也需要把它带入D,像这样:

class D : public B 
public:
  using B::f;
  void f() override;
;

现在我们有了D::f()(被覆盖的虚函数)和D::f(std::string )(我们从B引入)。

另一种简单的解决方案是简单地重命名一个或另一个函数,这样一开始就没有这个问题。

【讨论】:

反正比我要写的要好得多!很好的答案 编译器一旦找到不匹配的候选者就停止寻找匹配函数的任何原因?对我来说,在报告错误之前继续搜索所有候选人是有意义的。我这里有什么遗漏吗? @nmoses 这里的顺序是 (1) 找到候选函数 (2) 修剪到可行的候选函数 (3) 选择最佳可行的候选函数。步骤 (1) 只是名称查找。一旦我们找到D::f(),我们就完成了这一步。然后步骤 (2) 将其删除,因为它不可行。【参考方案2】:

您使用的是NVI 惯用语吗?如:

class B 
 public:
  virtual void f() = 0;
  void f(string msg)  
    /* print msg and call f() */
    std::cout << msg;
    f();
  
  virtual ~B() 
;

那你应该尽量尝试从基类调用非虚成员函数(通过引用或指针),如:

D d;
B& b = d;
b.f("msg");

【讨论】:

以上是关于基类中抽象方法的包装器实现的主要内容,如果未能解决你的问题,请参考以下文章

抽象派生类中的抽象方法覆盖/实现抽象基类的抽象或具体方法。如何以及为啥?

C# 中基类,虚类,抽象类,密封类,接口的区别

虚方法抽象方法接口方法

不是抽象类的基类不是好基类

抽象类与多态

抽象类,虚方法,接口