Redefine a member variable in a derived class

E

eselk

If I have:

class A
{
public:
class some_base_class **Obj;
};

And I would like to redefine "Obj" in a class derived from class A,
something like this maybe:

class B : public A
{
public:
class some_other_class **Obj;
};

How would I do that, or is it even possible? If I just use the above
method, class B ends up getting a second copy of Obj, so anything done
to Obj in the base class isn't working with the same pointers... that
isn't what I want.

The reason I want to do this is because class B creates different
objects for the "Obj" array. Whenever I reference Obj from a class B
object I'd like to be able to do it without type-casting. I currently
have something like this all over my code:

TNameRecord *nr = (TNameRecord*) NameRecs->Obj[x];

I'd like to just be able to do this:

TNameRecord *nr = NameRecs->Obj[x];

And if I accidently did this, I'd expect a compiler error:

TNameRecord *nr = MessageRecs->Obj[x];

Because MessageRecs uses some other type of object.

I've searched all over the newsgroups and can't find what I'm looking
for, although I'm sure this question has already been answered. I
guesss I just don't know what the terms are that I need to search for.
 
V

Victor Bazarov

If I have:

class A
{
public:
class some_base_class **Obj;
};

And I would like to redefine "Obj" in a class derived from class A,
something like this maybe:

class B : public A
{
public:
class some_other_class **Obj;
};

How would I do that, or is it even possible?

It is possible, allowed, legal. Just do it. What seems to be the
problem?
> If I just use the above
method, class B ends up getting a second copy of Obj,

Yes. Its own. Every class has its own data.
> so anything done
to Obj in the base class isn't working with the same pointers... that
isn't what I want.

Of course not. What *do* you want?
The reason I want to do this is because class B creates different
objects for the "Obj" array.

Uh... Then why is 'B' derived from 'A'?
> Whenever I reference Obj from a class B
object I'd like to be able to do it without type-casting. I currently
have something like this all over my code:

TNameRecord *nr = (TNameRecord*) NameRecs->Obj[x];

I'd like to just be able to do this:

TNameRecord *nr = NameRecs->Obj[x];

And if I accidently did this, I'd expect a compiler error:

TNameRecord *nr = MessageRecs->Obj[x];

Because MessageRecs uses some other type of object.

I've searched all over the newsgroups and can't find what I'm looking
for, although I'm sure this question has already been answered. I
guesss I just don't know what the terms are that I need to search for.

It is possible that you just don't know what you really need and why...

Why is your 'B' derived from 'A'? Does the LSP apply here? If 'B' needs
to change 'A' so much as to replace such _implementation_detail_ as one of
the 'A's *data members* with some other thing, then you are most likely
applying _inheritance_ without merit.

Perhaps you need to generalize your class[es] and have a template instead?
Then you can instantiate it for different types:

template<class T>
struct AB {
T **Obj;
};

typedef AB<TMessageRecord> A;
typedef AB<TNameRecord> B;
....
A MessageRecs;
B NameRecs;
....

V
 
E

eselk

In order to explain why I think inheritance is the correct approach,
take the following objects for example:

class TRecord
{
public:
long ID;
};

class TRecords
{
public:
TRecord **Rec;
void Sort(void);
void SortByID(void);
virtual int Compare(TRecord *r1,TRecord *r2)=0;
};

class TNameRecord : public TRecord
{
public:
char Name[21];
};

class TNameRecords
{
public:
TNameRecord* Add(void);
int Compare(TRecord *r1,TRecord *r2);
};

By using inheritance I can have 99% of the record sorting code in
TRecords::Sort(). TRecords::Sort() can have all kinds of code to do a
Bubble Sort, a Quick Sort, Binary, B-Tree, etc... The only thing the
derived classes need to provide are a compare function.

Each derived class creates an object derived from TRecord (the Add()
function does this), which is added to the "Rec" array of the base
class. Since they can create their own objects to add to the Rec
array, they can store any type of data. However, since each object
they add to the array must be derived from TRecord, the base class is
able to perform a SortByID() without any code in the derived class.
Also, it isn't show here, but TRecord has a virtual destructor, so the
base class is also able to free all objects when it is destroyed. The
base class can also have methods such as Search(), Move(), and Delete()
(since it can safely delete the objects due to the virtual
destructors).

All of this works well for me, and I think it is a good design. I've
seen it used in MFC and Borland's VCL a lot (TObjectList, TList,
TStringList). They usually use void* or other generic pointers, but in
my case I want to use specific pointer types for each derived class,
instead of having to typecast all of the time. Because, anytime I'm
using TNameRecords::Rec, I *know* that the pointers are always going to
point to a TNameRecord, and I'd like to catch bugs at compile time if I
accidently mix pointer types.

One way I've seen to handle this problem would be to add a function to
TNameRecords, like this (a "getter"):

TNameRecord* TNameRecords::getRecAsNameRecord(int index)
{
return((TNameRecord*)Rec[index]);
}

The function name could be anything, I just used that long name to make
the point clear.

However, I don't like this method because it seems like that adds
overhead both to the execution of the code, and to the size of the code
(the EXE file size). If I use __inline functions I guess it may not
add overhead to execution? But it will still increase the EXE size I
would imagine.
 
?

=?iso-8859-1?Q?Ali_=C7ehreli?=

