2016-11-25 76 views
1

让一个类层次结构:调用从基类指针向右free函数/参考

class Base { virtual ~Base() throw(); }; 

class DerivedA : public Base { }; 

class DerivedB : public Base { }; 

我想有一些具体到每一个派生类的代码。但是,该代码也是特定于使用此类层次结构的应用程序的,我不想将这种派生类专用代码嵌入到这些派生类中。为了避免这样做,我想过写免费功能:

void DerivedASpecificWork(DerivedA da); 

void DerivedBSpecificWork(DerivedB db); 

然而,通过参考/指向一个基本给定一个派生类的实例时,我没有进入实际类型的实例,因此不能调用适当的Derived * SpecificWork()函数。

我想知道是否有nome的设计模式,可以让我在不知道实例的实际类型的情况下调用派生类特定的函数,即具有与虚函数提供的相同机制,但而不需要这些虚拟函数,这就需要我将特定于应用程序的代码嵌入到该类层次结构中。

实际上,为什么我想这样做是为了提供有关由Lua脚本调用的本机实现的函数内发生的异常的信息。每个异常都带有自己的一组信息,我想在脚本中表示错误的方式取决于异常的类型。我可以在基类中创建一个由派生类实现的纯虚方法,但这需要我将Lua相关的代码嵌入到我的异常层次结构中,这是我不想做的,因为Lua是特定于使用该异常层次的应用程序。

另外我不能使用C++ 11。

谢谢。

+1

你的父母析构函数是私有的虽然.. –

+1

