我正在编写一个库,其中有一个类 BaseClass。使用该库的任何人都将创建自己的继承自 BaseClass 的类。我有另一个类,我们称之为管理器,它包含一个 BaseClass 指针 vector ,它可以包含从 BaseClass 派生的任何对象。
Manager 类必须处理添加到其 BaseClass vector 中的任何对象的创建和销毁。这是因为vector中的任何对象都可以随时删除,Manager本身也可以删除。因此,库的用户无法通过将对象传递给从 BaseClass 派生的现有对象的指针来将对象添加到 Manager 的 baseClass vector 中。实际上,我可以允许用户这样做。但这将涉及复制一个虚拟对象,我宁愿不这样做。
为了解决这个问题,我尝试使用模板函数。用户在尝试将对象添加到管理器的 vector 时应传递从 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 vector 添加对象或从中获取对象时应执行此操作。 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() 函数工作?将不胜感激。
最佳答案
关于您的设计有很多事情不清楚,但是如果问题是您是否可以强制执行成员函数 templa Add
中的类型 T
可以强制执行从 BaseClass
派生,选项很简单:
- 什么都不做,编译器会很乐意在
BaseClass* baseClass = new T();
这行提示
- 添加一个静态断言以使其更加明显
- 使用花哨的 SFINAE 技巧从重载集中删除函数
我会选择前两个中的任何一个。静态断言可以拼写为:
static_assert(std::is_base_of<BaseClass,T>::value);
SFINAE 技巧,我真的会避免它,因为它会使代码更加困惑,但可以实现为:
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
函数,当您知道该对象实际上是一个 T
时,返回一个 BaseClass*
是没有意义的)
现在实际问题要复杂得多,因为您的设计正在主动避免考虑所有权,而您不应该这样做。考虑对象的所有权并确保有明确的所有者(或共享资源)。一旦知道谁拥有这些对象,就可以为其余代码创建一个协议(protocol),以便在需要删除对象时通知所有者。如果您允许任何代码删除您持有的指针,您很快就会遇到未定义的行为。
其他较小的问题可能包括您强制所有组件都具有默认构造函数这一事实,这可能合适也可能不合适。您可以通过使用一个带指针的非模板化 Add
并让调用者以他们喜欢的方式创建对象来简化此限制。
typeid
的使用通常是一种代码味道,我不认为这是异常(exception),也许你会更好地设计一个类型层次结构,你可以询问对象是什么,而不是运行 typeid
。如果你真的下定决心要做基于类型的接口(interface),那么考虑一下dynamic_cast
是否可以更好。它会更加低效,但如果你有多个继承级别,它会让你将派生最多的对象作为中间对象返回
关于具有派生类的 C++ 模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19824070/