In order to explain why I think inheritance is the correct approach,
take the following objects for example:

class TRecord
{
public:
long ID;
};

It will be better if you kept TRecord as an interface class. At least with a
virtual destructor... (I just realized that you mention this below.)

Also, it is not clear at all what that 'T' is doing at the beginning of the
name.
class TRecords
{
public:
TRecord **Rec;
void Sort(void);
void SortByID(void);
virtual int Compare(TRecord *r1,TRecord *r2)=0;
};

Instead of defining TRecords, typedef it from a standard container:

typedef std::vector<TRecord *> TRecords;

But that will give you resource management problems. Wrap your dynamic
objects in smart pointers:

#include <boost/shared_ptr.hpp>

/* ... */

typedef boost::shared_ptr<TRecord> TRecordPtr;

class TNameRecord : public TRecord
{
public:
char Name[21];
};
Fine...

class TNameRecords
{
public:
TNameRecord* Add(void);
int Compare(TRecord *r1,TRecord *r2);
};

Same comments apply for TNameRecords; but you can use TRecords to hold
TNameRecord objects too, because TNameRecord *is-a* TRecord.
By using inheritance I can have 99% of the record sorting code in
TRecords::Sort().

Regardless of inheritance, you can have 100% of record sorting code in a
free function. I suggest you spend a few days on the standard library.
Josuttis's "The C++ Standard Library" book is an excellent book.
Also, it isn't show here, but TRecord has a virtual destructor, so the
base class is also able to free all objects when it is destroyed.

Still, you will have to call the destructors explicitly through delete.
Needing to call delete explicitly, is an indication of design problems.
All of this works well for me, and I think it is a good design.

Standard library's separation of containers and algorithms is a good design
too.
I've
seen it used in MFC and Borland's VCL a lot (TObjectList, TList,
TStringList).

I don't know about VCL, but MFC was written before modern C++ features
and/or techniques were accepted.
They usually use void* or other generic pointers,

There you go! :)
but in
my case I want to use specific pointer types for each derived class,
instead of having to typecast all of the time. Because, anytime I'm
using TNameRecords::Rec, I *know* that the pointers are always going to
point to a TNameRecord, and I'd like to catch bugs at compile time if I
accidently mix pointer types.

One way I've seen to handle this problem would be to add a function to
TNameRecords, like this (a "getter"):

TNameRecord* TNameRecords::getRecAsNameRecord(int index)
{
return((TNameRecord*)Rec[index]);
}

No matter what, use one of C++'s casting operators. I think you want to use
static_cast above. The compiler provides no help for C-style casts.
However, I don't like this method because it seems like that adds
overhead both to the execution of the code, and to the size of the code
(the EXE file size). If I use __inline functions I guess it may not
add overhead to execution?

The function overhead is a myth until the application is tested and it is
proven to be so. Use functions generously.
But it will still increase the EXE size I
would imagine.

You can't know that. With or without using the inline keyword, the compiler
will do whatever it sees fit. The non-standard __inline keyword may be
different...

Ali
 
O

Old Wolf

If I have:

class A
{
public:
class some_base_class **Obj;
};

And I would like to redefine "Obj" in a class derived from class A,
something like this maybe:

class B : public A
{
public:
class some_other_class **Obj;
};

How would I do that, or is it even possible? If I just use the above
method, class B ends up getting a second copy of Obj

class A
{
Base **Obj;
public:
virtual Base **getObj() { return Obj; }
};

class B: public A
{
Derived **Obj;
public:
virtual Base **getObj() { return Obj; }
};

Then use getObj() whenever you want to refer to Obj.
You can't have polymorphic variables, only functions.
 
E

eselk

Thanks to all who replied, and sorry if my quoting isn't very good
(Google seems to have taken a step backwards in that area, I may need
to find a new news reader).

I obviously have a lot to learn about C++ still, especially when it
comes to templates and C++ style casts. I'm more of an OO C
programmer, if you know what I mean. I have read about polymorphism,
and I think that is what I was looking for, which I guess can't be done
for variables.

I'll do some learning about the smart pointers and templates, however
for this project I'll probably just use the get() function route, since
that will require less changes to existing/tested code.

p.s.- The "T" in the object name has to do with the company I work for.
I think we've used that longer than Borland! Not sure why Borland
uses T also, it has been a problem in a couple places (duplicate class
names, and duplicate namespaces even, if you can beleive it), must go
back to their "Turbo" line of compilers.
 
P

Puppet_Sock

Thanks to all who replied, and sorry if my quoting isn't very good
(Google seems to have taken a step backwards in that area, I may need
to find a new news reader).

Click the "options" link, and post from what you get there.
It will quote the whole message and you can edit and snip.
I obviously have a lot to learn about C++ still,
[snip]

I think all but a very small number of coders could reasonably
say that. A few of the best posters here probably are "up" on
nearly the entire language, but even the best make small mistakes
now and then.

Get yourself some of the better intro/intermediate texts.
Check out www.accu.org and the book reviews there. My
personal suggestions for early reading:

- Accelerated C++ by Koenig and Moo.
- Just about anything by Scott Meyers.
- Just about anything by Herb Sutter.

And, of course, you need Stroustrup's The C++ Programming Language.
Socks
 

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

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,819
Latest member
masterdaster

Latest Threads

Top