이준학
이준학

Reputation: 31

I wonder if I can allocate a class to a particular member data and create an instance of that class

I want to know whether it is possible or not.

I have a class in one project.

    class A { ...
    void CreateInternal(void);
    B* m_pData;
    ... }

void A::CreateInternal(void)
{
  m_pData= new B;
}

and in other project, i add C class and want to make it in A::CreateInernal(void) like this...

void A::CreateInternal(void)
{
  m_pData = new C; // ex) B is base class of C
}

Of course, i can make Class A to template class, however, i do not want it. I want to use it last. ( Suppose that dependency or other build error is free )

    typename<class T>
    class A { ...
    void CreateInternal(void);
    T* m_pData
        ... }

    void A<T>::CreateInternal(void)
    {
       m_pData = new T;
    }

I just want to register Class B or Class C in Class A. Is it possible??

Thank you for your reply!

Upvotes: 2

Views: 182

Answers (1)

Aconcagua
Aconcagua

Reputation: 25526

As denoted in the comments:

  1. You don't want to make A a template class.
  2. You the classes you want to create instances of share a common base class, which is the type of the member pointer as well (precondition for the following!).

So you can make createInternal a template function instead:

template <typename T>
void A::createInternal()
//   ^^^ A still is a normal class!
{
    m_data = new T();
    // (will fail if T is a different type NOT inheriting
    // from the base class in question)
}

Side note: Normally, if template functions are provided inside classes, you would have to implement them in the header file (typically at least, there are ways around...), if createInstance is private, though, and you don't call it from public inline functions, you can safely implement it in the source file as well as this file will be the only location the template ever is used.

You can even go a step further and allow to call all possible constructors by use of a variadic template:

template <typename T, typename ... TT>
void A::createInternal(TT&& ... tt)
{
    m_data = new T(std::forward(tt)...);
    // (will fail, if there is no matching constructor
    // for the arguments provided)
}

OK, you now have to specify which instance you want to create:

void A::f()
{
    createInternal<B>();
    createInternal<C>();
}

I assume this being acceptable as you need to be able to tell somehow which type you actually want to create.

The alternative would be different functions for different classes (createInternalB, createInternalC), but that certainly is less elegant.

Side note:

Is it possible??

Even if your classes did not share a common base class, you still could store your objects created in a std::variant or, if you happen to compile pre-C++11, even in a union (but you need to take care yourself about which type actually is stored – in this respect, std::variant is just much safer, so prefer it whenever possible).

Update on new comments:

If you want to be able to create arbitrary objects within A without having to know which one actually is created, now offering two further alternatives:

In first variant you could make createInternal virtual in A (possibly even pure virtual) and then override in derived classes to provide the objects suiting your needs.

In second variant you provide an instance of some object provider class; assuming D is the base class of both B : public D and A : public D, so then the solution might look similar to this:

class A
{
public:
    class ObjectProvider
    {
    public:
        virtual ~ObjectProvider() = default;
        public: virtual D* createInstance() = 0;
    };
    template <typename T>
    class TheObjectProvider : public ObjectProvider
    {
        public:
        D* createInstance() override
        {
            return new T();
        }
    };

    A(std::unique_ptr<ObjectProvider> p)
        : objectProvider(std::move(p))
    { }

private:
    std::unique_ptr<ObjectProvider> objectProvider;
    std::unique_ptr<D> m_data;

    void createInternal()
    {
        m_data = objectProvider->createInstance();
    }
};

Usage:

A ab(std::make_unique<A::ObjectProvider<B>());

// your own custom object provider:
class OP : public A::ObjectProvider
{
public:
     C* createInstance() override { return new C(); }
};
A ac(std::make_unique<OP>());

The std::unique_ptr as parameter (although in general, it is not recommended to use smart pointers for) now has an important function: It indicates that the class will take ownership of the object passed, i. e. is a sink in Herb Sutter's wording – see his GotW91).

If you have a most commonly used data type (e. g. B), you could provide an overloaded default constructor providing an object provider for this type (or possibly the base type D, if not being abstract).

Upvotes: 4

Related Questions