problems with inheritance and protected attributes

  • Thread starter Lucas Kanebley Tavares
  • Start date
L

Lucas Kanebley Tavares

Hey all,

I'm working on a project with a layout similar to this:

class A {
protected:
A *next;
int i;
public:
void m(A *a);
};

class B: public A {
public:
void m(B *b);
}

where both A and B are linked lists, but each one of its own type, and
m is a method that manipulates A's 'next' attribute, for instance:
A::m(A *a) {
a->next = next;
i++;
}

as the behavior would be exactly the same for B, I did:
B::m(B *b) {
A::m(b);
}

But that gives me errors on compiling whenever method B::m, tries to
access a protected member of class A (for instance incrementing i).
I've tried declaring a new variable i to class B, but that just causes
both A::i and B::i to exist, and A increments A::i, while whenever
someone reads from B, it reads B::i. Which is obviously wrong.

Does anybody know how I can fix this? Did anybody even understand what
I just asked? I know I kinda lost me. :p

Any help would be greatly appreciated,
Lucas
 
L

Lucas Kanebley Tavares

I'm sorry, working hard for long hours can get you paranoid. I just
noticed that that part of the code worked. What I'm getting is a:

wwperiod.hpp: In member function 'virtual double
ZWPeriod::getMinimalCost()':
wwperiod.hpp:50: error: 'DATATABLE* WWPeriod::resTable' is protected
zwperiod.cpp:46: error: within this context
(and the same error for a lot of other attributes in the same method).

Where I have something like (posting real code so I can't mess up like
last time):

class WWPeriod {
public:
virtual double getMinimalCost();
protected:
DATATABLE *resTable;
};

class ZWPeriod: public WWPeriod {
public:
virtual double getMinimalCost();
};

Where the line giving the error is:
zwperiod.cpp:46: resTable[0].tempCost = prev->resTable[0].tempCost +
prev->holdCost;

I know resTable is protected in WWPeriod, but that is because I want
it to be visible from ZWPeriod, and not to anyone else. If I re-
declare those attibutes in ZWPeriod I still get the same errors.

Could the error be related to the method being virtual? (it's the only
virtual method in that class, and it's the only place where I'm
getting those errors).

Again, any help would be very appreciated,
Lucas
 
A

Alf P. Steinbach

* Lucas Kanebley Tavares:
I'm working on a project with a layout similar to this:

class A {
protected:
A *next;
int i;
public:
void m(A *a);
};

class B: public A {
public:
void m(B *b);
}

where both A and B are linked lists, but each one of its own type, and
m is a method that manipulates A's 'next' attribute, for instance:
A::m(A *a) {
a->next = next;
i++;
}

as the behavior would be exactly the same for B, I did:
B::m(B *b) {
A::m(b);
}

But that gives me errors on compiling whenever method B::m, tries to
access a protected member of class A (for instance incrementing i).

It would be nice if you posted the actual offending code, but happily
this "problem" is not uncommon, so it is reasonable to assume you have
something like

B::m( B* b )
{
A::m( b );
next->i = 666;
}

That will cause the compiler to complain, because while B can access
protected A features on B objects, and on objects of classes publicly
derived from B, if it could do it could access protected A features on A
objects it could also access protected A features on Z-objects, where Z
is a class I have derived from A.

So in that case "protected" wouldn't be any protection: you could access
protected A features of my Z instances simply by deriving a class from A.

So C++ does not allow that.

I've tried declaring a new variable i to class B, but that just causes
both A::i and B::i to exist, and A increments A::i, while whenever
someone reads from B, it reads B::i. Which is obviously wrong.

Does anybody know how I can fix this?

Without knowing what you're trying to achieve it's difficult to fix it.

However, the above explains what the problem most likely is, and the fix
should involve not accessing protected A features on A objects down in
class B, but up in class A -- or else let those objects be B objects.

On the third hand, a little redesign is called for anyway, and it may be
that that also fixes your problem.

The reason a little redesign is called for: with the current design you
can't traverse the list and access the objects as B objects without
casting down from A* to B*.

And casting is ungood, because it's so easy to get wrong.

It seems that class A is a generic node-type, just a Linkable, and that
the idea is to add some data and behavior to each node down in class B,
serving as a full-blown Node.

Perhaps someone will suggest templatizing class A, but that will give
you code that's difficult to work with, in particular completely
indeciphericable kilometer-long error messages from the compiler.

So I suggest a simple forward declaration, and replacing the inheritance
with containment, where here class Data represents the stuff added in B:

class Data;

class Linkable
{
private:
Data* myData;
Linkable* myNext;

void linkIntoNextField( Linkable*& aNextField )
{
myNext = aNextField;
aNextField = this;
}
public:
Linkable( Data* data ): myData( data ), myNext( 0 ) {}

~Linkable() { delete myData; }

Data& data() { return *myData; }

Linkable* next() { return myNext; }

Linkable* insertAfter( Linkable* p )
{
linkIntoNextField( p->myNext );
return this;
}

Linkable* unlinkNext()
{
Linkable result = myNext;
if( myNext != 0 )
{
myNext = myNext->next();
}
return result;
}
};

