从基础 pointer/reference 调用正确的自由函数
Calling the right free function from a base pointer/reference
让 class 层次结构:
class Base { virtual ~Base() throw(); };
class DerivedA : public Base { };
class DerivedB : public Base { };
我想要一些特定于每个派生的 classes 的代码。然而,该代码也特定于使用此 class 层次结构的应用程序,我不想将此派生的 class 特定代码嵌入到这些派生的 classes 中。为了避免这样做,我考虑编写免费函数:
void DerivedASpecificWork( DerivedA da );
void DerivedBSpecificWork( DerivedB db );
但是,当通过 reference/pointer 将派生 class 的实例提供给 Base 时,我无权访问实例的实际类型,因此无法调用正确的 Derived *SpecificWork() 函数。
我想知道是否有某种设计模式允许我在不知道实例的实际类型的情况下调用派生的-class-特定函数,即具有与虚函数提供,但没有这些虚函数需要我将特定于应用程序的代码嵌入到 class 层次结构中。
实际上,我为什么要这样做是为了提供有关在 Lua 脚本调用的本机实现的函数中发生的异常的信息。每个异常都带有自己的一组信息,我想在脚本中表示错误的方式取决于异常的类型。我可以在基础 class 中创建一个由派生 classes 实现的纯虚方法,但这需要我将 Lua 相关代码嵌入到我的异常层次结构中,我不想这样做,因为 Lua 特定于使用该异常层次结构的应用程序之一。
我也不能使用 C++11。
谢谢。
为什么不为特定于应用程序的实现使用一组新的层次结构?
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 */ }
};
这样,您的第一个层次结构:Base
、DerivedA
、DerivedB
可以在不了解 AppLuaSpecific
中实现的应用程序特定代码的情况下存在。
您可以按如下方式实施您自己的特定于应用程序的调度 (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);
}
也许 Brigde 模式可以帮助您。
当您想避免抽象与其实现之间的永久绑定时,可以使用此模式。
(我没有看到你关于使用 c++11 的限制的评论,但你可以删除 std::unique_ptr
、std::move
和 override
关键字)
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);
}
正如我在访问者模式的评论中所说,您可以在不更改主层次结构(基础->派生类型)的情况下添加新功能。您只需定义一个新的访问者实现并为主层次结构中的每个 class 编写逻辑。在您的示例中,您可以将特定于应用程序的逻辑打包到一个对象中,并在您的派生对象中引用该逻辑,这是一种更简单的方法。
让 class 层次结构:
class Base { virtual ~Base() throw(); };
class DerivedA : public Base { };
class DerivedB : public Base { };
我想要一些特定于每个派生的 classes 的代码。然而,该代码也特定于使用此 class 层次结构的应用程序,我不想将此派生的 class 特定代码嵌入到这些派生的 classes 中。为了避免这样做,我考虑编写免费函数:
void DerivedASpecificWork( DerivedA da );
void DerivedBSpecificWork( DerivedB db );
但是,当通过 reference/pointer 将派生 class 的实例提供给 Base 时,我无权访问实例的实际类型,因此无法调用正确的 Derived *SpecificWork() 函数。
我想知道是否有某种设计模式允许我在不知道实例的实际类型的情况下调用派生的-class-特定函数,即具有与虚函数提供,但没有这些虚函数需要我将特定于应用程序的代码嵌入到 class 层次结构中。
实际上,我为什么要这样做是为了提供有关在 Lua 脚本调用的本机实现的函数中发生的异常的信息。每个异常都带有自己的一组信息,我想在脚本中表示错误的方式取决于异常的类型。我可以在基础 class 中创建一个由派生 classes 实现的纯虚方法,但这需要我将 Lua 相关代码嵌入到我的异常层次结构中,我不想这样做,因为 Lua 特定于使用该异常层次结构的应用程序之一。
我也不能使用 C++11。
谢谢。
为什么不为特定于应用程序的实现使用一组新的层次结构?
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 */ }
};
这样,您的第一个层次结构:Base
、DerivedA
、DerivedB
可以在不了解 AppLuaSpecific
中实现的应用程序特定代码的情况下存在。
您可以按如下方式实施您自己的特定于应用程序的调度 (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);
}
也许 Brigde 模式可以帮助您。
当您想避免抽象与其实现之间的永久绑定时,可以使用此模式。
(我没有看到你关于使用 c++11 的限制的评论,但你可以删除 std::unique_ptr
、std::move
和 override
关键字)
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);
}
正如我在访问者模式的评论中所说,您可以在不更改主层次结构(基础->派生类型)的情况下添加新功能。您只需定义一个新的访问者实现并为主层次结构中的每个 class 编写逻辑。在您的示例中,您可以将特定于应用程序的逻辑打包到一个对象中,并在您的派生对象中引用该逻辑,这是一种更简单的方法。