how do I tell the class of an object?

Let's say I have base class and a derived class inheriting from it. Then I have a pointer to a base class that is pointing to an object. So, because of polymorphism, this object could be of the base class or the derived class. How do I tell which one is it?
Why may I ask do you need to do that?

There are ways but this sort of thing should generally be avoided. For example, typeof could be used or by testing the success of a dynamic_cast to a pointer of the derived type.

It is very likely that simple polymorphism can accomplish your goal, though. Would adding a virtual method, overridden in the derived class, that performs the appropriate "action" fit your needs?

Here's a simple example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

class Animal
{
public:
    virtual speak() { cout << "generic animal noises!" << endl; }
};

class Dog : public Animal
{
public:
    virtual speak() { cout << "bark, bark, bark!" << endl; }
};

int main()
{
    Animal a;
    Dog d;
    a.speak();
    d.speak();
    return 0;
}


From a pointer, p, to either of those types, you could just call p->speak(); to get the intended polymorphic behavior.
If you absolutely have to do this (though, as has been asked previously, why?), you could create a virtual method that returns some data you can use to determine what type it is.
The reason that people are asking "why" is that the need to do this is a very strong indicator of a bad class hierarchy. It generally means that your inheritance hierarchy do not have a proper "IS A" relationship or you are delegating class functionality outside the class.
Ok, here's an example

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
#include <vector>

class Task
{
};

class Study_Task : public Task
{
private:
   //what textbook should I study
   Textbook object;
   //how many hours should I study
   int hours;
public:
   Textbook get_textbook()
   {
      return object;
   }
};

class Watch_Movie_Task : public Task
{
private:
   //what movie should I watch
   Movie movie_object;
   //where is the cinema
   Position object;
public:
   Movie get_movie()
   {
      return movie_object;
   }
};

int main()
{

   //this is my schedule, a list of task that I need to do in order
   vector<Task*> schedule;

   //first I should study
   schedule.push_back( new Study_Task() );
   //then I go watch a movie
   schedule.push_back( new Watch_movie_Task() );
}


And so let say I pass my schedule to someone and he or she needs to read it, then that someone will do something like
1
2
3
4
5
6
7
8
9
10
11
12
//if the first task is to study
if( typeid(schedule[0]).name()) == "class Study_Task")
{
   //find out which textbook to study
   dynamic_cast<Study_Task*>(schedule[0])->get_textbook();
}
//if the first task is to watch a movie
else if( typeid(schedule[0]).name()) == "class Watch_Movie_Task")
{
   //find out which movie to watch
   dynamic_cast<Watch_Movie_Task*>(schedule[0])->get_movie();
}


Is this right?
Last edited on
You could, but in this case I would suggest making a pure virtual function in Task called do() (maybe print() too, if you want to). Then just call that function, and you won't have to care (or print() if you want to show what data it has).
In that case, it would be better to define Task as an abstract class:
1
2
3
class Task{
    virtual void perform()=0;
};

This has the following side effects:
1. You can't create an instance of Task.
2. Classes derived from Task have to implement perform(), or they're also abstract classes and thus can't be created instances of.

Now you don't have to cast anything to perform the tasks, you can just loop through the vector:
1
2
for (size_t a=0;a<schedule.size();a++)
    schedule[a]->perform();
Ok, I wasn't being clear. But the purpose is to extract the information and do something to it, something like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

//if the first task is to study
if( typeid(schedule[0]).name()) == "class Study_Task")
{
   //find out which textbook to study
   Textbook object = dynamic_cast<Study_Task*>(schedule[0])->get_textbook();

   display_textbook_front_cover( object );
}
//if the first task is to watch a movie
else if( typeid(schedule[0]).name()) == "class Watch_Movie_Task")
{
   //find out which movie to watch
   Movie object = dynamic_cast<Watch_Movie_Task*>(schedule[0])->get_movie();

   display_movie_trailer( object );
}
Sounds to me like it can still be inside a perform() function. Or perhaps have both Textbook and Movie derive from a class Data.
It definitely doesn't seem like you need RTTI.
Last edited on
Ok, maybe I'm giving a really bad example of my situation, but no, I don't think I can put it inside the perform() function. I need to extract the information in the Task objects, combine it with other information from other places and process all these data. And no I can't make Textbook and Movie derive from a class since each class that is derived from Task is holding more than one piece of info. (like Textbook and hours in Study_Task)
Last edited on
If I'm understanding you right...it seems like you are using classes incorrectly. You shouldn't really be getting internal members out of them, as the classes are supposed to encapsulate and hide their data for the most part.
Right.

