C++ refresher...question on new functions in derived classes

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?

Thanks for taking a look!
new gives you a pointer.

'o' is an object.

1
2
3
Object* p = new NewObj;  // <- this would work

p->foo(); // <- however this will not, because 'Object' has no 'foo' member 


If you're going for polymorphism, you must make a virtual member in the base class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Object
{
public:
    virtual void foo() = 0;
};

class NewObj : public Object  // <- make sure to make the inheritance public
{
public:
    virtual void 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
}
Last edited on
Are you supposed to be actually writing "override" there?
Isn't that a Java thing? I'm sure that should not be there.

Another thing, when you make virtual class members, make virtual DESTRUCTORS too:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Object {
public:
    virtual void foo() = 0;
    inline virtual ~Object() {}
};

class NewObj : public Object {
public:
    virtual void foo() { /* do NewObj's foo things */ }
};

class NewObj2 : public Object {
public:
    virtual void 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:
    virtual void foo() { /* Object's foo things */ }
    inline virtual ~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:
    virtual void foo() { Object::foo(); /* do NewObj's foo things */ }
};
Last edited on
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.
EssGeEich wrote:
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.
@Maxxjr:

The approach I would take is this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

class GameObject {
    inline virtual ~GameObject() {}
};

class DrivableObject : public GameObject {
    // Changes the steering wheel based on the user input
    virtual void SetSteeringPosition(float) = 0;
    // Sets engine on or off
    virtual void SetEngineTurnedOn(bool) = 0;
    // Goes forward or backwards
    virtual void SetAcceleration(float) = 0;
};

class WorldSolidObject : public GameObject {
};

class Wall : public WorldSolidObject {
};

class Car : public DrivableObject {
    virtual void SetSteeringPosition(float) { /* handle the situation for a car*/ }
    virtual void SetEngineTurnedOn(bool) { /* handle the situation for a car */ }
    virtual void SetAcceleration(float) { /* handle the situation for a car */ }
};

class Boat : public DrivableObject {
    virtual void SetSteeringPosition(float) { /* handle the situation for a boat */ }
    virtual void SetEngineTurnedOn(bool) { /* handle the situation for a boat */ }
    virtual void SetAcceleration(float) { /* handle the situation for a boat */ }
};

class Train : public DrivableObject {
    virtual void SetSteeringPosition(float) { /* ignore situations, trains cannot steer */ }
    virtual void SetEngineTurnedOn(bool) { /* handle the situation for a train */ }
    virtual void 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.
Last edited on
EssGeEich wrote:
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 
Last edited on
Topic archived. No new replies allowed.