class Data { ... };

class List { ... };

One interesting exercise is how many 'const' keywords it's possible to
sprinkle in this code. It should ideally be done.

Did anybody even understand what
I just asked?

Possibly.

I assumed that this is a school project where you're learning about
pointers etc., and where templating would just be added complication,
not a real-world project where you'd just use std::list.


I know I kinda lost me. :p

Any help would be greatly appreciated,

You're welcome.
 
L

Lucas Kanebley Tavares

Thanks for your suggestions, but that didn't do it for me. I however
do understand what was wrong with the code earlier (I had fixed it
without knowing how).

My real problem now is the one that was posted on the second e-mail,
sorry about the mess.

This project is a research project, and it involves dynamic
programming, so given the recursive nature of the project I wanted to
be able to transverse the list in a easier/faster way than going
through iterators (and avoiding some of the overhead std::list
causes).

The Base class represents a production planning period (of a
production line) and has its data set, a method for calculating the
total cost up to the current point, a method for adding periods to the
list and the list pointers. The Derived class, adds functionality to
the base class, it has a few other attributes and overrides the method
to calculate the total cost, but most of the functionality remains the
same.

As I said, I'm having trouble accessing the protected data of the base
class from within the overridden virtual method of the derived one.
Where I get the error already posted:

--- Compiler error
wwperiod.hpp: In member function 'virtual double
ZWPeriod::getMinimalCost()':
wwperiod.hpp:50: error: 'DATATABLE* WWPeriod::resTable' is protected
zwperiod.cpp:46: error: within this context
(and the same error for a lot of other attributes in the same
method).

--- Classes layout
class WWPeriod {
public:
virtual double getMinimalCost();
protected:
DATATABLE *resTable;

};

class ZWPeriod: public WWPeriod {
public:
virtual double getMinimalCost();

};

--- Line giving the presented error:
zwperiod.cpp:46: resTable[0].tempCost = prev->resTable[0].tempCost +
prev->holdCost;

---

I'm a reasonably good C programmer, but although I know C++ syntax,
I've never really had any serious projects in C++, so I'm
unexperienced.

Thank you for your reply, but my problem remains.
Any more suggestions for a n00b? :D

Lucas

PS: I am using STL on other parts of the project, I just wanted to
avoid it in that specific part.
 
A

Alf P. Steinbach

* Lucas Kanebley Tavares:
It would be nice if you posted the actual offending code, but happily
this "problem" is not uncommon, so it is reasonable to assume you have
something like

B::m( B* b )
{
A::m( b );
next->i = 666;
}

That will cause the compiler to complain, because while B can access
protected A features on B objects, and on objects of classes publicly
derived from B, if it could do it could access protected A features on A
objects it could also access protected A features on Z-objects, where Z
is a class I have derived from A.

So in that case "protected" wouldn't be any protection: you could access
protected A features of my Z instances simply by deriving a class from A.

So C++ does not allow that.

--- Compiler error
wwperiod.hpp: In member function 'virtual double
ZWPeriod::getMinimalCost()':
wwperiod.hpp:50: error: 'DATATABLE* WWPeriod::resTable' is protected
zwperiod.cpp:46: error: within this context
(and the same error for a lot of other attributes in the same
method).

--- Classes layout
class WWPeriod {
public:
virtual double getMinimalCost();
protected:
DATATABLE *resTable;

};

class ZWPeriod: public WWPeriod {
public:
virtual double getMinimalCost();

};

--- Line giving the presented error:
zwperiod.cpp:46: resTable[0].tempCost = prev->resTable[0].tempCost +
prev->holdCost;

---

I'm a reasonably good C programmer, but although I know C++ syntax,
I've never really had any serious projects in C++, so I'm
unexperienced.

Thank you for your reply, but my problem remains.
Any more suggestions for a n00b? :D

Only to reconsider the above earlier suggestion, which is now confirmed.

The (or at least one) problem is the attempted access via prev->, as I
surmised it would be. Why that is not permitted is explained above.
How to possibly fix it, explained above -- and perhaps also take a
look at the suggested redesign. In addition to the earlier advice, with
concrete code I can now make the suggestion to introduce public inline
const accessors tempCost() and holdCost() in the base class, and change
the naming convention for data members to e.g. myTempCost or tempCost_.

By the way, please don't top-post (rearranged, see the FAQ please), and
please don't quote signatures.

Cheers, & hth.,

- Alf
 
L

Lucas Kanebley Tavares

* Lucas Kanebley Tavares:


