I am trying to quickly come back up to speed on some of the finer points of C++ after 10+ years away from it. I may get an answer faster here while I try to find the magic Google search terms for what I am looking for vs. a million other similar topics, and scanning through an old C++ text.
I have a base class, and a derived class . In the derived class, I might have new functions that do not logically fit in the base class.
I would like to do something like the following:
class Object{
}
class NewObj:Object{
public:
void foo(){};
}
int main(){
Object o;
o = new NewObj();
o.foo();
}
Now...the compiler is telling me the above is not correct. Is this a syntax/structure issue?
class Object
{
public:
virtualvoid foo() = 0;
};
class NewObj : public Object // <- make sure to make the inheritance public
{
public:
virtualvoid foo() override { }
};
int main()
{
Object* p = new NewObj;
p->foo();
delete p; // <- remember to delete whatever you new.
// or use a smart pointer so it deletes itself
}
class Object {
public:
virtualvoid foo() = 0;
inlinevirtual ~Object() {}
};
class NewObj : public Object {
public:
virtualvoid foo() { /* do NewObj's foo things */ }
};
class NewObj2 : public Object {
public:
virtualvoid foo() { /* do NewObj2's foo things */ }
};
int main()
{
Object * p = new NewObj;
p->foo(); // NewObj's foo things
delete p;
p = new NewObj2;
p->foo(); // NewObj2's foo things
delete p;
}
Another thing: In this EXACT example, you won't be able to do:
Object p; or Object * p = new Object;
Why? Because Object has no associated "foo" function.
To resolve this problem, make a default "foo" function like this:
1 2 3 4 5
class Object {
public:
virtualvoid foo() { /* Object's foo things */ }
inlinevirtual ~Object() {}
};
At this point, this "foo" function will ONLY be called if the class is an Object.
Not if it's a NewObj, neither if it's a NewObj2.
But you can still call that function like this:
1 2 3 4
class NewObj : public Object {
public:
virtualvoid foo() { Object::foo(); /* do NewObj's foo things */ }
};
So...in a list of objects, a "car" might have a steering function, and a "wall" of course would not need it. But to iterate through a list of all objects, and call the steering function when appropriate, I would nevertheless have the steering function defined (as virtual) in the base class. So that Wall objects will have some steering function vestigial appendage even if never implemented. Philosophically, this seems counter-intuitive to what class hierarchy is supposed to achieve. The base class would only contain "stuff" that everybody cares about, and derived classes specialize from there. As object diversity increases, and particularly with input from multiple programmers, the base class has to be actively maintained to be compatible with new or changed derived classes.
Are you supposed to be actually writing "override" there?
Isn't that a Java thing? I'm sure that should not be there.
It's a C++11 thing.
Another thing, when you make virtual class members, make virtual DESTRUCTORS too:
Good catch! I can't believe I forgot that. You're absolutely right.
maxxjr wrote:
I would nevertheless have the steering function defined (as virtual) in the base class. So that Wall objects will have some steering function vestigial appendage even if never implemented.
That'd be one way to do it... although that's arguably a poor design.
Walls and Cars might not have enough in common to have a common base class. An 'Object' base class is pretty generic and not particularly useful.
Just because you can derive from a common base class doesn't mean you should.
As object diversity increases, and particularly with input from multiple programmers, the base class has to be actively maintained to be compatible with new or changed derived classes.
You should not take this approach... and in fact you should really try to avoid it.
The parent class should not change to accommodate the needs of its children. It's actually the exact opposite: the children should be built to fulfill the needs of the parent.
class GameObject {
inlinevirtual ~GameObject() {}
};
class DrivableObject : public GameObject {
// Changes the steering wheel based on the user input
virtualvoid SetSteeringPosition(float) = 0;
// Sets engine on or off
virtualvoid SetEngineTurnedOn(bool) = 0;
// Goes forward or backwards
virtualvoid SetAcceleration(float) = 0;
};
class WorldSolidObject : public GameObject {
};
class Wall : public WorldSolidObject {
};
class Car : public DrivableObject {
virtualvoid SetSteeringPosition(float) { /* handle the situation for a car*/ }
virtualvoid SetEngineTurnedOn(bool) { /* handle the situation for a car */ }
virtualvoid SetAcceleration(float) { /* handle the situation for a car */ }
};
class Boat : public DrivableObject {
virtualvoid SetSteeringPosition(float) { /* handle the situation for a boat */ }
virtualvoid SetEngineTurnedOn(bool) { /* handle the situation for a boat */ }
virtualvoid SetAcceleration(float) { /* handle the situation for a boat */ }
};
class Train : public DrivableObject {
virtualvoid SetSteeringPosition(float) { /* ignore situations, trains cannot steer */ }
virtualvoid SetEngineTurnedOn(bool) { /* handle the situation for a train */ }
virtualvoid SetAcceleration(float) { /* handle the situation for a train */ }
};
int main()
{
std::vector<DrivableObject*> vehicles;
std::vector<WorldObject*> world;
std::vector<GameObject*> all_objects;
vehicles.push_back(new Car);
vehicles.push_back(new Train);
vehicles.push_back(new Boat);
world.push_back(new Wall);
for(size_t i = 0; i < vehicles.size(); ++i)
all_objects.push_back(vehicles[i]);
for(size_t i = 0; i < world.size(); ++i)
all_objects.push_back(world[i]);
/*
You are free to do this:
*/
vehicles[0]->SetEngineTurnedOn(1); // Turn car engine on
vehicles[1]->SetEngineTurnedOn(1); // Turn train engine on
vehicles[2]->SetEngineTurnedOn(0); // Turn boat engine on
world[0]->act_as_a_wall(); // fictional
vehicles.clear();
world.clear();
for(size_t i = 0; i < all_objects.size(); ++i)
delete (all_objects[i]);
}
Basically:
Make multiple classes deriving from a single one, and make more deriving from them.
Make them comply with a single base one, so you can store all of them in the same array, to easify deletion and storage.
Make them comply with a single base one, so you can store all of them in the same array, to easify deletion and storage.
Putting them in a smart pointer makes it even easier and negates the need for a "master" list entirely. It also removes the necessity for an otherwise useless 'GameObject' base class.
Plus it's fullproof and you don't have to worry about deleting individual elements as they are removed.
RAII is the way to go. Manual memory management is best to be avoided.
1 2 3 4 5 6 7 8 9 10 11 12 13
typedef std::unique_ptr<DrivableObject> DrivableObjectPtr;
typedef std::unique_ptr<WorldObject> WorldObjectPtr;
std::vector<DrivableObjectPtr> vehicles;
std::vector<WorldObjectPtr> world;
vehicles.emplace_back( new Car );
vehicles.emplace_back( new Train );
vehicles.emplace_back( new Boat );
world.emplace_back( new Wall );
// no need to do any cleanup