繁体   English   中英

具有派生类的C ++模板

[英]C++ templates with derived classes

我正在写一个库,其中有一个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()函数正常工作? 帮助将不胜感激。

您的设计还有很多地方不清楚,但是如果问题是您是否可以强制成员函数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函数,当您知道对象确实是真正的对象时,返回BaseClass*毫无意义。一个T

现在,实际问题要复杂得多,因为您的设计正在积极地避免所有权的考虑,而您不应这样做。 考虑对象的所有权,并确保有明确的所有者(或资源是共享的)。 一旦知道谁拥有对象,就为其余代码创建协议,以在需要删除对象时通知所有者。 如果您允许任何代码删除持有的指针,那么您很快就会遇到未定义的行为。

其他较小的问题可能包括以下事实:您强制所有组件都具有默认构造函数,该构造函数可能适用或不合适。 您可以通过使用带有指针的非模板化Add并允许调用方按其喜欢的方式创建对象,来简化此限制。

使用typeid通常是一种代码嗅觉,我也不认为这是例外,也许您可​​以通过设计一个可以询问对象是什么的类型层次结构来更好,而不是运行typeid 如果您真的确定要根据类型创建接口,请考虑使用dynamic_cast是否会更好。 它会更加低效,但是如果您具有多个继承级别,它将使您将派生最多的对象作为中间对象返回

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM