如何通过复制构造函数深度复制派生 class 指针的向量?

How do you deep copy a vector of derived class pointers via the copy constructor?

#include <iostream>
#include <string>
#include <vector>
//#include "V2d.h"
using namespace std;

class item {
public:
    int value;
    string description;
    item() {}
    virtual ~item() {}
    virtual void display() {
        cout << "Value: " << value << endl;
    }
};

class Cat : public item {
public:
    string name = "Cat";
    Cat() {
        value = 20;
        description = "a cat";
    }
    void display() {
        cout << "Name: " << name << endl;
        cout << "Value: " << value << endl;
        cout << "Description: " << description << "\n" << endl;
    }
};

class Dog : public item {
public:
    string name = "Dog";
    Dog() {
        value = 10;
        description = "a dog";
    }
    void display() {
        cout << "Name: " << name << endl;
        cout << "Value: " << value << endl;
        cout << "Description: " << description << "\n" << endl;
    }
};

class v2d {
public:
    int hitPoints;

    enum ItemName {
        APPLE, ORANGE
    };
    vector<item*> inventory;
    v2d() {
    }
    ~v2d() {
        for (int i = 0; i < inventory.size(); i++) {
            delete inventory[i];
        }

        inventory.clear();

    }
    v2d(const v2d& orig) : inventory(orig.inventory.size()) {

        hitPoints = orig.hitPoints;
        for (int i = 0; i < inventory.size(); ++i) {
            inventory[i] = new item(*orig.inventory[i]);
        }
        cout << "Copied!" << endl;
    }
    void display() {
        for (int i = 0; i < inventory.size(); i++) {
            inventory[i]->display();
        }
    }

};
int main() {
    v2d vect1;
    vect1.inventory.push_back(new Cat());
    vect1.inventory.push_back(new Dog());
    vect1.inventory.push_back(new Dog());
    vect1.inventory.push_back(new Cat());
    vect1.inventory.push_back(new Dog());
    vect1.display();

    cout << "**************************\n" << endl;

    v2d vect2(vect1);
    vect2.display();
}

我应该从哪里开始尝试重载 v2d 中的 += 和 -= 运算符以减去和添加到库存向量? 我已经到了拥有基础 class 和派生 class 的部分;我似乎在使用动态转换时 not 遇到了麻烦。有没有一种简单的方法可以在不使用 clone() 函数的情况下通过复制构造函数深度复制派生 class 指针的向量?

要正确执行此操作,还需要向基础中添加两件事 class:

class item {
public:

    virtual ~item() {}

基础 class 必须有一个虚析构函数。

    virtual item *clone() const=0;

还有一个传统上称为 clone() 的抽象方法。您的每个子classes 都必须实现clone(),通常是通过使用复制构造函数:

class Cat : public item {
public:

    item *clone() const override { return new Cat{*this}; };

item 的所有其他子 class 执行相同的操作。然后,有了这个,您就可以正确地克隆这些对象的向量:

    for (int i = 0; i < inventory.size(); ++i) {
        inventory[i] = orig.inventory[i]->clone();
    }

I seem to be having trouble not using dynamic casting. Is there a simple way to deep copy a vector of derived class pointers via the copy constructor without using a clone() function?

clone是传统的解决方案,但我会提出一个替代方案。

考虑使用类型擦除和值类型。它将提供最简单的用法,到目前为止,设置起来更复杂。值类型通常可以更干净地与许多语言和库进行互操作。它们的侵入性较小。标准类型可以满足您的接口。不需要继承。值类型不需要到处都是指针和间接寻址。这些只是其中的一小部分优势。

在C++20中,我们可以为item定义一个概念,也就是接口的另一个名字:

template <typename Item>
concept item = std::copyable<Item> && requires(const Item& const_item, Item& item) {
    { item.value() } -> std::same_as<int&>;
    { const_item.value() } -> std::same_as<const int&>;
    { item.description() } -> std::same_as<std::string&>;
    { const_item.description() } -> std::same_as<const std::string&>;
    { item.display() };
    { const_item.display() };
};

在 C++20 之前,概念通常是隐式的或记录的,而不是代码中的实体。但在 C++20 中,我们可以定义它们。请注意,此公式需要 std::copyable,因此任何满足此条件的 item 都可以使用标准复制构造函数进行复制。另外,请注意,接口中的原始数据成员会使事情稍微复杂一些,因此我将它们替换为访问器,同时仍然允许 public 读写访问问题中的代码允许。

定义了接口后,就可以定义一个类型擦除版本:定义一个 any_item 满足 item 的类型,它本身可以包含满足 item 的任何类型的值。您可能会找到许多关于类型擦除和相关选择的在线资源。关键是要为对象提供存储空间,并为接口提供 table 函数指针 (a vtable)。可以将对象存储在堆上,或者为小对象设置一个小的内部缓冲区。可以内联存储 vtable 或存储指向 vtable 的指针。可以显式地编写 vtable,或者依靠隐藏的 class 层次结构来强制编译器编写一个。甚至可以依靠图书馆(例如 dyno)为您做这件事。

请注意,所有这些复杂性都由界面或库作者处理。用户不必继承任何东西。用户不必定义样板 clone 函数。用户只需定义概念所需的功能。然后用户可以使用类型擦除类型,并像使用任何其他值类型一样使用它。用户可以将它们放在 std::vector 中并观察复制构造函数的工作。

看起来像的代码:

std::vector<item*> inventory;

~v2d()
{
    for (auto* item: inventory) {
        delete item;
    }
}

v2d(const v2d& orig) :
    inventory(orig.inventory.size())
{
    for (int i = 0; i < inventory.size(); i++) {
        inventory[i] = new item(*orig.inventory[i]);
    }
    cout << "Copied!" << endl;
}

vect1.inventory.push_back(new Cat());

变为:

// just use values
std::vector<any_item> inventory;

// destruction just works
~v2d() = default;

// copy just works
v2d(const v2d& other) :
    inventory(other.inventory)
{
    std::cout << "Copied!\n";
}

// just use values, again
vect1.inventory.push_back(cat());

See an example