Trying to understand casting, from base to derived.

S

Simon

Hi,

If I have two classes.

Class A
{
public:
A( const char *sz);
~A();
virtual void Foo() = 0;
}

Class B : public A
{
public:
B(const char *sz) : A( sz ) {};
~B(){ /* do clean up */ };
virtual void Foo();

int iSomeObject;
char *pSomePointer;
}

if i do

A *test = new A();
//
// is it then safe to do
//

((B *)test)->Foo();

// or even
((B *)test)->iSomeObject = 10

//////////////////////////////////////////////
Why do I ask?

because my base class reads information from a file.
So it is easier for me to have one call

A * test = new A( /* file name to read */ )

Depending on the actual data in the file a certain base class will be
used...

I could do

B * test = new B( /* file name to read */ )

but as I said, I only know what derived class to use after the file has been
read.
I guess I could do it the long way around, (read the file and depending on
the type call the relevant derived class), but that does not seem very
efficient.

To make matters worse I want to save all the pointers in a vector.

std::vector< A* >

but is it safe to include derived classes pointer in a vector?

Many thanks in advance for your help/advices.

Simon
 
K

Karl Heinz Buchegger

Simon said:
Hi,

If I have two classes.

Class A
{
public:
A( const char *sz);
~A();
virtual void Foo() = 0;
}

Class B : public A
{
public:
B(const char *sz) : A( sz ) {};
~B(){ /* do clean up */ };
virtual void Foo();

int iSomeObject;
char *pSomePointer;
}

if i do

A *test = new A();
//
// is it then safe to do
//

((B *)test)->Foo();

No. test points to an object of type A, not B.
// or even
((B *)test)->iSomeObject = 10

No. test points to an object of type A. Such an object
doesn't have a member iSomeObject. No memory space
was reserved for such a member, since your new requested
an A object, not a B.
//////////////////////////////////////////////
Why do I ask?

because my base class reads information from a file.
So it is easier for me to have one call

A * test = new A( /* file name to read */ )

Depending on the actual data in the file a certain base class will be
used...

I could do

B * test = new B( /* file name to read */ )

but as I said, I only know what derived class to use after the file has been
read.

Change your file format.
In the format insert codes which guide the reader, such that it knows which
object to create. -> You know what derived class *during* you read the file
and hence can create the correct object.
I guess I could do it the long way around, (read the file and depending on
the type call the relevant derived class), but that does not seem very
efficient.

As said: Change your file format. It is always possible to augument the file
with information, such that the reader can figure out what to do.
To make matters worse I want to save all the pointers in a vector.

std::vector< A* >

but is it safe to include derived classes pointer in a vector?

That's fine.
Every A pointer can always point to any other class dervied from A.
 
G

Giulio Guarnone

Simon ha scritto:
Class A
{
public:
A( const char *sz);
~A();
virtual void Foo() = 0;
}

Class B : public A
{
public:
B(const char *sz) : A( sz ) {};
~B(){ /* do clean up */ };
virtual void Foo();

int iSomeObject;
char *pSomePointer;
}
You can't do that, vitual pure classes can't be instantied
A *test = new A();

It's illegal, you are trying to call a B::method on A
((B *)test)->Foo();
((B *)test)->iSomeObject = 10

B IS-A A, but not viceversa

A test = new B('stuffstring');

then you can :

test->Foo(); // LEGAL
test->iSomeObject = 0; // ERROR

class B {/*...*/};
class D : public B {/*.../*};

D is-a B, but B isn't D.

when you (publicly) extend a class you may both change behavior and
adding features, and the base class don't have to know anything about it
(so you can force B to use functions declared only in D).

It seems your had understood inheritance in the opposite way.

Bye,
Giulio
 
S

Simon

Karl Heinz Buchegger said:
Simon wrote:

Change your file format.
In the format insert codes which guide the reader, such that it knows
which
object to create. -> You know what derived class *during* you read the
file
and hence can create the correct object.


As said: Change your file format. It is always possible to augument the
file
with information, such that the reader can figure out what to do.


That's fine.
Every A pointer can always point to any other class dervied from A.

Thanks for that.
One last question.

When it comes time to delete all the pointers in the vector, I would
normally do

A *pSomePointer = (*(m_vectorData.begin()+nPos));
delete pSomePointer;

if pSomePointer was created with the derived class B will the destructor of
B be called or the destructor of A?

Simon
 
A

annamalai.gurusami

Simon said:
Depending on the actual data in the file a certain base class will be
used...

I think you wanted to say "certain derived class will be used"?
There are two operations here.

1. Finding the "type" of the file.
2. Calling the appropriate derived class mf to process the file.

You could have the base class interface as

class Base {
public:
void process_file( const string& fileName)
{
process_file( find_file_type(fileName) );
}
protected:
virtual void process_file(const FileType&);
// each derived class will take care of its own
// FileType
private:
FileType find_file_type( const string& fileName);
};

An object of FileType will have the opened file descriptor or
FILE* or an ifstream or whatever. This, so that you don't have
to re-open the file or re-initialize the stream.

By doing this, you are having clear separation of responsibilities.
It is the responsibility of the base class to determine the type
of file. And it is the responsibility of the derived class to
process the file.

Any comments on this approach?

Rgds,
anna
 
C

Chris Theis

Simon said:
Thanks for that.
One last question.

When it comes time to delete all the pointers in the vector, I would
normally do

A *pSomePointer = (*(m_vectorData.begin()+nPos));
delete pSomePointer;

Why don't you use the subscript [] operator which would make the syntax more
legible?
if pSomePointer was created with the derived class B will the destructor
of B be called or the destructor of A?

Simon

You can certainly do this but you need to have the destructor of the base
class declared virtual!
See http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.7

HTH
Chris
 
G

Giulio Guarnone

Simon ha scritto:
When it comes time to delete all the pointers in the vector, I would
normally do

A *pSomePointer = (*(m_vectorData.begin()+nPos));
delete pSomePointer;

if pSomePointer was created with the derived class B will the destructor of
B be called or the destructor of A?

If you are using interface(or public) inheritance you had to declare a
destructor like this :

class B
{
/* ... */
public:
virtual ~B() throw();
/* ... */
}

