Archived

This topic is now archived and is closed to further replies.

Greg K

Class Questions

Recommended Posts

Ok, I have a base class with a variable - Lets call it Alive - and then I have a bunch of derived classes. If I have a void pointer to one of the derived classes, can I prototype it as the base class and read the Alive variable without any horrible repercussions?
  
#include <iostream.h>

class BaseClass
{
public:
 int Alive;
};

class DerivedClass : public BaseClass
{
public:
 int a;
 int b;
 int c;
};

void main( void )
{
 DerivedClass dc;
 dc.Alive = 10;
 void *pdc;
 pdc = &dc;
 cout << ((BaseClass*)(pdc))->Alive;
}
  

Share this post


Link to post
Share on other sites
I can''t see anything wrong with your code, although if you''re practising this examples in your real codes, it would be a bad OOP approach to declare a variable a public and modify it directly.

Share this post


Link to post
Share on other sites
That won't neccessarily work correctly if you use mutliple inheritence - it still can, but it's no longer a garuanteed thing. dynamic_cast<> can help in the situations where it matters.
    
#include <iostream.h>

class BaseClass
{
public:
int Alive;
};

class DerivedClass : public BaseClass
{
public:
int a;
int b;
int c;
};

void main( void )
{
DerivedClass dc;
dc.Alive = 10;
void *pdc;
pdc = dynamic_cast<BaseClass*>(&dc);
cout << reinterpret_cast<BaseClass*>(pdc)->Alive;
}

The second cast (the reinterpret one) is now safe becuase you explicit put a BaseClass* into the void*. And the dynamic_cast won't compile if dc doesn't derive from BaseClass. (If you're doing something with COM, dynamic_cast are needed in QueryInterface).

That aside, why on God's Green Earth would you partake in such an abomination? Are you passing it to some legacy API (like DirectPlay8) that uses void* for cookies? If not, don't do it, there's a typesafe way using inheritence and/or templates.

Edited by - Magmai Kai Holmlor on January 11, 2002 1:28:56 AM

Share this post


Link to post
Share on other sites
I want to do it so that I can make a messaging system. The data will have to be stored as a chunk of data and sent across the internet. When it arrives all you will know about the packet is it''s size. If all my messages come from a base class then I can typecast it as the baseclass and check it''s ID. Is there a much better way to do this that I am just missing?

Share this post


Link to post
Share on other sites
The right way (in my opinion) would be to do it like this:

    
class BaseClass
{
public:
int GetAlive(){return Alive;}
void SetAlive(int a){Alive = a;}

private:
int Alive;
};

class DerivedClass : public BaseClass
{
public:
int a;
int b;
int c;
};

void main( void )
{
DerivedClass dc;
dc.SetAlive(10);
void *pdc;
pdc = &dc;
cout << ((BaseClass*)pdc)->GetAlive();
}


- Mike

Edited by - mkaltner on January 11, 2002 1:51:14 AM

Share this post


Link to post
Share on other sites
Yeah, I am not using that as actual code. It was just an example. Either way you code it should not have an impact on the problem at hand.

Share this post


Link to post
Share on other sites
quote:
Original post by Magmai Kai Holmlor

That won''t neccessarily work correctly if you use mutliple inheritence - it still can, but it''s no longer a garuanteed thing. dynamic_cast<> can help in the situations where it matters.


dynamic_cast is a RTTI (as in Run-Time Type Information) feature and as such it may have overhead. You should only place it in your code when necessary.


quote:
Original post by Magmai Kai Holmlor

The second cast (the reinterpret one) is now safe becuase you explicit put a BaseClass* into the void*. And the dynamic_cast won''t compile if dc doesn''t derive from BaseClass. (If you''re doing something with COM, dynamic_cast are needed in QueryInterface).


The first cast is unnecessary and misleading. The difference between static_cast and dynamic_cast here is that the latter uses an unnecessary run-time check. A simple static_cast will work fine because due to the conversion rules it will also perform the compile-time check to make sure that "dc" is really an instance of an class that was publicly derived from "BaseClass".

The second cast is also unnecessary and misleading. All you need is static_cast; the standard guarantees that a pointer of any type can be converted to a void* and back again without losing its value; it does not guarantee bit patterns of pointers and also there is nothing inherently non-portable about the cast.

If you want to cast from a "BaseClass*" to a "DerivedClass*" without knowing the type then you should use dynamic_cast (since it performs a run-time type check). Additionally, using dynamic_cast to cast to a reference type will throw a std::bad_cast exception if it fails.


quote:
Original post by Greg K

I want to do it so that I can make a messaging system. The data will have to be stored as a chunk of data and sent across the internet. When it arrives all you will know about the packet is it''s size. If all my messages come from a base class then I can typecast it as the baseclass and check it''s ID. Is there a much better way to do this that I am just missing?


If you send the whole object as data (especially if you use virtual functions) then you must make sure that you are using the same compiler, the same version of the compiler, and the same version of the source code on both ends. Otherwise, one end may interpret the raw bits differently than the other.

You can minimize these dependencies by creating a manager class that manages class IDs and acts as a class factory for the messages, and have each message type write its data members into the data stream, one-by-one, ala:

  
#include <algorithm>
 
class message
{
protected:
virtual void serialize ( char* memory) = 0;
virtual void serialize (const char* memory) = 0;
 
template <class T> copy (char* memory, const T& data)
{
char* p = reinterpret_cast<const char*> (data);
std::copy(p, p + sizeof (T), memory);
}
 
template <class T> copy (const char* memory, T& data)
{
char* p = reinterpret_cast<char*> (data);
std::copy(memory, memory + sizeof (T), p);
}
 
friend class message_manager;
};
 
class example_message : public message
{
int alive;
long double other;
 
protected:
void serialize ( char* memory);
void serialize (const char* memory);
};
 
void example_message::serialize (char* memory)
{
copy (memory, alive);
copy (memory, other);
}
 
void example_message::serialize (const char* memory)
{
copy (memory, alive);
copy (memory, other);
}
 
int main(int argc, char* argv[])
{
example_message ex;
 
void* p1 = static_cast<message*> (&ex);
message* p2 = static_cast<message*> ( p1);
 
return 0;
}


If you wanted to do something neat, you could define an i/o stream interface (derived from std:::iostream) for the network packet memory...and have the message classes'' serialization to/from use std::istream/std::ostream, respectively, as opposed to raw memory. That way you could add some extra/debugging processing later if need be without modifying the message classes, or also serialize the messages to disk just as easily as sending them over a network. Just a thought.

Well, that''s enough information for one post.

Share this post


Link to post
Share on other sites