How you change an object from base class to derived class?

Hello, I have the following classes
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
class Rock {
public:
  Rock() { goldAmount = rand() % 100 + 1; price = 10; };
  int revealContent() { return goldAmount; };
  virtual void checkPrice() { cout << price << endl; };
protected:
  int price;
private:
  int goldAmount;
};

class ValuableRock : public Rock {
public:
  ValuableRock() { price = 100; }; // Try to set the new price in constructor
};

class PreciousRock : public Rock {
public:
  PreciousRock() { }; // Try changeing new price in overloaded function instead
  virtual void checkPrice() { cout << price * 100 << endl; };
};

void main() {
  srand((unsigned int)time(NULL));
  Rock **bagOfRocks = new Rock*[3];
  for (int i = 0; i < 3; i++) {
    bagOfRocks[i] = new Rock;	// 3 random rocks in a bag
  }
  // just crack open some of the rocks
  // first method
  if (bagOfRocks[1]->revealContent() <= 70) {  // not many gold inside
  //... something I dont know how to write but I tried this
    bagOfRocks[1] = (ValuableRock*)bagOfRocks[1];
  } else if (bagOfRocks[1]->revealContent() > 70) {  // many gold inside
  //... again, I dont know how to write
    bagOfRocks[1] = (PreciousRock*)bagOfRocks[1];
  }
  // second method
  if (bagOfRocks[2]->revealContent() <= 70) {
    //... something I dont know how to write but I also tried this
    delete bagOfRocks[2];
    bagOfRocks[2] = new ValuableRock;	// but the content of rock is rand()
                                        // may not necessary <= 70 this time
  } else if (bagOfRocks[2]->revealContent() > 70) {
    //... again, I dont know how to write
    delete bagOfRocks[2];
    bagOfRocks[2] = new PreciousRock;	// but the content of rock is rand()
                                        // may not necessary > 70 this time
  }
  for (int i = 0; i < 3; i++) {
    bagOfRocks[i]->checkPrice();// I expect it to output 10 100or1000 100or1000
  }
  for (int i = 0; i < 3; i++) {
    delete bagOfRocks[i];
    bagOfRocks[i] = NULL;
  }
  delete [] bagOfRocks;
  bagOfRocks = NULL;
}


Here I have a base class Rock, which can have random number of gold amount.
If after a rock object is created, I check how many gold is inside
If there is less than 71 gold, I have to change it to a ValuableRock
If there is more than 70 gold, I have to change it to a PreciousRock
Rocks uncracked is still ordinary Rock
but once cracked, it has to be either ValuableRock or PreciousRock
and by calling checkPrice(), it should print out the corresponding price

I tried to type cast the pointer type (method 1) but it does not call the corresponding function
then I tried to delete the object (method 2) and create an appropriate object using the same base class pointer, this way the correct checkprice is called, but since it creates a new object, the gold amount is another random one

Can anyone provide a good solution for me so that
1. I can access all kind of rock classes using an array of pointers
2. gold must rand() in the base class, not in the main() then assign appropriate class of xxxRocks construction because not all Rocks are cracked at time of creation
3. class hierarchy Rock, ValuableRock, PreciousRock must be maintained. (I know there is solution that can be done within a single class)

Thank you everyone!
bump! Hope someone can help! This is stalling me
In the context of OO modelling, an object can't turn into something else. It's more a matter of an object being able to do things and a derived object being able to do all that stuff and a bit more.

I think your concepts aren't quite right. It may be more useful to describe this as a state change, with each state having it's own properties or behaviours.

Your Rock is just a Rock. However, it may be thought of as usless, valuable or precious. For each of these states, there may be a (possibly different) set of properties, like value, amount of gold and so on/
Last edited on

If after a rock object is created, I check how many gold is inside
If there is less than 71 gold, I have to change it to a ValuableRock
If there is more than 70 gold, I have to change it to a PreciousRock


There is a fundamental flaw in this logic. You cannot change an object's type once it has been created. If you need to do this, you must instead generate a random number of gold first, and create the derived type based on how much gold there is.

