我有一个类层次结构,其中I<T>
和O<T>
是一个扩展B
(常规类)的模板化类。 I
通过指针聚合O
,如下所示:
class B { ... };
template <class T>
class O : public B
{
T getValue() { return value; }
private:
T value;
}
template <class T>
class I : public B
{
T getValue() { return ptr->getValue(); }
void setPtr(O<T>* po) { ptr = po; }
private:
O<T>* ptr;
}
上面的代码确实被简化为只显示它的骨头。尽管通过用户配置参数,“连接”I
和O
仍会自动完成。因此,Singleton表存储了指向所有B
和I
对象的多态O
指针,并根据需要调用I::setPtr
来执行链接。
但问题是,用户实际上可能会混淆和混淆I
和O
对象的模板类型,例如用户可以要求将I<int>
与O<float>
联系起来。 E.g:
O<int>* oi = new O<int>();
O<float>* of = new O<float>();
I<int> ii;
ii.setPtr(oi); /* This is OK. */
O<int>* oi_wrong = static_cast<O<int>*>((B*)of); /* This happens. */
ii.setPtr(oi_wrong); /* This is bad. */
为了防止这种行为,我在I
和O
中都包含了一个存储typeid(T).name()
的成员变量,因此可以检查模板参数是否实际相同。
有更好的方法吗?
编辑:这里有更好的方法进行投射吗?在这种情况下存储和使用多态指针不是一个好主意吗?
答案 0 :(得分:1)
如果您的用户不想使用您的界面,那么您无能为力 - 您应该(合理地)设计代码而不假设您的用户将使用static_cast
和reinterpret_cast
自由地。
在您的情况下,我不会添加任何其他代码/支票。假设您的用户以合理的方式行事。
如果你真的想要,你可以使用assert
和dynamic_cast
来检查传递的指针是否实际上是你在DEBUG构建中所期望的:
struct A { virtual ~A() { } };
struct B : A { };
struct C : A { };
void setPtr(A* po)
{
assert(dynamic_cast<C*>(po) != nullptr);
}
int main()
{
B b;
setPtr((C*)&b); // might trigger assertion
}
但我强烈建议不要这样做,因为这太过分了。
答案 1 :(得分:0)
如何为setPtr()使用友元函数,因此用户被迫输入I和O的拟合类型:
class B { };
template <typename T>
class O : public B
{
public:
T getValue() { return value; }
void setPtr(I<T>& ref_i) { ref_i.ptr = this; };
private:
T value;
};
template <typename T>
class I : public B
{
public:
T getValue() { return ptr->getValue(); }
friend void O::setPtr(I<T>& ref_i)
private:
O<T>* ptr;
};
int main()
{
O<int>* oi = new O<int>();
O<float>* of = new O<float>();
I<int> ii;
of->setPtr(ii); /* This fails. */
O<int>* oi_wrong = static_cast<O<int>*>((B*)of); /* This CAN happen. */
oi->setPtr(ii); /* This is OK. */
}