简体   繁体   中英

Can I handle class as objects in C++

Here is what I am trying to achieve: I have a list of Classes (Class1 to Classn) which inherit from a main Class I would like to be able to instanciate an object of any of the n classes without having to do a large switch case (or equivalent). something along the lines of:

static ClassPointerType const * const ArrayOfClassTypes[]={ Class1, Class2, .. Classn }; 

static Class *GetObjectOfClass(int i)
{
  return new ArrayOfClassTypes[i](some parameters for the constructor);
}

You can do that in other OO langues like Delphi where you have a TClass type and can get the class of an object... but I was not able to locate the equivalent functionality in C++.

Are you looking for something like this?

template<typename T>
std::unique_ptr<base> make()
{
    return std::unique_ptr<base>(new T);
}

class factory
{
    static constexpr std::unique_ptr<Base> (*fns[])(){make<derived_a>, make<derived_b>};

    std::unique_ptr<base> get_object_of_class(int const i)
    {
        if (i < 0 || sizeof fns / sizeof *fns <= i) {
            return nullptr;
        }

        return fns[i]();
    }
};

You can add virtual method Clone to your base class and have it pure virtual (= NULL). Have there all required parameters.
Then you can override it in every subclass and create concrete classes there.
And in your factory do:

static ClassPointerType *GetObjectOfClass(int i)
{
  return new ArrayOfClassTypes[i]->Clone(some params for the constructor);
}

And your array should contain concrete classes which you will clone:

static ClassPointerType* const ArrayOfClassTypes[]={ new ClassPointerType1(),new ClassPointerType2(), .. new ClassPointerTypeN() }; 

Complete example as requested:

// base class
class ClassPointerType
{
…
public:
    virtual ClassPointerType* Clone(your params) = NULL;
};

// concrete classes
class ClassPointerType1 : public ClassPointerType
{
…
    public:
        // note: it clones own concrete instance
        virtual ClassPointerType* Clone(your params) {return new ClassPointerType1(your params)};
}

class ClassPointerType2 : public ClassPointerType
{
…
    public:
        virtual ClassPointerType* Clone(your params) {return new ClassPointerType2(your params)};
}
…


class ClassPointerTypeN : public ClassPointerType
{
…
    public:
        virtual ClassPointerType* Clone(your params) {return new ClassPointerTypeN(your params)};
}

I implemented something alike recently. In my approach I stored a list of static create-functions instead and feeded a factory class with that

I implemented a templated base class wich

a) is able to register the derived into a factory class

b) implicitly forces the derived class to provide static functions.

However, you have to announce each derived class once. You can use it like this:

int main(int argc, char* argv[])
{
    DerivedA::announce();

    //and later
    IInterface * prt = SingeltonFactory::create(DerivedA::_type);

    delete prt;

    return 0;
}

The Derived class DerivedA is defined as:

class DerivedA :
      public IInterface,
      public StaticBase<DerivedA>
{
public:
    using StaticBase::announce;
    static IInterface * create(){ return new DerivedA; }
    static const std::string _type;

};
const std::string DerivedA::_type=std::string("DerivedA");

And the static Base class forcing the _type attribute and the create function to exist are defined as follows:

template<class TDerived>
class StaticBase
{
protected:
    static void announce()
    {
        // register into factory:
        SingeltonFactory::registerFun(TDerived::_type,TDerived::_create());
        // The call of _type and _create implicitly forces the derived class to implement these, if it is deriving from this Base class
    }
};

The factory class does contain a map of

std::map<std::string,tFunPtr> 

where typedef tFunPtr is:

typedef IInterface * (*tFunPtr)(void);

This map you can use as an "array of classes" in order to handle it like an object. Thus, you represent the classes by a function pointer of the static create function

Does this answer your requirements? Shall I provide the factory class?

Assuming you are using a C++11 compiler here is a solution equivalent to the one provided earlier but less tricky and clever:

#include <iostream>
#include <memory>
#include <array>

class Base {
public:
    virtual void doSomething() = 0;
};

class Der1: public Base {
private:
    void doSomething() override {
    std::cout << "Der1 did something" << std::endl;
}
};

class Der2: public Base {
private:
    void doSomething() override {
    std::cout << "Der2 did something" << std::endl;
}

};

template <typename T>
std::unique_ptr<Base> make() {
    return std::unique_ptr<T>(new T);
}

int main() {

    std::array<std::function<std::unique_ptr<Base>(void)>, 2> arr{make<Der1>,
                                                                  make<Der2>};

    auto obj = arr[0]();
    obj->doSomething();
    obj = arr[1]();
    obj->doSomething();
}

You can use std::bind too to pass arguments to make if you have to use a non-default contructor

Hope that helps

No, in C++ classes are not first-order entities.

(I just noticed everybody is providing a solution to your problem but not precisely answering your question.)

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