You can do this with a "named ctor" (not really a ctor -- instead it's a static function that creates an object dynamically). 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

class Rock
{
public:
    static Rock* CreateRandomRock();
    
    // ...
};

// define ValuableRock and PreciousRock here

Rock* Rock::CreateRandomRock()
{
    int gold = rand() % 100 + 1;

    Rock* ret;
    if(gold <= 70)  ret = new ValuableRock;
    else            ret = new PreciousRock;
    
    ret->goldAmount = gold;
    return ret;
}

// then to make a random kind of rock:

  Rock* somekindarock = Rock::CreateRandomRock();



EDIT --

It might sound like what I said conflicts with kbw. Here's some clarification.

Every kind of object in C++ has 1 "real" type. IE: if you make an object a PreciousRock, then it is a PreciousRock and there's no way to change it.

However though the use of polymorphism you can treat objects like other objects, without actually changing them. For example because PreciousRock is derived from Rock, you can treat all PreciousRocks like Rocks, even though they're really PreciousRocks. That's a 1-way street though, you can't treat all Rocks like PreciousRocks (all poodles are dogs, but not all dogs are poodles).

Anyway once you create an object, that determines that object's one and only type. It cannot be changed from there -- but it might be able to be treated different ways.
Last edited on
i havent read the whole post but what i have figured out is you are trying to downcast or upcast..

if thats correct.. cant we use dynamic_cast or reinterpret_cast in your case??

You can't downcast to a lower point in the hierarchy than where the class was created. He's creating classes of type Parent and wanting to "convert" them to type Child after they've been created.
closed account (z05DSL3A)
cppstudent, I have not got the time to do a fully formed reply but hope this helps:
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
#include<iostream>
#include<time.h>
 
class ValuableRock;
class PreciousRock;
 
class Rock 
{
public:
    Rock(int gold = 0)
        :goldAmount(gold), price(10), cracked(false)
    {}
 
    int revealContent() 
    {   
        cracked = true;
        return goldAmount; 
    }
 
 
    virtual int Price() 
    {return 10;}
    
    
    //Factory Method
    static Rock *makeRock();
 
protected:
    int price;
    int goldAmount;
    bool cracked;
};
 
class ValuableRock : public Rock 
{
public:
    ValuableRock(int gold = 70)
    :Rock(gold)
    {
        price = 100;
    };
 
    int Price() 
    {
        if(cracked)
            return price;
        else
            return Rock::Price();   
    }
 
};
 
class PreciousRock : public Rock 
{
public:
    PreciousRock(int gold = 71) 
    :Rock(gold)
    { 
        price = 1000;
    } // Try changeing new price in overloaded function instead
  
    int Price() 
    {
        if(cracked)
            return price;
        else
            return Rock::Price();   
    }
};
 
Rock *Rock::makeRock()
{
    
    int goldContent = rand() % 100 + 1;
 
    if (goldContent < 71)
        return new ValuableRock(goldContent);
    else
        return new PreciousRock(goldContent);
}
 
int main(int argc, char *argv[])
{
    srand((unsigned int)time(NULL));

    using std::cout;
    using std::endl;
 
    Rock **bagOfRocks = new Rock*[3];
  
    for (int i = 0; i < 3; i++) 
    {
        bagOfRocks[i] = Rock::makeRock(); // 3 random rocks in a bag
    }
 
    for (int i = 0; i < 3; i++) 
    {
        cout << "Rock [" << i << "] Price: ";
        cout << bagOfRocks[i]->Price() << " reveals ";   
        cout << bagOfRocks[i]->revealContent() << " gold and is now worth: ";
        cout << bagOfRocks[i]->Price() << endl;       
    }
    
    return 0;
}

NB: Un-compiled, Un-tested code for general guidance.

Edit: Changed code should compile and run
Edit2:Disch, Sorry I somehow managed to totally overlook your first post.
Last edited on
Disch:



okok..
you mean this:

1
2
3
4
5
bagOfRocks[i] = new Rock;   // 3 random rocks in a bag

//some code......

bagOfRocks[1] = (ValuableRock*)bagOfRocks[1];


:o
Thank you all of you.
I also thought of using static_cast
but then I realise the pointer was a base class one
static_cast would have helped if I have a derived class pointer

I guess this static class object creator method within the base class is a common solution by all the Pro.

But I think one draw back of this solution is that the base class must have knowledge about all of the dervied classes, which is not often the case.

I have another idea to use constructor of derived class that takes in a base class as variable.
Something like
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
class Rock {
//...
protected:
  int goldAmount;
};

class ValuableRock : public Rock {
public:
  ValuableRock(Rock* temp) {
    goldAmount = temp->revealContent();
    price = 100;
    delete temp;
    temp = NULL;
  };
  void checkPrice() {
    cout << price * 99 << endl; // for testing purpose
  };
//...
};
//...

bagOfRocks[i] = new Rock;
if (bagOfRocks[i]->revealContent() <= 70) {
  bagOfRocks[i] = new ValuableRock(bagOfRocks[i]);
}


But I found that even this works (by setting a new price 100), the base class pointer bagOfRocks[i] is not calling the checkPrice() function of the derived class ValuableRock (although it is assigned a new ValuableRock object address and the constructor of ValuableRock is called). I still dont know how to make the base class pointer bagOfRocks[i] to call the functions of a derived class.

Feel free to comment on this. Thank you again!
Topic archived. No new replies allowed.