ICoffeeConsumer
ICoffeeConsumer

Reputation: 912

Access class of current object

I'm implementing a virtual clone() method that can be entirely identical with the exception of the "actual" type of the object returned, and I realized that there's going to be a lot of duplication. For example, here's the obvious solution that I want to avoid:

AbstractVehicle* ConcreteCar::clone() const
{
    auto c = new ConcreteCar;
    c->setSomething(this->getSomething());
    return c;
}

AbstractVehicle* ConcreteJet::clone() const
{
    auto j = new ConcreteJet;
    j->setSomething(this->getSomething());
    return j;
}

And here's what I would like:

AbstractVehicle* AbstractVehicle::clone() const
{
    auto v = new this->ACTUAL_CLASS;
    v->setSomething(this->getSomething());
    return v;
}

Is there any straightforward way to do this? If not, I'd love to know why this is the case, because naively it does not seem like more of a challenge to implement than something like dynamic dispatch.

Upvotes: 3

Views: 800

Answers (4)

Francis Cugler
Francis Cugler

Reputation: 7915

This may not be exactly what you was asking for; but this may achieve what you are after. What I have here is an abstract base class with three defined derived classes. I do not have a cloning method within the class that is of a virtual type, but what I do have is a template function that does the work for you.

#include <iostream>
#include <conio.h>

class Base {
public:
    enum Type {
        TYPE_1 = 0,
        TYPE_2,
        TYPE_3,
    }; // Type

private:
    Type m_eType;

public:
    virtual ~Base() {}

    Type getType() const { return m_eType; }

protected:
    explicit Base( Type eType ) : m_eType( eType ) {}

private:
    Base( const Base& c ); // Not Implemented
    Base& operator=( const Base& c ); // Not Implemented

}; // Base

class Derived1 : public Base {
private:
    int m_val;

public:
    Derived1() : Base( TYPE_1 ), m_val( 0 ) {}
    explicit Derived1( int val ) : Base( TYPE_1 ), m_val( val ) {}
    virtual ~Derived1() {}

    int    getValue() const { return m_val; }
    void   setValue( int val ) { m_val = val; }

private:
    Derived1( const Derived1& c ); // Not Implemented
    Derived1& operator=( const Derived1& c ); // Not Implemented

}; // Derived1

class Derived2 : public Base {
private:
    float m_val;

public:
    Derived2() : Base( TYPE_2 ), m_val( 0 ) {}
    explicit Derived2( float val ) : Base( TYPE_2 ), m_val( val ) {}
    virtual ~Derived2() {}

    float getValue() const { return m_val; }
    void  setValue( float val ) { m_val = val; }

private:
    Derived2( const Derived2& c ); // Not Implemented
    Derived2& operator=( const Derived2& c ); // Not Implemented

}; // Derived2

class Derived3 : public Base {
private:
    double m_val;

public:
    Derived3() : Base( TYPE_3 ), m_val( 0 ) {}
    explicit Derived3( double val ) : Base( TYPE_3 ), m_val( val ) {}
    virtual ~Derived3() {}

    double getValue() const { return m_val; }
    void   setValue( double val ) { m_val = val; }

private:
    Derived3( const Derived3& c ); // Not Implemented
    Derived3& operator=( const Derived3& c ); // Not Implemented

}; // Derived3

// This Function Template Takes The Base Of The Object And The
// Derived Type For Its Templated Parameters. The Function's
// Parameter Takes A Pointer To The Derived Type And A Bool Flag That Is 
// True By Default And Will Make A Complete Clone, If This Is False It Will 
// Make A Fresh Clean Object, It Will Construct
// A New Object On The Heap, Then Dynamically Cast It To A Pointer Of
// Its Base And It Will Return Its Base Pointer, Otherwise It Will
// Return nullptr. NOTE: I Did Not Include Any Error Checking To
// See If The Two Templated Types Passed In Are Derived From One Another.
template<class BaseT, class DerivedT>
BaseT* clone( DerivedT* clonerPtr, bool bClone = true ) {
    BaseT* basePtr = nullptr;

    if ( bClone ) {
        DerivedT* pTemp = new DerivedT();
        pTemp = clonerPtr; // Will Require operator=() to be defined for the derived classes.
        if ( pTemp == nullptr ) {
            return nullptr;
        }
        basePtr = dynamic_cast<BaseT*>( pTemp );
    } else {
        // Create New Empty Object.
        clonerPtr = new DerivedT();
        if ( clonerPtr == nullptr ) {
            return nullptr;
        }
        basePtr = dynamic_cast<BaseT*>( clonerPtr );

    }       
    return basePtr;
}

int main() {
    Base* pBase = nullptr;

    // 1st Case Using The Heap        
    Derived1* ptr1 = new Derived1( 5 );        
    pBase = clone<Base, Derived2>( ptr1 );        
    std::cout << pBase->getType() << std::endl;

    if ( ptr1 ) {
        delete ptr1;
        ptr1 = nullptr;
    }
    if ( pBase ) {
        delete pBase;
        pBase = nullptr;
    }

    // 2nd Case Using Stack Object
    pBase = nullptr; // Not need but just incase
    Derived2 d2( 3.41f );
    pBase = clone<Base, Derived2>( &d2 );
    std::cout << pBase->getType() << std::endl;

    if ( pBase ) {
        delete pBase;
        pBase = nullptr;
    }

    std::cout << "Press any key to quit." << std::endl;
    _getch();
    return 0;
}

Another reminder is this; the template function nor the classes does any clean up of memory on the heap via the new and delete operator. So care needs to be used with this method or approach. If one wanted to achieve a similar approach but doesn't have to worry about cleaning up dynamic memory then the use of smart pointers should be used within the function template.

Upvotes: 0

1201ProgramAlarm
1201ProgramAlarm

Reputation: 32762

Depending on hour your setSomething/getSomething functions are defined, you may be able keep a single clone, and just have overrides on a 'create me' type function. For example:

AbstractVehicle* ConcreteCar::MakeNew() {    // virtual method
   return new ConcreteCar();
}

This would be called from the base class clone:

AbstractVehicle* AbstractVehicle::Clone() {
    auto j = MakeNew();
    j->setSomething(this->getSomething());
    return j;
}

Upvotes: 1

Dimitrios Bouzas
Dimitrios Bouzas

Reputation: 42909

There's no straightforward way to do this. However, (I might get hanged for this) you can avoid writing multiple definitions of your clone functions by using a pre-processor define as below:

#define DEFINE_VEHICLE_CLONE_MEMFUN(Vehicle) \
AbstractVehicle* Vehicle::clone() const {    \
    auto v = new Vehicle;                    \
    v->setSomething(this->getSomething());   \
    return v;                                \
}

And then define the member function in code as:

DEFINE_VEHICLE_CLONE_MEMFUN(ConcreteCar)
DEFINE_VEHICLE_CLONE_MEMFUN(ConcreteJet)

Upvotes: 1

Peter
Peter

Reputation: 36607

No. operator new requires the type of the object(s) it is creating to be specified at compile time. What you are asking for requires the type of object to be created to be unknown at compile time, and worked out (or otherwise obtained) at run time.

Upvotes: 2

Related Questions