Specialised data in derived class

I'm newish to c++ and am trying to modify an existing c program that has been partially converted to c++. I want to create a derived class with some extra data, but have been trying to figure out the best way to do it and came across this unexpected (for me) behaviour. The simplified code here reproduces it. The existing program uses the first instantiation method (X). Can someone help me understand what is going on? My intention is to minimise changes to the Base class but allow Derived to expose independent extra data (i.e., c), with this instantiation approach. In addition, can anyone suggest the simplest virtual approach?

Thanks!

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
// Unexpected initialisation
#include <iostream>

using namespace std;



// Interfaces

class Base

{

public:

    // Constructors

    Base( int a, int b );

    // Destructor

    virtual ~Base();

    // Methods

    int a, b, c;

    virtual void info ( std::string prefix );

};



class Derived: public Base

{

public:

    // Constructors

    Derived( int a, int b, int c );

    // Destructor

    virtual ~Derived();

    // Methods

    int a, b, c;

    void info ( std::string prefix );

};



// Base

// Constructors

Base::Base ( int a, int b )

:

    a(a),

    b(b)

{}

// Destructor

Base::~Base () {}

// Methods

void Base::info ( std::string prefix )

{

    cout << prefix << "a = " << a << endl;

    cout << prefix << "b = " << b << endl;

    return;

}



// Derived

// Constructors

Derived::Derived ( int a, int b, int c )

:

    Base( a, b ),

    c(c)

{}

// Destructor

Derived::~Derived () {}

// Methods

void Derived::info ( std::string prefix )

{

    Base::info( prefix );

    cout << prefix << "c = " << c << endl;

    return;

}



int main () {

  Base *X = new Derived( 10, 2, 3 );

  cout << "X defined as Base and allocated as Derived." << endl;

  X->info("X->info: ");

  cout  << "X->a = " << X->a << endl

        << "X->b = " << X->b << endl

        << "X->c = " << X->c << endl;



  Derived *Y = new Derived( 10, 2, 3 );

  cout << "Y defined as Derived and allocated as Derived." << endl;

  Y->info("Y->info: ");

  cout  << "Y->a = " << Y->a << endl

        << "Y->b = " << Y->b << endl

        << "Y->c = " << Y->c << endl;

  return 0;

}


Output:

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
X defined as Base and allocated as Derived.

X->info: a = 10

X->info: b = 2

X->info: c = 3

X->a = 10

X->b = 2

X->c = 0

Y defined as Base and allocated as Derived.

Y->info: a = 10

Y->info: b = 2

Y->info: c = 3

Y->a = 980379246

Y->b = 32

Y->c = 3
You have declared the member variables {a, b, c} in both Base and Derived. You probably want {a, b} in Base, and {c} in Derived. Remove the rest!

Note that after you do this, the code will not compile, because Base doesn't have a member named "c". This is correct. Fix it by removing line 138. You will still see the value of "c" when you call X->info();
Last edited on
Yeh, however I want to access c directly (i.e. X->c). Like you say, c was included in Base because the compiler insisted upon it :)

I can get around this by turning Base into an abstract class to retrieve private data in Derived (below), which works but is only practical for a small number of Derived classes. The program I'm trying to modify, has Base with a whole bunch of Derived's, and the abstract approach means I would have to go through all existing Derived's and add the retriever functions for their private data, imposing this burden on all future Derived additions. I'm starting to see how the existing set up is pretty poor and am looking at how templates can help me more elegantly arrange things.

By the way, another approach I tried was a downcast using static_cast. This works, but I ran into a limitation (potentially me). I have a function which accepts a Base class pointer that points to one of a dozen or so possible Derived class instances, but the function only wants to perform work if Derived is one of three types (the type index is in Base). Remember, I'm trying to work with an existing code structure here. Downcasting works but if I have branching statements:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int my_function( Base &data, double foo )  
{
    if (data.type_index == DERIVED_TYPE_1){
        DerivedType1 *my_data = static_cast<DerivedType1*>(data);
        <work1>
    }
    if (data.type_index == DERIVED_TYPE_2){
        DerivedType2 *my_data = static_cast<DerivedType2*>(data);
        <work2>
    }
    if (data.type_index == DERIVED_TYPE_1){
        DerivedType3 *my_data = static_cast<DerivedType3*>(data);
        <work3>
    }

    <work0>
    return result;
}


Then the down-cast pointers *my_data only have scope within the if statements and to do <work0> on them I'd need to turn <work0> into another function, apparently. I suppose maybe there is a way to assign *my_data dynamically on the heap, but I don't know how.

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

using namespace std;



// Interfaces

class Base

{

public:

    // Constructors

    Base( int a, int b );

    // Destructor

    virtual ~Base();

    // Methods

    int a, b;

    virtual void info (std::string prefix);

    virtual int c (void) =0;

};



class Derived: public Base

{

    int _c;

public:

    // Constructors

    Derived( int a, int b, int _c );

    // Destructor

    virtual ~Derived();

    // Methods

    int a, b;

    void info (std::string prefix);

    inline int c (void);

};



// Base

// Constructors

