带派生类的C ++模板

时间:2013-11-06 22:21:37

标签: c++ templates inheritance derived-class polymorphism

我正在写一个库,在其中我有一个类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()函数起作用?非常感谢帮助。

1 个答案:

答案 0 :(得分:5)

关于您的设计有很多不清楚的事情,但是如果问题是您是否可以强制执行成员函数templa T中的Add类型可以强制从{{1选项很简单:

  • 什么都不做,编译器会很乐意在BaseClass
  • 行上抱怨
  • 添加静态断言以使其更明显
  • 使用花哨的SFINAE技巧从过载集中删除函数

我会选择前两个中的任何一个。静态断言可以拼写为:

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是否会更好。它会更加无效,但是如果你有多个级别的继承,它将允许你将最派生的对象作为中间对象返回