1

我有一些课程:

class Base
{
public:
    virtual void Something() = 0;
}

class A : public Base
{
public:
    virtual void Something() { /*...*/ }
    void SpecialActionForA();
}

class B : public Base
{
public:
    virtual void Something() { /*...*/ }
    void SpecialActionForB();
}

和一个数组:

Base* MyMembers[2];
MyMembers[0] = new A;
MyMembers[1] = new B;

我想做:

A* pointer_to_a = Get(0);
B* pointer_to_b = Get(1);

有没有什么好的方法来实现这个Get()功能?


我的解决方案是:

template <typename T>
T* Get(int index)
{
    return dynamic_cast<T*>(MyMembers[index]);
}

但首先我必须写

A* pointer_to_a = Get<A>(0)

这需要额外的<A>;

第二件事是,如果以某种方式new搞砸了:

MyMembers[0] = new B;

然后Get()失败了。
我想要的是一种可以将索引 0 映射到 A 的自动机制。


额外细节:实际上我有80不同的类派生自Base(它们是我的用户界面),
我需要的是让真正的类(真正的 UI)来做事。

我需要使用的功能是上面的SpecialActionForA()......等等。

也被Something()使用,但处于这些 UI 的初始化阶段,或者由 UI 管理器系统管理的东西。

4

3 回答 3

1

函数重载通过查找参数列表来工作。只判断返回类型是无法理解你想要什么样的值的。因此编译器强制你提供额外的细节来实例化模板。所以你的选择是:

Base *Get(int N) {
    return MyMembers[N];
}

或者

A *GetA(int N) {
   return MyMembers[N];
}
B *GetB(int N) {
   return MyMembers[N];
}

或者

template <typename T> void Get(int N, T *&item) {
   item = (T*)MyMembers[N];
}
A* pointer_to_a; 
B* pointer_to_b; 
Get(0, pointer_to_a);
Get(1, pointer_to_b);

或你自己的

template <typename T>
T* Get(int index)
{
    return dynamic_cast<T*>(MyMembers[index]);
}
于 2013-10-31T03:20:49.160 回答
1

原则上您不能这样做unknown* Get(int),因为它需要两个或多个具有相同名称但仅在返回值上有所不同的函数。C++ 明确禁止这一点。

您可以获得的最接近的功能是:

template <typename T> T* Get() {
  static T* instance = new T;
  return instance;
}

因此您可以将其用作:

A *a = Get<A>(); 
B *b = Get<B>();
于 2013-10-31T03:26:35.677 回答
0

在不了解您的使用场景的情况下,提供有用的答案会有点困难,但我确实相信您想要的是类型匹配。所以你所做的就是将变量作为基指针传递,直到我真正需要知道具体的底层类型,此时你对基指针的底层类型进行匹配。这与混合了 lambda 的 switch 语句非常相似,我将尝试提供一个示例。

...
Base* ptr = some_argument;
// Here we need the underlying type, so we'll do a match
Match(ptr)
{
    Case(A* a)
    {
        // this is invoked if ptr happened to hold an A, now do something A specific.
    }
    Case(B* b)
    {
        // this is invoked if ptr happened to hold a B, now do something B specific
    }
    Otherwise()
    {
        // this is invoked whenever none of the above are
    }
}
EndMatch;
...

这是通常解决方案的替代方案,即基类中的虚拟指针和覆盖。请注意,类型匹配通常用于函数式语言。对于 C++,Bjarne Stroustrup 的博士生之一 Yuriy Solodkyy 为实现这一目标提供了一个更好的解决方案。

可以在此处找到简短的介绍性文件;

https://parasol.tamu.edu/~yuriys/papers/OPM13EA.pdf

如需更多信息,请查看提交给 ISO 委员会的 hvis 论文;Stroustup、Dos Reis 和 Solodkyy 的 C++ 开放和高效类型切换

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3449.pdf

有关这些论文背后的源代码,请访问 Yuriys 主页:

https://parasol.tamu.edu/~yuriys/pm/

如果您拥有的类的数量确实是封闭的,那么请考虑使用封闭的代数数据类型,如boost::variant. 这些与不受限制的开放代数数据类型相比有一些优势,因为编译器通常可以确保穷举匹配,并且在封闭的性质允许更优化的模式匹配实现,尽管以灵活性为代价。

另请注意,它boost::variant在 C++03 上运行良好!

但是,请考虑您是否真的需要模式匹配或多态调度。两种方法都有优点和缺点,模式匹配方法的主要优点是您不需要更改类,并且避免使用超特定的虚拟方法污染基类,这些虚拟方法只能在单个调用中调用地点。

于 2013-10-31T04:00:33.040 回答