C++ How to access child class virtual function from an array of pointers

Viewed 46

I have an array of pointers defined like so:

Person *people[2];

If I populate it in the main function, the program works just fine and my child class can access its method:

int main(int argc, const char * argv[]) {

    Person *people[2];

    Person p1 = Person();
    Child c1 = Child();

    people[0] = &p1;
    people[1] = &c1;

    speak(people);

    return 0;
}


void speak(Person** people) {
    for (int i = 0; i < 2; i++) {
        people[i]->speak();
    }
}

However, if I create a separate method to populate my array of pointers, the speak() method does not recognize the second pointer as a pointer to my child object:

int main(int argc, const char * argv[]) {

    Person *people[2];

    populate(people);

    speak(people);

    return 0;
}

void populate(Person** people) {

    Person p1 = Person();
    Child c1 = Child();

    people[0] = &p1;
    people[1] = &c1;
}

void speak(Person** people) {
    for (int i = 0; i < 2; i++) {
        people[i]->speak();
    }
}

What exactly is wrong with my second example that is causing the speak() method to fail on the second iteration of the loop? Both examples look exactly identical to me with the only caveat being the populate() method.

Here are the class definitions for my Person and Child class:

class Person {
public:
    virtual void speak();
private:
    string first;
    string last;
    int age;
};

void Person::speak() {
    cout << "Hello! I'm a person!" << endl;
}

class Child: public Person {
public:
    virtual void speak();
private:
    string face;
};

void Child::speak() {
    cout << "Hello! I'm a child!" << endl;
}

All help is much appreciated!

1 Answers

In populate, you are assigning the address of temporary (stack local) variables when you do people[0] = &p1;.

Once you return from the function, the variable p1 dies, and person is left pointing to invalid memory. Attempting to access this memory from main then invokes undefined behaviour.

You can fix this by allocating memory on the heap for the passed in pointer:

void populate(Person** people) {

    people[0] = new Person();
    people[1] = new Child();
}

Here's a working demo.

In the version without populate, it works because those local variables whose addresses you take, live for the duration of the call to speak.

Related