Passing abstract class by reference

Dec 18, 2010 at 1:21am
Hi everyone,

I've been experimenting with using Abstract Base Class references as parameters to my functions, and have come across a behaviour that I can't understand!

As a simple example, I've created a Region abstract base class, and two derived classes: Square and Circle. They each have a say_hello() method that returns a string saying "I'm a ___" + encapsulated data.

The method works when called using the variable names, e.g.
Square sq(10,20);
cout << sq.say_hello();

But not when I use a function that takes two objects as references like this:
const char * greetings(Region & r1, Region & r2).

This is best demonstrated by a small compilable example. The code is at the end of this post, the following is example output.


Creating: square with side_length=10, val=20
: circle with centre=5, radius=12, val=30

sq.say_hello() => Hello, I am a square. [10]
sq.say_hello() => Hello, I am a circle. [5, 12]

Now try greetings function.. greetings(sq,circ) =>
inside greetings() --> r1 address: 0xbf813d74 and r2 address: 0xbf813d64
r1 says: Hello, I am a square. [10] || r2 says: Hello, I am a square. [10]

Now try greetings function.. greetings(circ,sq) =>
inside greetings() --> r1 address: 0xbf813d64 and r2 address: 0xbf813d74
r1 says: Hello, I am a circle. [5, 12] || r2 says: Hello, I am a circle. [5, 12]


The expected output is for greetings(sq,circ) is:


Now try greetings function.. greetings(sq,circ) =>
inside greetings() --> r1 address: 0xbf813d74 and r2 address: 0xbf813d64
r1 says: Hello, I am a square. [10] || r2 says: Hello, I am a circle. [5, 12]


Thanks for any help!

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
#include <iostream>
#include <sstream>


//CLASSES -------------------V-------------------------V------------------------------V
class Region
{
private:
    //datum to aid in understanding which object is which, etc.
    int value;
public:
    Region(int val) {value = val;}
    virtual ~Region() {}
    virtual const char * say_hello() const = 0; //says e.g. "i'm a square" + encapsulated data
};



//Circle class derived from ABC Region
class Circle : public Region
{
private:
    //some data to aid in understanding which object is which, etc. 
    int centre;
    int radius;
public:    
    Circle(int cent, int rad, int val) : Region(val) {centre = cent; radius = rad;}
    virtual ~Circle() {}
    virtual const char* say_hello() const;
};



//Square class derived from ABC Region
class Square : public Region
{
private:
    //datum to aid in understanding which object is which, etc.
    int side_length;
public:
    Square(int side_len, int val) : Region(val) {side_length = side_len;}
    virtual ~Square() {}
    virtual const char* say_hello() const;
};



//-----------------------member defs--- v - v

const char * Circle::say_hello() const
{
    std::stringstream ret_str;
    ret_str << "Hello, I am a circle. [" << centre << ", " << radius << "]";
    return ret_str.str().c_str();
}

const char * Square::say_hello() const
{
    std::stringstream ret_str;
    ret_str << "Hello, I am a square. [" << side_length << "]";
    return ret_str.str().c_str();
}

//END CLASSES ----------------^-------------------------^---------------------------^



//prototype
const char * greetings(Region &r1, Region & r2);




//MAIN ------V


int main()
{
    using std::cout;
    using std::endl;
    
    Square sq(10,20);
    Circle circ(5,12,30);
    
    cout << "Creating: square with side_length=10, val=20" << endl;
    cout << "        : circle with centre=5, radius=12, val=30" << endl << endl;
    
    cout << "sq.say_hello() => " << sq.say_hello() << endl;
    cout << "circ.say_hello() => " << circ.say_hello() << endl << endl;
    
    cout << "Now try greetings function.. greetings(sq,circ) => " << endl;
    cout << greetings(sq,circ) << endl << endl;
    
    cout << "Now try greetings function.. greetings(circ,sq) => " << endl;
    cout << greetings(circ,sq) << endl << endl;

    return 0;
}


const char * greetings(Region & r1, Region & r2)
{
    std::cout << "inside greetings() --> r1 address: " << &r1 << "    and r2 address:    " << &r2 << std::endl;
    std::stringstream ret_str;
    ret_str << "r1 says: " << r1.say_hello() << " || r2 says: " << r2.say_hello();
    return ret_str.str().c_str();
}


Dec 18, 2010 at 1:24am
Also: My goal is to use the abstract base class as the parameter because this way I can avoid having multiple methods for all of the derived classes. A common goal, I'm know. What is the standard way to do this in C++? Am I on the wrong track, perhaps?

Thanks,

Tom.
Dec 18, 2010 at 2:09am
The problem here is that the return values of your functions point to garbage.
ret_str is a temporary object that is destroyed when the function returns.
As a result, ret_str.str().c_str() (the return value of your function) can point to anything.
You were lucky to even get correct results for sq.say_hello() and circ.say_hello()

You can easily fix this by #including <string>
and replacing const char * with std::string
and ret_str.str().c_str() with ret_str.str()

http://codepad.org/6KCYU1zX
Last edited on Dec 18, 2010 at 2:28am
Dec 18, 2010 at 2:22am
Ohh! Thank you :-D
Topic archived. No new replies allowed.