【问题标题】:C++ How to properly copy container(vector) of pointers?C ++如何正确复制指针的容器(向量)?
【发布时间】:2018-03-22 01:41:37
【问题描述】:

有几次我偶然发现我有一个需要复制的指针容器。

假设我们有以下类层次结构:

  • 学生(基类)

    • 大一(子类)
    • Sophmore(子类)
    • 初级(子类)
    • 高级(子类)
  • 学生服务

StudentService 类有一个std::vector<Student*> students 字段和以下构造函数:

StudentService::StudentService(std::vector<Student*> students) {
   // code
}

只使用std::vector::operator= 运算符并写this-&gt;students = students 是不正确的,因为这只会复制指针地址,因此如果外部人员删除了这些指针指向的对象,那么 StudentService班级会因此受到影响。

解决方案是循环遍历students参数中的每个指针,并创建一个新的动态对象,如下所示:

for(int i = 0; i < students.size(); i++) {
   this->students.at(i) = new Student(*students.at(i));
}

但即使这样也不合适,因为它只会创建 Student 对象。我们知道学生可以是大一、大二、大三或大四。所以这是我的问题:这个问题的最佳解决方案是什么?

我想一种方法是在每个 Student 类中放置一个私有枚举字段,并有 4 个 if-else 语句检查它是什么类型的 Student,然后基于它创建一个新的动态对象,如下所示:

 for(int i = 0; i < students.size(); i++) {
   if(students.at(i).getType() == FRESHMAN) {
      this->students.at(i) = new Freshman(*students.at(i));
   } else if(students.at(i).getType() == SOPHMORE) {
      this->students.at(i) = new Sophmore(*students.at(i));
   } else if {
   // and so on...
   }
}

但这似乎仍然很麻烦,那么您有什么建议呢?

【问题讨论】:

  • 忘记向量。您将如何从Student 指针创建Freshman?这就是您需要在 SO 上搜索的问题。
  • 学生是否因为在不同地方被“使用”而被克隆到你的大学?
  • 您可以使用智能指针(在本例中为std::shared_ptr)来确保即使一个副本被销毁,对象也能保持活动状态。

标签: c++ pointers containers dynamic-memory-allocation


【解决方案1】:

您正在寻找克隆模式。 向 Student 添加一个 clone() 虚函数,该函数在每个后代中被覆盖并创建适当的副本。然后按照您正确指定的方式编写容器的深层副本。

编辑:我的工作假设是您的新生等课程是学生的后代。如果不是,请使用变体并应用复制访问者。

【讨论】:

【解决方案2】:

解决所有权问题

如果您认为在您的模块之间共享Student-s,那么您将面临所有权问题,我建议使用std::shared_ptr&lt;Student&gt;-s 的向量来解决它。

如果您有std::vector&lt;std::shared_ptr&lt;Student&gt;&gt;,您可以将其传递给您想要的任何人。接收者可以使用赋值运算符复制vector,然后他添加/删除的任何对象都不会影响原始容器,就像您希望的那样。

解决克隆问题

如果您对每个模块都有自己的Student-s 向量副本感兴趣,那么您将面临克隆问题。

您可以通过将以下方法添加到您的类来解决它:

class Student {
    [..]
    virtual Student * clone() const = 0; // Assuming Student is abstract, otherwise implement as well
};

class Freshman : public Student {
    [..]
    virtual Freshman * clone() const { return new Freshman(*this); }
};

// Same for other derived classes...

然后使用std::transform复制向量:

// students is the original std::vector<Student *>
std::vector<Student *> copy(students.size());
std::transform(students.begin(), students.end(), copy.begin(), [](Student * s) -> Student * { return s->clone(); });

顺便说一句,这是大二,不是大二...

【讨论】:

  • 我认为这不是 OP 想要的。当我相信 OP 想要一个深拷贝时,使用 std::shared_ptr 会给你一个浅拷贝。
  • @NathanOliver,根据 OP:“因为这只会复制指针地址,所以如果有人从外部删除这些指针指向的对象,那么 StudentService 类将因此受到影响”。这肯定会防止它发生。
  • 是的,它使它安全,但现在两个向量仍然指向同一个对象。我相信 OP 希望两个向量都有自己指向的对象。
  • @NathanOliver 不清楚这是否是他想要的。他明确指出的唯一问题是删除仍在使用的对象。创建深拷贝似乎是他建议的解决方案之一,但其本身不一定是目标。除非他澄清,否则我们无法真正知道。无论如何,以他的方式使用原始指针通常是一个坏主意,所以如果他不使用 shared_ptr 他可能至少应该使用 unique_ptr...
  • 我和 Nathan 在一起,否则 StudentServices 会引用数组。如果您希望它引用相同的对象,则没有理由复制任何内容(包括指针)。
猜你喜欢
  • 2020-06-19
  • 1970-01-01
  • 2018-03-19
  • 2019-04-17
  • 2015-12-31
  • 2011-10-20
  • 2016-05-20
  • 2016-09-25
相关资源
最近更新 更多