The whole point of abstraction is that it makes it so you can use the same code to work with any derived class. IE, to perform a task, you can just call Perform() instead of having to determine what type of task it is and do different things depending on the type.

It sounds like you just need to rethink your approach / change your design.

I need to extract the information in the Task object


You should hardly ever need to do this. It's most often avoided. If you have a Task, you should refrain from assuming it's a specific type of Task. After all, if you need to determine the exact type of task, it doesn't make much sense to abstract it in the first place.

combine it with other information from other places and process all these data


Why can't all of this be done in a Perform() function?

You'll have to write multiple versions of this code anyway (one for Movie data, one for Study data, etc). So you might as well write it inside the Movie/Study class. That's kind of the whole point of having a class.
Ok, it seems like you guys don't really understand unless I reveal my source code. what I'm actually trying to do is make an A.I. This is what my code looks like: (plz don't laugh at me if this looks stupid as I totally have no experience in this.)

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//This is an abstract base class
class Task
{
public:
	//constructor
	Task()
	{
	}
	
};

class moving_to_destination_Task : public Task
{
protected:
	Position destination;
 
	
public:
	//constructor
	moving_to_destination_Task(int x, int y):Task()
	{
		destination.x = x;
		destination.y = y;
	}
	Position get_destination() const
	{
		return destination;
	}
};

class attacking_Task : public Task
{
protected:
        //the enemy
	Combat_Character* target;
public:
	//constructor
	attacking_Task(Combat_Character* target_para) : Task()
	{
		if(target_para == NULL)
		{
			throw "Error!";
		}

		target = target_para;
	}
	Combat_Character* get_target() const
	{
		return target;
	}
};

class Combat_Character
{
private:
   //a bunch of other member variables here, including position, velocity and attack range
   list<Task*> schedule;
public:
   void receive_input()
   {
       if ( /*the user clicks on an enemy*/ )
       {
            if ( /*enemy is not within the attack range*/ )
            {
                //create a moving_to_destination_Task
                schedule.push_back( new moving_to_destination_Task(x, y) );
             }
             
             //create an attacking_Task
             schedule.push_back( new attacking_Task( /*pointer to enemy*/) );
        }
    }
    
  void think()
  {
       //if the current task is to attack
        if(  typeid( *( schedule[0] ) ).name() == "class attacking_Task")
        {
             //find out the position of the enemy
            Position enemy_position = dynamic_cast<attacking_Task*>(schedule[0])->get_target()->get_position();
             if (/*the enemy position is out of the attack range of Combat Character*/)
             {
                  //need to change schedule by moving toward enemy first
                 schedule.push_front( new moving_to_destination_Task(enemy_position.x, enemy_position.y);
             }
        }
        //else if the current task is to move to destination
             //if the combat character has already reached destination. 
                    //delete the current task
                    delete dynamic_cast<moving_to_destination_Task*>(schedule[0]);
                    schedule.pop_front();
   }

  void perform_action()
    {
          //if the current task is to move to a destination
          if(  typeid( *( schedule[0] ) ).name() == "class moving_to_destination_Task")
          {
                //find out what's the destination
                Position destination = dynamic_cast<moving_to_destination_Task*>(schedule[0])->get_destination()
               //set the velocity in the correct direction
               set_velocity( destination ); //member function of Combat_Character
          }
          //else if the current task is to attack the enemy...
          //get the pointer to the enemy from the Task object....
          // blah blah blah.....
    }

};


Phew.... I hope that clarifies my situation for the last time. I know it's really troublesome to use dynamic_cast but I can't think of a better way.
Why can't think() and perform_action() be the same function? Then attacking_task does all the thinking in its perform() override, and moving_to_destination_Task does any calculation it needs in its perform() override.
I agree with helios and the others.

There's no need for dynamic cast here. Just have a pure virtual function do the path splitting (and have a virtual dtor so you don't have to downcast for your delete)

Here's my recommended changes:

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
class Task
{
protected:
    Combat_Character* owner;        // the owner of the task (ie, who is to perform it)
public:
	Task(Combat_Character* c)       // change the ctor to take it
	{
	    owner = c;
	}

    // give yourself a virtual dtor so you can safely delete Task* pointers
    // without downcasting
    virtual ~Task() { }
    
    // make a pure virtual Perform() action to have the appropriate action performed
    //  -- to respond to your 'Think' function below -- I have this function return a bool.
    //   it'll return 'true' if the task was completed / needs to be removed from the
    //   schedule and 'false' if the task is not complete yet and is still pending.
    //
    //  Note that this is very simplistic, and a better approach might be to return an enum
    //  which signals different responses to a task outcome.
    //
    //  An even better approach might be to return a Task or another kind of Response class
    //   that gives you more flexibility to determine another action to take.
    //
    //
    //
    //  The bool is simple for this example, though.  So while it might not be the best approach,
    //   it's what I'll use here.
    virtual bool Perform() = 0;
};

class moving_to_destination_Task : public Task
{
protected:
	Position destination;
 
	
public:
	//put the new member in the ctor, and pass it to Task's ctor
	moving_to_destination_Task(Combat_Character* c,int x, int y):Task(c)
	{
		destination.x = x;
		destination.y = y;
	}
	// get rid of this.  It's useless unless you have a moving_to_destination_Task pointer
	//  which you won't -- you'll only have abstract Task pointers
/*	Position get_destination() const
	{
		return destination;
	}*/
	
	// instead, just move the character from your perform function:
	virtual bool Perform()
	{
	    // it might make more sense to have Combat_Character move itself, so you
	    //  can pass the buck here:
	    return owner->Perform_Move(destination);
	}
};

class attacking_Task : public Task
{
protected:
	Combat_Character* target;   // the character he's attacking
public:
	// take the owner param here, too
	attacking_Task(Combat_Character* owner_para,Combat_Character* target_para) : Task(owner_para)
	{
		if(target_para == NULL)
		{
			throw "Error!";
		}

		target = target_para;
	}
	/*  // get rid of this
	Combat_Character* get_target() const
	{
		return target;
	}*/
	
	// Perform the action.  Like the above, it might make more sense to have
	//  Combat_Character do the actual attacking
	virtual bool Perform()
	{
	    return owner->Perform_Attack(target);
	}
};

class Combat_Character
{
private:
   //a bunch of other member variables here, including position, velocity and attack range
   list<Task*> schedule;
public:
   void receive_input()
   {
       if ( /*the user clicks on an enemy*/ )
       {
            if ( /*enemy is not within the attack range*/ )
            {
                //create a moving_to_destination_Task
                
                schedule.push_back( new moving_to_destination_Task(x, y) );
             }
             
             //create an attacking_Task
             schedule.push_back( new attacking_Task( /*pointer to enemy*/) );
        }
    }
    
    /* I agree with helios that the 'think' function seems unnecessary.
      so I deleted it here */

    // Perform the action
  void perform_action()
    {
        // perform the task from the schedule
        if( schedule[0]->Perform() )  // that's all you have to do!  No casting!
        {
            // if the task was completed.. remove it from the schedule
            delete schedule[0];  // works fine because we have a virtual dtor.  No need to cast
            schedule.pop_front();
        }
    }
    
    // Perform_Move
    bool Perform_Move(const Position& pos)
    {
        if( /* already at destination */ )
        {
            // kill velocity
            return true;  // done with the move, this task is complete
        }
        else
        {
            // set velocity
            return false; // not done with the move yet, keep task in schedule
        }
    }
    
    // Perform_Attack
    bool Perform_Attack(Combat_Character* target)
    {
        if( /* target is within range */ )
        {
            // attack the target
            return true;  // task complete
        }
        else
        {
            // otherwise, we're not in range
            // so move towards the target first
            schedule.push_back( new moving_to_destination_Task( this, x, y ) ); // ** NOTE **
            
            // then attack the target again
            schedule.push_back( new attack_Task( this,target ) );
            
            return true; // complete (sort of -- because we added a new attack task)
            
            /*
                *** NOTE ***
                since perform_action removes the front element of the schedule, you don't want to 
                push_front any tasks here.  That's why I push the new tasks to the end instead of pushing them to the front.
                
                Also beware that whenever you add/remove tasks from the schedule you may invalidate any iterators you're using
                (depending on the container you're using -- I'm guessing deque from your use of [0] and pop_front -- but you may
                want to consider using list instead for this very reason)
            */
        }
    }
};
Ok, but in the Perform_Attack function, if we're not in range, can I just push_front a new moving_to_destination_Task and return false?

I'm not sure if it's ok to get rid of the think function because think is to decide what to do and perform_action is to actually do it.

Anyway, is it ok if my code looks like this?
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//This is an abstract base class
class Task
{
protected:
       Combat_Character* owner;
public:
	//constructor
	Task(Combat_Character* c)       // change the ctor to take it
	{
	    owner = c;
	}
       virtual ~Task() { }

       virtual bool task_completed() = 0;
       //decide whether the schedule should change even though task is not completed
       virtual void change_schedule() = 0;
	virtual void Perform() = 0;  //here I change the return type to void

};

class moving_to_destination_Task : public Task
{
protected:
	Position destination;
 
public:
	//put the new member in the ctor, and pass it to Task's ctor
	moving_to_destination_Task(Combat_Character* c,int x, int y):Task(c)
	{
		destination.x = x;
		destination.y = y;
	}
   
        virtual bool task_completed()
        {
              //if the owner reached destination
               if( owner->get_position() == destination )
               {
                       return true;
                }
                else
                      return false;
         }
         virtual void change_schedule(){}
         virtual void Perform()
	{
	     owner->Perform_Move(destination);
	}
};

class attacking_Task : public Task
{
protected:
        //the enemy
	Combat_Character* target;
public:
	attacking_Task(Combat_Character* owner_para,Combat_Character* target_para) : Task(owner_para)
	{
		if(target_para == NULL)
		{
			throw "Error!";
		}

		target = target_para;
	}
	
	 virtual bool task_completed()
        {
               //actually, I want the combat character to keep attacking until the enemy is dead
               if( target->is_dead() )
               {
                       return true;
                }
                else
                      return false;
         }

        virtual void change_schedule()
       {
             owner->change_attack schedule( target );
        }
        virtual void Perform()
	{
	    owner->Perform_Attack(target);
	}

};

class Combat_Character
{
private:
   //a bunch of other member variables here, including position, velocity and attack range
   list<Task*> schedule;
public:
   void receive_input()
   {
       if ( /*the user clicks on an enemy*/ )
       {
            if ( /*enemy is not within the attack range*/ )
            {
                //create a moving_to_destination_Task
                schedule.push_back( new moving_to_destination_Task(x, y) );
             }
             
             //create an attacking_Task
             schedule.push_back( new attacking_Task( /*pointer to enemy*/) );
        }
    }
    
  void think()
  {
       //if the task is completed
       if( schedule[0]->task_completed() )
       {
             delete schedule[0];  
            schedule.pop_front();
       }
       else
       {
             //decide whether schedule should change even though task is not completed
             schedule[0]->change_schedule();
        }

   }
  
  void change_attack schedule( Combat_Character* target )
  {
        if (/*the enemy is out of range*/)
        {
             //get the enemy's position and set the task
            schedule[0].push_back( new moving_to_destination_Task( this, target->get_x(), target->get_y() ) );
         }
   }
  void perform_action()
    {
          schedule[0]->Perform();
    }
  // Perform_Attack
    void Perform_Attack(Combat_Character* target)
    {
         // just attack the target
    }

};

Last edited on
Topic archived. No new replies allowed.