这里的典型模式是使用* actual *虚函数。其他一切都是[反模式](https://en.wikipedia.org/wiki/Anti-pattern)。 –

+0

@Someprogrammerdude是的,这似乎是合乎逻辑的,但我不想污染用作特定于应用程序的代码的库,这就是为什么我问是否有解决方法。 – Virus721

回答

2

可能多桥模式可以帮助你。

当您想要避免抽象与其实现之间的永久绑定时,可以使用此模式。

(我没有看到你关于使用C++ 11的限制评论,但你可以删除std::unique_ptrstd::moveoverride关键字)

class AppSpecificImp 
{ 
public: 
    virtual void DoWork() = 0; 
}; 

class Base 
{ 
public: 
    virtual ~Base() throw(); 

    virtual DoWork() = 0; 
}; 

class DerivedA : public Base 
{ 
public: 
    DerivedA(std::unique_ptr<AppSpecificImp> appImp) 
     : imp(std::move(appImp)) 
    { 
    } 

    void DoWork() override 
    { 
     // DerivedA specific code 

     imp->DoWork(); 
    } 

private: 
    std::unique_ptr<AppSpecificImp> imp; 
}; 

class DerivedB : public Base 
{ 
public: 
    DerivedB(std::unique_ptr<AppSpecificImp> appImp) 
     : imp(std::move(appImp)) 
    { 
    } 

    void DoWork() override 
    { 
     // DerivedB specific code 

     imp->DoWork(); 
    } 

private: 
    std::unique_ptr<AppSpecificImp> imp; 
}; 

编辑显示访问者模式用法:

随着访客模式,你可以做你想要的,但更多的努力。

class Visitor 
{ 
public: 
    virtual void VisitDerivedA(DerivedA* object) = 0; 

    virtual void VisitDerivedB(DerivedB* object) = 0; 
}; 

class Base 
{ 
public: 
    virtual void Visit(Visitor* visitor) = 0; 
}; 

class DerivedA : public Base 
{ 
public: 
    virtual void Visit(Visitor* visitor) 
    { 
     visitor->VisitDerivedA(this); 
    } 
}; 

class DerivedB : public Base 
{ 
public: 
    virtual void Visit(Visitor* visitor) 
    { 
     visitor->VisitDerivedB(this); 
    } 
}; 

class AppSpecificVisitor : public Visitor 
{ 
public: 
    void VisitDerivedA(DerivedA* object) 
    { 
     // Do any work related to DerivedA class 
    } 

    void VisitDerivedB(DerivedB* object) 
    { 
     // Do any work related to DerivedB class 
    } 
} 


int main() 
{ 
    AppSpecificVisitor myVisitor; 

    Base* myBase = // any class in your hierarchy 

    myBase->Visit(&myVisitor); 
} 

正如我在与访问者模式评论说,你可以在功能上增加新的不改变主层次结构(基极 - >派生类型)。您只需定义一个新的访问者实现并为主层次结构中的每个类编写逻辑。在您的示例中,您可以将应用程序特定的逻辑打包到一个对象中,并在派生对象中引用这个更简单的方法。

+0

感谢您的回答。这看起来有点像访问者模式,除了存储特定于应用程序的对象,对吗? – Virus721

+0

@ Virus721访客模式的一个用例是Double-Dispatching,这意味着将要执行的操作取决于调用者和被调用者(操作在其上执行的对象)。 Visitor还定义了两个类层次结构:一个是访问者,另一个是实现和其他接受访问者对象的层次结构。通过这种方式,您可以添加新功能,编写新的访问者实现,而无需更改接受访问者的其他层次结构在你的情况下,你不需要双重调度,你只需要一种方法来向Bridge模式中的库类层次结构注入逻辑。 – MRB

+0

@ Virus721看我的编辑 – MRB

1

为什么不使用一组新层次结构来实现特定于应用程序?

class AppBase 
{ 
public: 
    virtual ~AppBase() throw(); 
    virtual void work_with_app() = 0; 
}; 

class Base 
{ 
public: 
    Base(AppBase& app) : m_app(app) {} 
    virtual ~Base() throw(); 
protected: 
    AppBase& m_app; 
}; 

class DerivedA : public Base { DerivedA(AppBase& app) : Base(app) {} }; 

class DerivedB : public Base { DerivedA(AppBase& app) : Base(app) {} }; 

// Application specific implementation : 
class AppLuaSpecific : public AppBase 
{ 
public: 
    void work_with_app() { /* Lua app specific */ } 
}; 

这样,你的第一个层次:BaseDerivedADerivedB能生活在不知道有关AppLuaSpecific实现的应用程序特定代码什么。

+0

感谢您的回答。这也看起来类似于访客模式,对吧? – Virus721

+0

@ Virus721:不,这不是访问者模式。这是一个简化的桥接模式:1个DerivedA/B实例可以与AppSpecific的1个单一实例(在DerivedA/B对象构造中定义)交互。访问者模式将是必需的如果你需要多个派遣,这意味着一个DerivedA/B类型的对象能够在单个应用程序中与来自AppBase的几个应用程序特定类进行交互(根据我的理解,这是毫无意义的您的需求)。因此,我所提出的Bridge模式解决方案应该适合您的需求(正如MohammadRB所建议的那样)。 – shrike

+0

那么,在我的应用程序中,我也有多种方式来表示Lua脚本中的(派生)异常。我可以更容易地将其表示为包含所有信息的字符串,或者作为多个Lua值。这意味着被执行的东西取决于两件事:异常的实际类型和我想要使用的方式。所以我想如果我理解正确的话,访问者会更好。 – Virus721

1

您可以实现自己的应用程序特定的调度如下(check it live on Coliru):

#include <iostream> 
#include <typeinfo> 


struct Base { virtual ~Base() {} }; 

struct DerivedA : public Base { }; 

struct DerivedB : public Base { }; 

namespace AppSpecific 
{ 

template<class F> 
void dispatch(const Base& b) 
{ 
    const std::type_info& t = typeid(b); 
    if (t == typeid(DerivedA)) 
     F::doit(static_cast<const DerivedA&>(b)); 
    else if (t == typeid(DerivedB)) 
     F::doit(static_cast<const DerivedB&>(b)); 
} 

struct Foo 
{ 
    static void doit(const DerivedA& da) { std::cout << "Foo(DerivedA)\n"; } 
    static void doit(const DerivedB& db) { std::cout << "Foo(DerivedB)\n"; } 
}; 

struct Bar 
{ 
    static void doit(const DerivedA& da) { std::cout << "Bar(DerivedA)\n"; } 
    static void doit(const DerivedB& db) { std::cout << "Bar(DerivedB)\n"; } 
}; 

} // namespace AppSpecific 

int main() 
{ 
    DerivedA da; 
    DerivedB db; 

    Base& b1 = da; 
    Base& b2 = db; 
    AppSpecific::dispatch<AppSpecific::Foo>(b1); 
    AppSpecific::dispatch<AppSpecific::Foo>(b2); 
    AppSpecific::dispatch<AppSpecific::Bar>(b1); 
    AppSpecific::dispatch<AppSpecific::Bar>(b2); 
}