I have read (here https://stackoverflow.com/a/18351550/1474291 ) that it is possible to store objects of derived classes that inherit the same base class in a <vector>
. To prevent Object Slicing I need to delete the move constructor, the copy constructor and the copy assignment.
NOTE: I am interested in storing the object by value and not as pointers.
I tried to compile the following code with Visual C++ 2013 and MinGW (GCC 4.8.2 and 4.9.1) but the code fails to compile. I want to do that both in C++11 by using "delete", "default" and the older way.
What is the correct way to implement that? (Do actually C++ compilers support "delete" and "default" properly yet?)
Here is my example code:
#include <iostream>
#include <vector>
using namespace std;
#if __cplusplus > 199711L
#define CPP11
#endif
class Animal {
public:
Animal() {
cout << "Making animal:";
}
virtual ~Animal() {
cout << "Send the animal home!";
}
#ifdef CPP11
public:
Animal(Animal&&) = delete;
Animal(Animal const&) = delete;
Animal& operator=(Animal&) = delete;
#else // C++98
private:
Animal(Animal const&);
Animal& operator=(Animal&);
#endif // CPP11
public:
virtual void speak() {
cout << "I am an animal!";
}
};
class Dog : public Animal {
public:
Dog() {
cout << "Making dog:";
}
virtual ~Dog() {
cout << "Send the dog home!";
}
#ifdef CPP11
public:
Dog(Dog&&) = default;
Dog(Dog const&) = default;
Dog& operator=(Dog&) = default;
#else // C++98
private:
Dog(Dog const&);
Dog& operator=(Dog&);
#endif // CPP11
virtual void speak() {
cout << "I am a dog!";
}
};
class Cat : public Animal{
public:
Cat() {
cout << "Making cat";
}
virtual ~Cat() {
cout << "Sending the cat home!";
}
#ifdef CPP11
public:
Cat(Cat&&) = default;
Cat(Cat const&) = default;
Cat& operator=(Cat&) = default;
#else // C++98
private:
Cat(Cat const&);
Cat& operator=(Cat&);
#endif // CPP11
virtual void speak() {
cout << "I am a cag!";
}
};
int main()
{
vector<Animal> animals;
for (int i = 0; 10 > i; i++) {
Dog dog;
animals.push_back(dog);
Cat cat;
animals.push_back(cat);
}
#ifdef CPP11
for (Animal& animal: animals) {
animal.speak();
}
#else
for (std::vector<Animal>::iterator currentAnimal = animals.begin();
currentAnimal != animals.end();
++currentAnimal) {
currentAnimal->speak();
}
#endif // CPP11
return 0;
}
The point I think you are missing is that the container needs to set aside a specific amount of memory for every element it contains. So the container's elements are of fixed size .
Now you want to stuff objects of varying sizes into those fixed size boxes. Do you see where this is heading?
You are going to get slicing whenever you try to stuff a derived object that is larger than its base class into a variable (read container element) designed to hold base class sized objects.
You cannot avoid object slicing when putting objects of a derived type into a vector of the base. The question you refer to explains how to avoid object slicing by making it impossible to copy objects of the relevant type. This means instead of object slicing you get a compilation error.
That aside, one possible way to implement a "polymorphic" type that can be stored by value is by implementing one single type, whose implementation can be set at runtime. Something along these lines:
class Animal
{
public:
void talk() const { impl_->talk(); }
// implement copy, assignment, move copy, move assignment
// implement constructor allowing to specify the implementation
private:
std::unique_ptr<AnimalImpl> impl_;
};
struct AnimalImpl
{
virtual void talk() const = 0;
virtual ~AninalImpl() {}
};
struct Elephant : AnimalImpl
{
void talk() const override { std::cout << "I am an elephant\n"; }
};
Then you provide a means of constructing Animal
objects with different underlying implementations. This allows you to store different kinds of Animal
in an std::vector<Animal>
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.