--- Compiler error
wwperiod.hpp: In member function 'virtual double
ZWPeriod::getMinimalCost()':
wwperiod.hpp:50: error: 'DATATABLE* WWPeriod::resTable' is protected
zwperiod.cpp:46: error: within this context
(and the same error for a lot of other attributes in the same
method).
--- Classes layout
class WWPeriod {
public:
virtual double getMinimalCost();
protected:
DATATABLE *resTable;

class ZWPeriod: public WWPeriod {
public:
virtual double getMinimalCost();

--- Line giving the presented error:
zwperiod.cpp:46: resTable[0].tempCost = prev->resTable[0].tempCost +
prev->holdCost;

I'm a reasonably good C programmer, but although I know C++ syntax,
I've never really had any serious projects in C++, so I'm
unexperienced.
Thank you for your reply, but my problem remains.
Any more suggestions for a n00b? :D

Only to reconsider the above earlier suggestion, which is now confirmed.

The (or at least one) problem is the attempted access via prev->, as I
surmised it would be. Why that is not permitted is explained above.
How to possibly fix it, explained above -- and perhaps also take a
look at the suggested redesign. In addition to the earlier advice, with
concrete code I can now make the suggestion to introduce public inline
const accessors tempCost() and holdCost() in the base class, and change
the naming convention for data members to e.g. myTempCost or tempCost_.

By the way, please don't top-post (rearranged, see the FAQ please), and
please don't quote signatures.

Cheers, & hth.,

Thank you, it worked and now I understand it better. I'll probably
restructure the code when I've got some free time. And sorry about the
top-posting, and signature quoting, I didn't know the netiquette of
this place.

Concerning the accessors, they do exist in the form of getters. And
now to think about it, considering they're inline I might just change
the code to use them.

A few questions though:
do I need to explicitly put the 'inline' keyword in the function
declarations, or just by adding the code directly to the class
declarations they're already inlined by g++? Also, why should I change
the naming convention to the data members? How would that increase
readability?

Sorry to bother once more, and thanks for your time :)
Lucas

PS: hth?
 
B

BobR

Lucas Kanebley Tavares wrote in message...
Concerning the accessors, they do exist in the form of getters. And
now to think about it, considering they're inline I might just change
the code to use them.

A few questions though:
do I need to explicitly put the 'inline' keyword in the function
declarations, or just by adding the code directly to the class
declarations they're already inlined by g++? Also, why should I change
the naming convention to the data members? How would that increase
readability?

// - MyStuff.h -
// include guards here

class Bob{ public:
inline double getMinCost(); // declaration
double getMaxCost(){ return max_cost; } // default to inline
private:
double min_cost, max_cost;
};

// - here (in header, recommended) or .cpp. -
inline double Bob::getMinCost(){
// double Bob::getMinCost(){ // also OK (at least g++).
return min_cost;
}
// - MyStuff.h - end

The class Bob *only* has 'inline' member functions.
An 'inline' is special, it does not break the "one definition" rule (as long
as it's *exactly* the same if you define it in another place ). So, you can
include that header in many .cpp files (translation units).

Not all things can be inlined. GCC g++ has a 'flag' you can turn on to warn
you if something can not be inlined.
"
-Winline Warn if a function can not be inlined and it was declared as
inline.
"
Sorry to bother once more, and thanks for your time :)
Lucas

PS: hth?

hth == Hope That Helps

[ corrections welcome ]
 
L

Lucas Kanebley Tavares

Lucas Kanebley Tavares wrote in message...
Concerning the accessors, they do exist in the form of getters. And
now to think about it, considering they're inline I might just change
the code to use them.
A few questions though:
do I need to explicitly put the 'inline' keyword in the function
declarations, or just by adding the code directly to the class
declarations they're already inlined by g++? Also, why should I change
the naming convention to the data members? How would that increase
readability?

// - MyStuff.h -
// include guards here

class Bob{ public:
inline double getMinCost(); // declaration
double getMaxCost(){ return max_cost; } // default to inline
private:
double min_cost, max_cost;
};

// - here (in header, recommended) or .cpp. -
inline double Bob::getMinCost(){
// double Bob::getMinCost(){ // also OK (at least g++).
return min_cost;
}
// - MyStuff.h - end

The class Bob *only* has 'inline' member functions.
An 'inline' is special, it does not break the "one definition" rule (as long
as it's *exactly* the same if you define it in another place ). So, you can
include that header in many .cpp files (translation units).

Not all things can be inlined. GCC g++ has a 'flag' you can turn on to warn
you if something can not be inlined.
"
-Winline Warn if a function can not be inlined and it was declared as
inline.
"


Sorry to bother once more, and thanks for your time :)
Lucas

hth == Hope That Helps

[ corrections welcome ]


Thanks for the clarification
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,271
Messages
2,571,357
Members
48,042
Latest member
DoraMcBrie

Latest Threads

Top