覆盖合格的虚拟方法

时间:2011-03-30 02:53:16

标签: c++ virtual-functions ambiguity

我有多个父母的C ++课程;每个父级定义一个具有通用名称但功能不同的函数:

class BaseA
{
    virtual void myFunc();  // does some task
};
class BaseB
{
    virtual void myFunc();  // does some other task
};
class Derived : public BaseA, public BaseB;

如果就是这样,我就没有问题了 - 我可以通过使用语句解决它的模糊性,我可以选择使用基类名称和范围解析运算符调用哪一个。

不幸的是,派生类需要覆盖它们:

class Derived : public BaseA, public BaseB
{
    virtual void BaseA::myFunc(); // Derived needs to change the way both tasks are done
    virtual void BaseB::myFunc();
}

这不起作用,不是因为它引入了新的歧义(虽然它可能),但是因为

  

"错误C3240:' myFunc' :必须是' BaseA'"

的非重载抽象成员函数      

"错误C2838:成员声明中的非法限定名称"

在不同的情况下,我可能只是重命名方法,或者像编译器建议的那样使它们成为纯虚拟方法。但是,类层次结构和许多外部问题使得第一个选项极其困难,第二个选项不可能。

有人有建议吗?为什么限定符仅允许纯虚方法?有没有办法同时覆盖虚拟方法并解决歧义?

4 个答案:

答案 0 :(得分:3)

Microsoft allows that syntax(从Visual C ++ 2005开始提供)。 They also introduced a new, more powerful syntax for managed code only

C ++ 0x中都没有包含任何一个。

请参阅http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2108.html


我认为这是一种解决方法:

class BaseA
{
protected:
    virtual void myFunc();  // does some task
};
class BaseB
{
protected:
    virtual void myFunc();  // does some other task
};
class ShimA : virtual BaseA
{
    virtual void myFunc() { myFuncA(); }
protected:
    virtual void myFuncA() { BaseA::myFunc(); }
};
class ShimB : virtual BaseB
{
    virtual void myFunc() { myFuncB(); }
protected:
    virtual void myFuncB() { BaseB::myFunc(); }
};
class Derived : public virtual BaseA, public virtual BaseB, protected ShimA, protected ShimB
{
     virtual void myFuncA() {}
     virtual void myFuncB() {}
};

答案 1 :(得分:1)

这是multiple inheritance的重大问题之一。如果要继承同一个名称的多个函数以确定应该覆盖哪个函数,则始终存在问题,请参阅The Diamond Problem。您不能覆盖它们,因为函数语法(函数名称和运算符)必须是唯一的。

答案 2 :(得分:1)

您可以使用合成对象。

class Derived : public BaseB {        
    struct temp : public BaseA {
        virtual void myFunc() {
             d->BaseAMyFunc();
        }
        Derived* d;
    };
    temp t;
public:
    Derived() {
        t.d = this;
    }
    operator BaseA&() { return temp; }
    operator const BaseA&() const { return temp; }
    void myFunc(); // refers to BaseB::myFunc()
    void BaseAMyFunc(); // called when BaseA::myFunc() is called.
}

这不是特别整洁,但有些限制,但在某些情况下确实有效。

答案 3 :(得分:0)

我意识到这个问题已经过时了,但它有很多观点,如果你是接口的作者,还有一种解决这个问题的简洁方法。

许多人认为虚拟接口应具有公共非虚拟功能,这些功能在内部遵循私有虚拟功能(我同意这些功能)。这有一些优点,其中之一是非虚拟名称可以具有不同的含义,因为它们更强烈地绑定到接口:

struct BaseA
{
  virtual ~BaseA() = default;

  void func() 
  {
    handle_a_func();
  }

private:
  virtual void handle_a_func() = 0;
};

struct BaseB 
{
  virtual ~BaseB() = default;

  int func() const  // note the different signature and return type
  {
    handle_b_func();
  }

private:
  virtual int handle_b_func() const = 0;
};

// now provide an implementation

struct ABImpl : public BaseA, public BaseB
{
  ABImpl() {}

private:
  void handle_a_func() override final 
  {
    // alter some state
  }

  int handle_b_func() const override final
  {
    return _x;
  }

  int _x = 0;
};        

// now use the class
auto ab = make_shared<ABImpl>();

auto a = static_pointer_cast<BaseA>(ab);
auto b = static_pointer_cast<const BaseB>(ab);

a->func();  // uses A's mutable interface
auto x = b->func();  // uses B's const interface

这种方法的另一个优点是基类实现可以代表派生函数管理互斥体和标记等内容,例如:

struct base {

  void do() {
    std::unique_lock<std::mutex> l(_m);
    handle_do();
  }

private:
  virtual void handle_do() = 0;

  std::mutex _m;
};

另一个优点是一些有用的自由函数运算符只需要为整个类层次结构定义一次:

struct base
{
  void write_to(std::ostream& os) const {
    // lock a mutex here?
    handle_write(os);
  private:
    virtual void handle_write(std::ostream& os) const {
      os << "write base class info here\n";
    }
};

inline std::ostream& operator<<(std::ostream& os, const base& b) {
  b.write_to(os);
  return os;
}

struct derived : base {
private:
  virtual void handle_write(std::ostream& os) const override {
    base::handle_write(os);  // handle base class implementation
    std::cout << "write relevant data here. We could still be overridden safely\n";
  }
};

// write any derived class like this:
auto b = unique_ptr<base> { new derived() };
cout << "here is a kind-of b:\n" << *b << endl;