2016-04-04 31 views
1

不知道该如何解释好,所以我将只提供一个代码示例,演示我的问题:调用指定的方法

class Base { 
public: 
    Base() = default; 
    ~Base() = default; 

    virtual void stuff(std::shared_ptr<Base> b) = 0; 
}; 

class DerivedA : public Base { 
public: 
    DerivedA() = default; 
    ~DerivedA() = default; 

    void stuff(std::shared_ptr<Base> b) { 
    std::cout << "stuff Base" 
       << "\n"; 
    } 
}; 

class DerivedB : public Base { 
public: 
    DerivedB() = default; 
    ~DerivedB() = default; 

    void stuff(std::shared_ptr<Base> b) { 
    std::cout << "stuff Base" 
       << "\n"; 
    } 
    void stuff(std::shared_ptr<DerivedA> b) { 
    std::cout << "stuff Derived" 
       << "\n"; 
    } 
}; 

int main(int argc, char *argv[]) { 
    std::shared_ptr<Base> b1(new DerivedA()); 
    std::shared_ptr<Base> b2(new DerivedB()); 

    b1->stuff(b2); 
    b2->stuff(b1); 
    return 0; 
} 

输出将是:

stuff Base 
stuff Base 

现在,我想不可能调用派生方法,因为它不存在于基类中。

我的问题是:有没有办法使用基类来调用stuff(std::shared_ptr<DerivedA> b)

[编辑]

我已经想到了访问者模式(应该说,这和更具体)。

我的类代表实体,我必须检查它们之间的冲突。然而,一个& B之间的碰撞将有较B C.

之间&

我同意,这将工作不同的效果,但它意味着我将要定义吨的方法。

有没有更好的方法来做到这一点?

在此先感谢。

+0

这看起来像访客模式的工作! https://en.wikipedia.org/wiki/Visitor_pattern – Louen

+3

看起来像是在方法(或任何函数)重载和(虚拟)方法覆盖之间混淆了。 – hyde

+0

我编辑了我的问题以回答@Louen – Luc

回答

1

你所寻找的是通常被称为多重调度,或多重方法。在C++中没有直接的语言支持,但您可以自己明确地实现它。基本上,你有分派到另一个虚拟函数的具体对象的一个​​虚函数:

struct DerivedA; 
struct DerivedB; 

struct Base { 
    virtual ~Base() = default; 
    virtual void stuff(shared_ptr<Base>) = 0; 

    virtual void dispatch_stuff(Base&) = 0; 
    virtual void dispatch_stuff(DerivedA& p) { return dispatch_stuff(static_cast<Base&>(p)); } 
    virtual void dispatch_stuff(DerivedB& p) { return dispatch_stuff(static_cast<Base&>(p)); } 

struct DerivedA : Base { 
    void stuff(shared_ptr<Base> rhs) override { 
     rhs->dispatch_stuff(*this); 
    } 

    void dispatch_stuff(Base&) { /* default */ } 
    void dispatch_stuff(DerivedA&) { /* specific A-A stuff */ } 
}; 

这样:

b1->stuff(b2); // calls b2->dispatch_stuff(DerivedA&) 
b2->stuff(b1); // calls b1->dispatch_stuff(DerivedB&) 
1

我的问题是:有没有办法使用基类调用 stuff(std::shared_ptr<DerivedA> b)

不,因为Base类接口没有实现你想调用的方法。尽管Base类指针指向的是一个DerivedB对象,并且通过poilformism可以解决指针指向的对象类型(即DerivedB)的方法,但您只能调用Base类中定义的方法。因此,您不能使用Base指针指向stuff(std::shared_ptr<DerivedA> b)指针指向DerivedB对象。

例如:

std::shared_ptr<Base> b1(new DerivedA()); 
    std::shared_ptr<Base> b2(new DerivedB()); 
    std::shared_ptr<DerivedA> a1(new DerivedA()); 
    std::shared_ptr<DerivedB> bb1(new DerivedB()); 

    b1->stuff(b2); 
    b2->stuff(b1); 
    b2->stuff(a1); // b2 is the only class that implement stuff(std::shared_ptr<DerivedA> b) 
    bb1->stuff(a1) 

输出:

stuff Base 
stuff Base 
stuff Base 
stuff Derived 
stuff Base 
1

你正在试图解决的问题称为double dispatch问题,这意味着你正试图根据两个对象的具体类型调用一个行为。在谷歌或这里查找这个术语可能会产生一些有趣的结果。

首先,这种或那种方式,你将不得不编写许多函数,因为如果你有N个不同的类型,N个可能的配对就是N 。 (如果订单无关紧要,N N/2,在碰撞情况下可能是这种情况)。

visitor pattern是双调度问题的标准解决方案之一。

还有其他的,取决于你的重要。例如,如果子类型的数量在编译时是有限的并且已知的,那么您可以为每个类型指定一个索引,并且可以调用一个二维数组函数指针(不是非常优雅,也不是非常面向对象的但在性能方面效率很高)。

如果担心函数的数量可能会导致代码重复,则可以将函数或类中的代码(类似于CollisionPairBehavior)分解。