简体   繁体   中英

C++ templates with derived classes

I'm writing a library, and in it I have a class BaseClass. Anyone using the library will be creating their own classes that inherit from BaseClass. I have another class, lets call it Manager, which holds a vector of BaseClass pointers, which can hold any object derived from BaseClass.

The Manager class MUST handle the creation and destruction of any object added to its BaseClass vector. This is because any object in the vector can be deleted at any time, and the Manager itself can also be deleted. 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. 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. 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. DerivedClass derives from 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. What I need to know is, how can I get my Add() function to work? 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:

  • do nothing, the compiler will gladly complain on the line 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

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:

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 )

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.

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 . If you are really determined to make the interface based on types, then consider whether dynamic_cast could be better. 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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