Re: problems with inheritance and protected attributes
* 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.
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?