如何通过复制构造函数深度复制派生 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());
#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());