class D : public B
{
/* ... */
public:
virtual ~D() throw();
/* ... */
}

so in case of :

B *b = new D();
/* ... */
delete b;

D::~D 'll do the complete requested cleanup and the operator delete'll
free the right amount of memeory, with a non-virtual destructor B::~B
'll do its cleanup only.

the "throw()" on the method signature is exception-safe 'cause operator
delete can't throw any exception by definition, and whatever may happen
during the dctor call, the destruction must be completed.

bye,
Giulio
 
S

Simon

D::~D 'll do the complete requested cleanup and the operator delete'll
free the right amount of memeory, with a non-virtual destructor B::~B 'll
do its cleanup only.

Thanks for that, in a way it makes sense.
the "throw()" on the method signature is exception-safe 'cause operator
delete can't throw any exception by definition, and whatever may happen
during the dctor call, the destruction must be completed.

That, I did not know.
bye,
Giulio

Thanks

Simon
 
S

Simon

A *pSomePointer = (*(m_vectorData.begin()+nPos));
delete pSomePointer;

Why don't you use the subscript [] operator which would make the syntax
more legible?

It's one of many weird habit I pick-up along the way.
You can certainly do this but you need to have the destructor of the base
class declared virtual!
See http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.7

Thanks, i just wan't sure.
HTH
Chris

Simon
 
S

Simon

I think you wanted to say "certain derived class will be used"?
Indeed.

There are two operations here.

1. Finding the "type" of the file.
2. Calling the appropriate derived class mf to process the file.
An object of FileType will have the opened file descriptor or
FILE* or an ifstream or whatever. This, so that you don't have
to re-open the file or re-initialize the stream.

By doing this, you are having clear separation of responsibilities.
It is the responsibility of the base class to determine the type
of file. And it is the responsibility of the derived class to
process the file.

Any comments on this approach?

It does look quite good. I am not sure about 2 separate classes, but it is
as good as any way I guess.
I will probably go down that road.
Rgds,
anna

Simon
 
A

Andre Kostur

the "throw()" on the method signature is exception-safe 'cause
operator delete can't throw any exception by definition, and whatever
may happen during the dctor call, the destruction must be completed.

That safety is somewhat illusory.... if an exception does happen to be
thrown during the destruction, an exception _will_ escape your destructor:
std::unexpected.
 
G

Giulio Guarnone

Andre Kostur ha scritto:
That safety is somewhat illusory.... if an exception does happen to be
thrown during the destruction, an exception _will_ escape your destructor:
std::unexpected.

yes, and then program exit, it should be an unexpected (not managable)
event. No inconsistent objects will be used.

Bye,
Giulio
 
B

Ben

Simon said:
>
A * test = new A( /* file name to read */ )

Depending on the actual data in the file a certain base class will be
used...

I could do

B * test = new B( /* file name to read */ )

but as I said, I only know what derived class to use after the file has been
read.
I guess I could do it the long way around, (read the file and depending on
the type call the relevant derived class), but that does not seem very
efficient.

Research the Abstract Factory pattern.

Ben
 
F

Frank Chang

Simon: Your final question was?

"To make matters worse I want to save all the pointers in a vector
std::vector< A* >
but is it safe to include derived classes pointer in a vector?"

You could try using boost::shared_ptr. Please see the link
www.boost.org.
 
F

Frank Chang

Simon, One small nitpick . Your abstract base class A does not have a
virtual destructor. If you want your derived base class B's destructor
to function correctly you need to your destructor in class A virtual.
Thank you.
 
F

Frank Chang

Simon, As the following shows, the boost shared_ptr simplifies the
memory management for yout std::vector. When the vector goes out of
scope, all the elements in the vector that are not in use get
automatically destroyed:

class ATest
{
public:
ATest(const char *sz){ptr = new char[25];
strncpy(ptr, sz, strlen(sz));};
virtual ~ATest(){cout << "deleting ATest" << endl;};
virtual void Foo(){cout << "ATest foo" << endl;};
private:
char* ptr;
};

class BTest : public ATest
{
public:
BTest(const char *sz, int some = 0, char* ptr = NULL)
: ATest( sz ), iSomeObject(some),
pSomePointer(new char[25])
{ strcpy(pSomePointer, ptr); };
~BTest(){cout << "deleting BTest" << endl;};
virtual void Foo(){cout << "BTest foo" << endl;};

private:
int iSomeObject;
char *pSomePointer;
};

typedef std::vector< boost::shared_ptr<ATest> > ListOfObjects;

class DoesSomething
{
public:
DoesSomething() {};

ListOfObjects returnList()
{
ListOfObjects lst;
lst.push_back(boost::shared_ptr<ATest>(
new BTest("abcdef", 10, "hjklmnop") ));
lst.push_back(boost::shared_ptr<ATest>(
new ATest("clara")));
return lst;
}
};


int _tmain(int argc, _TCHAR* argv[])
{
using namespace boost;
using namespace std;

DoesSomething d;
ListOfObjects lst = d.returnList();
boost::shared_ptr<ATest> aptr = lst[0];
boost::shared_ptr<ATest> claraptr = lst[1];
aptr->Foo();
claraptr->Foo();
}

Thank you.
 

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
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top