Let's say I have a base class Animal
from which a class Cow
inherits, and a Barn
class containing an Animal
vector, and let's say the Animal class has a virtual function scream()
, which Cow
overrides.
With the following code:
Animal.h
#ifndef _ANIMAL_H
#define _ANIMAL_H
#include <iostream>
using namespace std;
class Animal {
public:
Animal() {};
virtual void scream() {cout << "aaaAAAAAAAAAAGHHHHHHHHHH!!! ahhh..." << endl;}
};
#endif /* _ANIMAL_H */
Cow.h
#ifndef _COW_H
#define _COW_H
#include "Animal.h"
class Cow: public Animal {
public:
Cow() {}
void scream() {cout << "MOOooooOOOOOOOO!!!" << endl;}
};
#endif /* _COW_H */
Barn.h
#ifndef _BARN_H
#define _BARN_H
#include "Animal.h"
#include <vector>
class Barn {
std::vector<Animal> animals;
publi开发者_如何学运维c:
Barn() {}
void insertAnimal(Animal animal) {animals.push_back(animal);}
void tortureAnimals() {
for(int a = 0; a < animals.size(); a++)
animals[a].scream();
}
};
#endif /* _BARN_H */
and finally main.cpp
#include <stdlib.h>
#include "Barn.h"
#include "Cow.h"
#include "Chicken.h"
/*
*
*/
int main(int argc, char** argv) {
Barn barn;
barn.insertAnimal(Cow());
barn.tortureAnimals();
return (EXIT_SUCCESS);
}
I get this output:
aaaAAAAAAAAAAGHHHHHHHHHH!!! ahhh...
How should I code this to get MOOooooOOOOOOOO!!!
(and whatever other classes inheriting Animal
wants scream()
to be) instead?
A std::vector<Animal>
can only contain Animal
objects, not Cow
objects.
Here's what happens when you say barn.insertAnimal(Cow());
:
- A temporary object of type Cow is created by the evaluation of
Cow()
. - The parameter
animal
is copy-constructed from that temporary cow since you chose to pass it by value. Thisanimal
is a copy of theAnimal
-part of the temporary cow. This is called object-slicing. - The next element in the vector is copy-constructed from the parameter
animal
. Now you already have one cow and two additional animals, but you only wanted one cow! - The parameter
animal
is destroyed becauseinsertBarn
returns. - The temporary cow is destroyed because evaluation has reached the semicolon at the end of the line (to be more precise: evaluation of the full-expression has completed).
What is the lesson here? Don't pass animals by value, and don't store animals by value.
Runtime polymorphism requires a level of indirection. You probably want a std::vector<Animal*>
or std::vector<shared_ptr<Animal> >
or boost::ptr_vector<Animal>
.
A slight modification that stores pointers to animals should help:
#ifndef _BARN_H
#define _BARN_H
#include "Animal.h"
#include <vector>
class Barn {
std::vector<Animal *> animals;
public:
Barn() {}
void insertAnimal(Animal *animal) {animals.push_back(animal);}
void tortureAnimals() {
for(int a = 0; a < animals.size(); a++)
animals[a]->scream();
}
};
int main(int argc, char** argv) {
Barn barn;
barn.insertAnimal(new Cow());
barn.tortureAnimals();
// should clean up barn contents here...
return (EXIT_SUCCESS);
}
You're doing pass-by-value, so Cow
is being made into Animal
.
Using Animal * instead works, though this has memory leaks:
class Barn {
std::vector<Animal *> animals;
void insertAnimal(Animal *animal) {animals.push_back(animal);}
void tortureAnimals() {
for(int a = 0; a < animals.size(); a++)
animals[a]->scream();
}
};
int main(int argc, char** argv) {
Barn barn;
Cow *c = new Cow();
barn.insertAnimal(c);
barn.tortureAnimals();
/* delete them here... */
}
精彩评论