通过 <vector> 删除复制构造函数等,按值(无指针)将具有相同基类的对象存储在C++中

Store objects with the same base class in C++ <vector> by value (no pointers) by deleting the copy constructor etc

本文关键字:基类 对象 C++ 存储 删除 复制 gt vector lt 构造函数 通过      更新时间:2023-10-16

我读到(这里https://stackoverflow.com/a/18351550/1474291),这是可能的,在<vector>继承相同的基类的派生类的存储对象。为了防止对象切片,我需要删除移动构造函数,复制构造函数和复制赋值。

注意:我感兴趣的是按值存储对象,而不是按指针存储。

我试图用Visual c++ 2013和MinGW (GCC 4.8.2和4.9.1)编译以下代码,但代码无法编译。我想在c++ 11中通过使用"delete", "default"和旧的方法来做到这一点。

正确的实现方法是什么?(实际上c++编译器支持"delete"answers"default"吗?)

下面是我的示例代码:
#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;
}

我认为您忽略的一点是容器需要为它包含的每个元素留出特定数量的内存。所以容器的元素固定大小

现在你想把不同大小的对象塞进这些固定大小的盒子里。你知道这是怎么回事吗?

当你试图将一个比基类大的派生对象塞进一个用来保存基类大小的对象的变量(读取容器元素)时,你将得到切片。

当将派生类型的对象放入基类向量中时,无法避免对象切片。你提到的问题解释了如何通过不可能复制相关类型的对象来避免对象切片。这意味着你得到的不是对象切片,而是编译错误。

除此之外,实现可以按值存储的"多态"类型的一种可能方法是实现一个单一类型,其实现可以在运行时设置。下面的内容:
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 elephantn"; }
};

然后提供了一种构造具有不同底层实现的Animal对象的方法。这允许您在std::vector<Animal>中存储不同类型的Animal