Base::Base ( int a, int b )

:

    a(a),

    b(b)

{}

// Destructor

Base::~Base () {}

// Methods

void Base::info ( std::string prefix )

{

    cout << prefix << "a = " << a << endl;

    cout << prefix << "b = " << b << endl;

    return;

}



// Derived

// Constructors

Derived::Derived ( int a, int b, int c )

:

    Base( a, b ),

    _c(c)

{}

// Destructor

Derived::~Derived () {}

// Methods

inline int Derived::c (void) { return _c; }

void Derived::info ( std::string prefix )

{

    Base::info( prefix );

    cout << prefix << "c = " << _c << endl;

    return;

}



int main () {

    Base *X = new Derived( 10, 2, 3 );

    //Derived *XX = static_cast<Derived*>(X);

    cout << "X defined as Base and allocated as Derived." << endl;

    X->info("X->info: ");

    cout  << "X->a      = " << X->a << endl

          << "X->b      = " << X->b << endl

          << "X->c()    = " << X->c() << endl;




  return 0;

}

Output:
X defined as Base and allocated as Derived.

X->info: a = 10

X->info: b = 2

X->info: c = 3

X->a      = 10

X->b      = 2

X->c()    = 3
To be more specific, in the code below, unblanking the DoWork2 line yields " error: ‘newdata’ was not declared in this scope" on compile . It would be a _whole_ lot easier if I could just get newdata out of the if scope, rather than trying to be a hero with templates and someone elses code. Can I just do some juggling of pointers or something?

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

using namespace std;



// Interfaces

class Base

{

public:

    // Constructors

    Base( int a, int b );

    // Destructor

    virtual ~Base();

    // Methods

    int a, b;

    virtual void info (std::string prefix);

};



class Derived: public Base

{

public:

    // Constructors

    Derived( int a, int b, int c );

    // Destructor

    virtual ~Derived();

    // Methods

    int a, b, c;

    void info (std::string prefix);

};



// Base

// Constructors

Base::Base ( int a, int b )

:

    a(a),

    b(b)

{}

// Destructor

Base::~Base () {}

// Methods

void Base::info ( std::string prefix )

{

    cout << prefix << "a    = " << a << endl;

    cout << prefix << "b    = " << b << endl;

    return;

}



// Derived

// Constructors

Derived::Derived ( int a, int b, int c )

:

    Base( a, b ),

    c(c)

{}



// Destructor

Derived::~Derived () {}

// Methods

void Derived::info ( std::string prefix )

{

    Base::info( prefix );

    cout << prefix << "c()  = " << c << endl;

    return;

}



int DoWork( Base* );

int DoWork( Base *data )

{

    if ( data->a == 10 )

    {

        Derived *newdata = static_cast<Derived*>(data);

        cout << "DoWork1: c = " << newdata->c << endl;

    }

    //cout << "DoWork2: c = " << newdata->c << endl;

    return 0;

}



int main () {

    Base *X = new Derived( 10, 2, 3 );

    DoWork(X);

    return 0;

}
Last edited on
BTW, declaring newdata in the outer scope works,

1
2
3
4
5
6
7
8
9
10
11
int DoWork( Base *data )
{
    Derived *newdata;
    if ( data->a == 10 )
    {
        Derived *newdata = static_cast<Derived*>(data);
        cout << "DoWork1: c = " << newdata->c << endl;
    }
    cout << "DoWork2: c = " << newdata->c << endl;
    return 0;
}   


but what to do if there are multiple possible types for newdata? e.g.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int DoWork( Base *data )
{
    
    if ( data->a == 10 )
    {
        Derived *newdata = static_cast<Derived*>(data);
        cout << "DoWork1: c = " << newdata->c << endl;
    }
    if ( data->a == 11 )
    {
        Derived2 *newdata = static_cast<Derived2*>(data);
        cout << "DoWork1: c = " << newdata->c << endl;
    }
    if ( data->a == 12 )
    {
        Derived3 *newdata = static_cast<Derived3*>(data);
        cout << "DoWork1: c = " << newdata->c << endl;
    }
    cout << "DoWork2: c = " << newdata->c << endl;
    return 0;
}   


All three Derived types have a data member named c that DoWork2 wants to access. If DoWork2 was bundled into another function that was called within each if statement, I suppose it would need to be a template function, but is there another way just to somehow wrangle the pointer?
Last edited on
You're trying to do things with inheritance that doesn't make sense. If some derived classes have a common property ("c"), then maybe they should have a common base class that defines this property.

It's not possible to give you any better advice without having the complete picture about your design, because this is definitely a design question. Although you're trying to turn it into an implementation issue.

The things you're trying to do can't be done, and they can't be done for a reason. It's just not how it works.

For more information, read this discussion:
http://cplusplus.com/forum/general/1212/
Thanks, yeh I totally don't like using hacks like casting etc, but I got it working putting calls to a single virtual function in the "DoWork1" lines. In this case I can't/don't want to redesign the bodgy code. But I think I understand what you're saying about design v implementation. Design seems to be much more important in c++ than c.

Cheers :)
Topic archived. No new replies allowed.