覆盖合格的虚拟方法

覆盖合格的虚拟方法

问题描述:

我有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:成员声明中的非法限定名称“

在不同的情况下,我可能会重命名这些方法,或者使它们像编译器所建议的那样是纯虚拟的。但是,班级层次结构和一些外部问题使得第一个选项难度很大,第二个不可能。

有没有人有建议?为什么限定符只允许纯虚拟方法?有什么方法可以同时覆盖虚拟方法和解决歧义?

+0

您也可以重写单独的类中的行为:'DerivedA:public BaseA','DerivedB:public BaseB',然后'派生:public DerivedA,public DerivedB'。这并不解决歧义问题,但 – Cameron 2011-03-30 03:23:14

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() {} 
}; 
+0

感谢有关MS语法的信息;不幸的是它仅适用于抽象基类(请注意他们的例子中的“__interface”关键字)。 – John 2011-03-30 03:35:41

+0

@John:托管变体适用于从基类继承的虚拟方法。无论如何,看看我的编辑,我认为这应该可以解决你的问题。 – 2011-03-30 03:36:43

+0

再次感谢。你的建议可行,但我的情况的细节阻止我使用它。基本上,我正在为一些反向工程代码编写类定义,而且我实际上无法更改继承结构或虚函数表的组成(显然,或者使用托管的C++) – John 2011-03-30 03:40:52

这是multiple inheritance的一个大问题。如果您有多个相同名称的函数被继承以确定哪一个函数应该被覆盖,总会遇到问题,请参阅The Diamond Problem。由于函数语法(函数名称和运算符)必须是唯一的,因此无法重写它们。

您可以使用组合对象。

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. 
} 

这不是特别整洁,它有点受限,但它在某些情况下有效。

我意识到这个问题是旧的,但它有很多的意见,并没有解决一个干净的方式这如果你是接口的作者。许多人认为虚拟接口应该具有公共的非虚拟功能,这些虚拟功能在内部遵循私有虚拟功能(我同意他们)。这有几个好处,他们是说非虚拟名称可以有,因为他们更强烈地绑定到该接口不同的含义之一:

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;