我正在写一个库,在其中我有一个类BaseClass。使用该库的任何人都将创建自己的继承自BaseClass的类。我有另一个类,我们称之为Manager,它包含一个BaseClass指针的向量,它可以包含从BaseClass派生的任何对象。
Manager类必须处理添加到其BaseClass向量的任何对象的创建和销毁。这是因为可以随时删除向量中的任何对象,也可以删除Manager本身。因此,库的用户无法通过向指向从BaseClass派生的现有对象传递指针来向Manager的baseClass向量添加对象。实际上,我可以允许用户这样做。但这将涉及复制虚拟对象,我宁愿不这样做。
要解决这个问题,我正在尝试使用模板功能。在尝试将其添加到Manager的向量时,用户应传递从BaseClass派生的对象的类型。这就是我现在所拥有的。
//Manager.h
#include <vector>
#include "BaseClass.h"
#include <typeinfo>
class Manager {
//Vector holding pointers to any objects inherited from BaseClass
vector<BaseClass*> baseClasses;
//Template function that needs to add NEW object to baseClass vector
//This I'm having problems with
template<class T>
BaseClass* Add() {
BaseClass* baseClass = new T();
baseClasses.push_back(baseClass);
return baseClass;
}
//Template function that gets object from baseClass vector
//This works fine
template<class T>
BaseClass* Get() {
for (int i = 0; i < baseClasses.size(); i++) {
if (typeid(*baseClasses[i]) == typeid(T)) {
return baseClasses[i];
}
}
return NULL;
}
};
例如,用户应该在从Manager的baseClass向量添加或获取对象时执行此操作。 DerivedClass派生自BaseClass
Manager manager;
//Add a new DerivedClass object to Manager's vector
manager.Add<DerivedClass>();
//Get a pointer to the DerivedClass object that was just added
DerivedClass* derivedClass = (DerivedClass*)manager.Get<DerivedClass>();
我的Get()函数运行正常。我需要知道的是,如何使我的Add()函数起作用?非常感谢帮助。
答案 0 :(得分:5)
关于您的设计有很多不清楚的事情,但是如果问题是您是否可以强制执行成员函数templa T
中的Add
类型可以强制从{{1选项很简单:
BaseClass
我会选择前两个中的任何一个。静态断言可以拼写为:
BaseClass* baseClass = new T();
SFINAE技巧,我真的避免使用它,因为它会使代码更加混乱,但可以实现为:
static_assert(std::is_base_of<BaseClass,T>::value);
(请注意,我将返回类型更改为template <class T>
typename std::enable_if<std::is_base_of<BaseClass,T>::value,T*>::type
Add() {
baseClasses.push_back(new T());
return baseClasses.back();
}
,对象为T*
,为什么返回T
?这同样适用于那里的BaseClass*
函数当你知道对象真的是Get
)
BaseClass*
是没有意义的
现在实际问题要复杂得多,因为你的设计主动避免了所有权的考虑,你不应该这样做。考虑对象的所有权,并确保有一个明确的所有者(或资源是共享的)。一旦知道谁拥有对象,就可以为其余代码创建协议,以便在需要删除对象时通知所有者。如果你允许任何代码删除你持有的指针,你很快就会遇到未定义的行为。
其他较少的问题可能包括您强制所有组件都有默认构造函数,这可能适合也可能不合适。您可以通过使用带有指针的非模板化T
来简化此限制,并让调用者以他们喜欢的方式创建对象。
Add
的使用通常是代码嗅觉,我不认为这是例外,也许你会通过设计一个类型层次结构来更好,你可以问对象是什么,而不是正在运行typeid
。如果你真的决心根据类型制作界面,那么考虑typeid
是否会更好。它会更加无效,但是如果你有多个级别的继承,它将允许你将最派生的对象作为中间对象返回