简体   繁体   English

具有派生类的C ++模板

[英]C++ templates with derived classes

I'm writing a library, and in it I have a class BaseClass. 我正在写一个库,其中有一个BaseClass类。 Anyone using the library will be creating their own classes that inherit from BaseClass. 使用该库的任何人都将创建自己的从BaseClass继承的类。 I have another class, lets call it Manager, which holds a vector of BaseClass pointers, which can hold any object derived from BaseClass. 我有另一个类,称之为Manager,它包含一个BaseClass指针向量,该指针可以容纳从BaseClass派生的任何对象。

The Manager class MUST handle the creation and destruction of any object added to its BaseClass vector. Manager类必须处理添加到其BaseClass向量中的任何对象的创建和销毁。 This is because any object in the vector can be deleted at any time, and the Manager itself can also be deleted. 这是因为可以随时删除矢量中的任何对象,并且Manager本身也可以删除。 Because of this, the user of the library cannot add an object to the Manager's baseClass vector by passing it a pointer to an already existing object derived from BaseClass. 因此,该库的用户无法通过将对象传递给从BaseClass派生的现有对象的指针来向Manager的baseClass向量添加对象。 Well actually, I could allow the user to do that. 好吧,实际上,我可以允许用户这样做。 But that would involve copying a dummy object, which I'd rather not do. 但这将涉及复制一个虚拟对象,而我宁愿不这样做。

To solve this, I'm trying to use a template function. 为了解决这个问题,我正在尝试使用模板函数。 The user should pass the type of the object derived from BaseClass when trying to add it to the Manager's vector. 尝试将其添加到Manager的向量时,用户应传递从BaseClass派生的对象的类型。 This is what I currently have. 这就是我目前所拥有的。

//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;
    }
};

For example, the user should do this when adding to or getting an object from the Manager's baseClass vector. 例如,当添加到Manager的baseClass向量中或从中获取对象时,用户应执行此操作。 DerivedClass derives from 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>();

My Get() function works fine. 我的Get()函数可以正常工作。 What I need to know is, how can I get my Add() function to work? 我需要知道的是,如何使Add()函数正常工作? Help would be greatly appreciated. 帮助将不胜感激。

There are quite a few things unclear about your design, but if the question is whether you can enforce that the type T in the member function templa Add can be enforced to derive from BaseClass , the options are simple: 您的设计还有很多地方不清楚,但是如果问题是您是否可以强制成员函数templa Add中的类型T可以强制从BaseClass派生,则选项很简单:

  • do nothing, the compiler will gladly complain on the line BaseClass* baseClass = new T(); 不执行任何操作,编译器将很乐意抱怨BaseClass* baseClass = new T();
  • add a static assert to make this more obvious 添加一个静态断言以使其更加明显
  • use fancy SFINAE tricks to remove the function from the set of overloads 使用花哨的SFINAE技巧从重载集中删除该函数

I would go for either one of the first two. 我会选择前两个。 The static assert could be spelled as: 静态断言可以拼写为:

static_assert(std::is_base_of<BaseClass,T>::value);

The SFINAE trick, I'd really avoid it, as it it going to make the code more confusing, but could be implemented as: 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();
}

(Note that I changed the return type to be T* , the object is a T , why return a BaseClass* ? The same applies to the Get function there is no point in returning a BaseClass* , when you know that the object is really a T ) (请注意,我将返回类型更改为T* ,对象是T ,为什么返回BaseClass* ?这同样适用于Get函数,当您知道对象确实是真正的对象时,返回BaseClass*毫无意义。一个T

Now the actual problem is far more complicated, since your design is actively avoiding considerations of ownership, which you should not. 现在,实际问题要复杂得多,因为您的设计正在积极地避免所有权的考虑,而您不应这样做。 Think about ownership of the objects and make sure that there is a clear owner (or that the resource is shared). 考虑对象的所有权,并确保有明确的所有者(或资源是共享的)。 Once you know who owns the objects, create a protocol for the rest of the code to notify the owner when an object needs to be deleted. 一旦知道谁拥有对象,就为其余代码创建协议,以在需要删除对象时通知所有者。 If you allow any code to delete the pointers you hold, you are going to run into undefined behavior very soon. 如果您允许任何代码删除持有的指针,那么您很快就会遇到未定义的行为。

Other lesser concerns could include the fact that you are enforcing that all components have a default constructor, which might or not be appropriate. 其他较小的问题可能包括以下事实:您强制所有组件都具有默认构造函数,该构造函数可能适用或不合适。 You could simplify this restriction by having a non-templated Add that takes a pointer, and letting the caller create the objects in the way they please. 您可以通过使用带有指针的非模板化Add并允许调用方按其喜欢的方式创建对象,来简化此限制。

The use of typeid is usually a code-smell and I don't think this is the exception, maybe you would be better off by designing a type hierarchy that you can ask what the object is, rather than running typeid . 使用typeid通常是一种代码嗅觉,我也不认为这是例外,也许您可​​以通过设计一个可以询问对象是什么的类型层次结构来更好,而不是运行typeid If you are really determined to make the interface based on types, then consider whether dynamic_cast could be better. 如果您真的确定要根据类型创建接口,请考虑使用dynamic_cast是否会更好。 It will be more innefficient, but if you have multiple levels of inheritance, it will let you return the most derived objects as intermediate objects 它会更加低效,但是如果您具有多个继承级别,它将使您将派生最多的对象作为中间对象返回

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

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