Short answer: Yes.
Long Answer: The factory method pattern is what you want.
Your network messages need to include the type and size of the object to deserialize in the message header and then on the recipient side your factory method can consume and deserialize the rest of the message body to construct the objects.
A good strategy to make this simple is to have all your classes store the data that they will be serialising and sending over the wire in a private struct. Other non-serialized class data would be outside this struct. That way you can just dump the whole struct on the network with minimal work. Obviously you may have to take into account byte order considerations if you're going cross platform (ie, big to little or little to big endian).
Something like this (I'm sure this is far from perfect as I'm just writing it off the top of my head):
enum VehicleType
{
VehicleType_Car,
VehicleType_Bike
};
class Vehicle
{
virtual size_t GetDataSize() = 0;
virtual void* GetData() = 0;
};
class Bike : Vehicle
{
private:
VehicleType _type;
size_t _dataSize;
struct BikeData
{
char[100] name;
// etc
} _data;
public:
Bike(void* data)
: Bike(static_cast<BikeData*>(data)->name)
{
}
Bike(char[]& name)
: _type(VehicleType_Bike), _dataSize(sizeof(BikeData))
{
memset(&_data.name, 0, 99);
strncpy(&_data.name, name, 99);
}
virtual size_t GetDataSize() { return _dataSize; }
virtual void* GetData() { return &_data; }
};
class Car : Vehicle
{
// etc
};
void SendVehicle(int socket, const Vehicle& vehicle)
{
write(socket, vehicle.GetData(), vehicle.GetDataSize());
}
Vehicle* ReceiveVehicle(int socket)
{
VehicleType type;
size_t dataSize;
read(socket, &type, sizeof(VehicleType));
read(socket, &dataSize, sizeof(size_t));
BYTE* data = new BYTE[dataSize];
read(socket, &data, dataSize);
Vehicle v* = CreateVehicle(type, dataSize, data);
delete[] data;
return v;
}
// The factory method.
Vehicle* CreateVehicle(VehicleType type, size_t dataSize, void* data)
{
switch(type)
{
case VehicleType_Car: return new Car(data);
case VehicleType_Bike: return new Bike(data);
}
return 0;
}
You could even avoid some memory fragmentation by using the buffer you read off the socket as the Bike's _data structure.
As always, reading up on the pattern you're using is a good idea. Here is the Wikipedia article on the Factory Method Pattern.
You should also look into the Boost Serialization library. It it will help you serialize data across systems with different endianness and word sizes. The method I have detailed above is very simple and doesn't deal with stuff like that.