Finding which derived class object is pointed to by a base class pointer

closed account (D80DSL3A)
What methods can be used?

I have a class with a dynamically allocated data member (through a base class pointer. I think I need to identify the object pointed to within my copy ctor and = operator, so I can allocate the appropriate type via that derived class's copy ctor.
closed account (D80DSL3A)
@ne555. That was perfect. Thank you!

Hamstermanns method would work, but it carries the disadvantage that another case would have to be added to the switch in copy_me() when any new derived class is added.

I am at liberty to base my classes on a clonable type so the Clone Pattern, as explained by jsmith, is perfect!

EDIT: Better yet! The class hierarchy already has an abstract base. I only need to add the pure virtual clone() to the existing base class!
Last edited on
Not entirely sure what you mean. But does this look right? I ran into this same thing last night and got it working this way.

I have a base class with a copy constructor.
I have a derived class with a copy constructor which calls the base classes copy constructor.
I have a function which casts the derived class to the base class so that we can add this as the initialization value in the base classes copy constructor.

You can't just use the default copy constructor because that will copy the address of the data into your pointer, not the data itself. And the memory will be de-allocated once the original object is gone. Therefore you have to make your own copy constructor and copy the memory manually in the base class. You'll need to call that copy ctor in your derived class and pass it an object that it can use.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class BaseClass
{
protected:
  int* _i;
public:
  BaseClass()                   // default ctor
    : _i(new int)
  {  }

  BaseClass( BaseClass& b)      // copy ctor
    : _i(new int)
  { 
    *_i = *(b._i); // Copy data manually
  }

  ~BaseClass() { delete _i; }   // dtor
};


class DerivedClass : BaseClass
{
public:
  DerivedClass(DerivedClass& d)  // copy ctor
    : BaseClass( d )             // casts b to BaseClass, then calls BaseClass copy ctor
  {  }

  operator BaseClass()           // cast (Derived to BaseClass)
  {
    BaseClass returnObject();
    *(returnObject._i) = *_i;  // Copy data here again
    return returnObject;
  }
};


// Edit, simplified code a little.
Last edited on
closed account (D80DSL3A)
Thank you Stewbond, but I think the situation I'm facing is different than you have imagined.

It actually appears that I need to use both solutions!

I have a class hierarchy of function objects:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class moveEquations// abstract base
{
    public:
    virtual float s( float t ) = 0;// gives position along an arc as a function of the time

    moveEquations();
    virtual ~moveEquations();
};

// for speed = constant
class constV : public moveEquations
{
    public:
    float v;// speed

    virtual float s( float t )
    { return v*t; }

    constV( float V ): v(V) {}
};

// for acceleration = constant
class constAcc : public moveEquations
{
    public:
    float v0;// speed at t = 0
    float a;// acceleration

    virtual float s( float t )
    { return v0*t + a*t*t/2.0f; }

    constAcc( float V0, float acc ): v0(V0), a(acc) {}
    constAcc( float V0, float Vf, float period ): v0(V0) { a = (Vf-V0)/period; }
};


Two classes exist, each having an inherited pointer to a moveEquation as a data member, and a copy ctor (among other things)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "moveEquations.h"

class Lbase// define the shape of the arc being traveled upon
{
    moveEquation* pMotion;
    virtual float x( float t ) = 0;// pMotion used here
    virtual float y( float t ) = 0;// and here
}

class L1 : public Lbase
{    
    L1( const L1& rL1 );
    .....
}

class L2 : public Lbase
{
    L2( const L2& rL2 );
    .....
}


Either a constV or a constAcc object is allocated to pMotion when either an L1 or L2 object is constructed (depending on which ctor is used - not shown).

If an L1 or L2 object has to be copied, then a new instance of a moveEquation must be created for the new L1 or L2 objects pMotion member to point to, as you have correctly pointed out.

It turns out that in class L1 it is fine to copy the moveEquation object "as-is", ie. without also modifying the values of the moveEquations data members.
In this case, a clone() works great!
1
2
3
4
5
L1::L1( const L1& rL1 )
{
    pMotion = rL1.pMotion->clone();// sweet!!
    .....
}


However, in class L2 it is necessary to "reset'" the state of the moveEquation being copied:
1
2
3
4
5
6
7
8
9
10
11
12
L2::L2( const L2& rL2 )
{
   switch( rL2.pMotion->get_id() )// 2nd solution method used
   {
        case: 1
        pMotion = new constV( 7.5 );// reset value of v
        break;
        case: 2
        pMotion = new constAcc( 5.0, 3.2 );// reset values of v and a
        break;
    .....
}


So, incredibly, I am implementing BOTH methods:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class moveEquations
{
    public:
    virtual float s( float t ) = 0;
    virtual moveEquations* clone() const = 0;// using clone pattern (jsmiths solution )
    virtual unsigned get_id() = 0;// Hamstermans solution

    moveEquations();
    virtual ~moveEquations();
};

// for speed = constant
class constV : public moveEquations
{
    public:
    float v;// speed

    virtual float s( float t )
    { return v*t; }

    virtual constV* clone() const
    { return new constV( *this ); }
    
    virtual unsigned get_id() { return 1;}

    constV( float V ): v(V) {}
};

// for acceleration = constant
class constAcc : public moveEquations
{
    public:
    float v0;// speed at t = 0
    float a;// acceleration

    virtual float s( float t )
    { return v0*t + a*t*t/2.0f; }

    virtual constAcc* clone() const
    { return new constAcc( *this ); }
    
    virtual unsigned get_id() { return 2;}

    constAcc( float V0, float acc ): v0(V0), a(acc) {}
    constAcc( float V0, float Vf, float period ): v0(V0) { a = (Vf-V0)/period; }
};


EDIT: Numerous typos. Adding code comments
Last edited on
However, in class L2 it is necessary to "reset'" the state of the moveEquation being copied:
So you only care about the type of movement.
Then you may want to implement a default method
1
2
3
4
5
6
7
8
9
10
class base{
public:
   virtual base* Default() const;
};
class derived: public base{
public:
   virtual derived* Default() const{
      return new derived();
   }
};
I don't like the id method as you need to modify the calling code, reducing flexibility.

Edit: default is a reserved word xP
Last edited on
closed account (D80DSL3A)
I need the freedom to assign values for v and a within the L2 class copy ctor.
It was misleading of me to indicate that the values assigned for the data members would be constant.
ne555 wrote:
So you only care about the type of movement.

I'm thinking of the movement "type" ( constant speed vs. constant acceleration )as determining what class "type" to use: constV for constant speed vs. constAcc for variable speed.
What I wish to reassign on copy are the motion parameters v (for a constV object) or v0 and a (for constAcc object). These determine the rates of motion.

I like the clone pattern solution very much as it does lend to easy expansion of the class list it covers. However, I don't see any way to access the data members of the object being cloned.

The get_id() method does have the disadvantage that you mentioned, but the cases almost have to be hard coded since different ctors are being used in the various cases.

I think that jsmiths closing comment re: the clone pattern deserves repetition here:
jsmith wrote:

It is worth mentioning here that this technique exploits the fact that
the compiler does not consider the return type of the function when
determining whether or not a derived class virtual method has overridden
a base class one with the same name.


EDIT: ne555's default method also exploits this fact. This default method is an exact parallel to the clone concept, except it uses the default ctor instead of the copy ctor.
Last edited on
Topic archived. No new replies allowed.