【问题标题】:In C++, how do I push an object to a vector while maintaining a pointer to the object?在 C++ 中,如何在保持指向对象的指针的同时将对象推送到向量?
【发布时间】:2009-10-12 03:04:18
【问题描述】:

在我的代码中,我有一个 Student 对象的向量。

vector<Student> m_students;

我想:

  1. 检查向量是否包含任何具有特定名称的学生。
  2. 如果不存在这样的学生,请添加一个新学生。
  3. 向同名的学生添加数据。

考虑以下代码:

// Check to see if the Student already exists.
Student* targetStudent = NULL;
for each (Student student in m_students)
{
    if (student.Name() == strName)
    {
        targetStudent = &student;
        break;
    }
}

// If the Student didn't exist, add it.
if (targetStudent == NULL)
{
    targetStudent = new Student(strName);
    m_students.push_back(*targetStudent);
}

// Add the course info to the Student.
targetStudent->Add(strQuarter, strCourse, strCredits, strGrade);

当我调用 m_students.push_back(*targetStudent); 时,似乎向量“m_students”最终得到了“targetStudent”当时指向的 Student 对象的副本。

随后尝试添加到 targetStudent 并不会更改向量中包含的对象。

如何从指向对象的指针开始,将该对象添加到向量中,然后访问向量中的对象?

【问题讨论】:

    标签: c++ pointers vector


    【解决方案1】:

    在对象插入向量后获取指向对象的指针,所以不是

    targetStudent = new Student(strName);
    m_students.push_back(*targetStudent);

    使用

    m_students.push_back(学生(strName)); 目标学生 = &m_students.back();

    还请注意,您的示例会泄漏内存,复制到向量中的 targetStudent 永远不会被删除。

    此外,请记住,当添加新元素时,指向向量的指针会变为无效(如果向量的物理大小增加并且必须将元素复制到新的、更大的向量中,则指向前一个缓冲区的所有指针都将变为无效)。

    【讨论】:

    • 这比shared_ptr 提案要简单得多,仅出于这个原因就更好了。
    • 我刚才重读了这个答案,同意我也喜欢它。 +1 我也同意 MSalters 所说的话,即使我写了 shared_ptr 答案,如果 OP 选择这个答案或 Jerry 的答案,我不会有任何问题,这两个都很好。 :-)
    • +1 这实际上与我在 Chris 指出容器将始终进行复制之后最终所做的非常相似。也感谢您指出内存泄漏(我习惯于垃圾收集语言并且不需要经常考虑堆。)
    【解决方案2】:

    STL 容器复制它们包含的对象。没有办法解决这个问题。

    但是,您可以拥有一个std::vector&lt;std::shared_ptr&lt;Student&gt; &gt;,它允许您拥有一个智能指针容器。但是,要使其正常工作,您的对象必须在构造时全部附加到 shared_ptr

    所以,类似:

    std::vector<std::shared_ptr<Student> > m_students;
    
    std::shared_ptr<Student> targetStudent;
    for each (std::shared_ptr<Student> student in m_students)
    {
            if (student->Name() == strName)
            {
                    targetStudent = student;
                    break;
            }
    }
    
    // If the Student didn't exist, add it.
    if (!targetStudent)
    {
            // creates a new Student and attaches it to smart pointer
            targetStudent.reset(new Student(strName));
            m_students.push_back(targetStudent);
    }
    

    std::shared_ptr 在 C++11 的 &lt;memory&gt; 标头中定义。 (在 TR1 中,您可以改用 std::tr1::shared_ptr。)如果您使用没有 TR1 的 C++98,或者需要使用它进行移植,您可以改用 boost::shared_ptr;从Boost下载。

    【讨论】:

    • 我建议在引用标准命名空间时始终使用 ::std 而不是 std。如果有人将自己的命名空间称为“std”(当然不是顶级命名空间,那是不合法的),那么在某些情况下,您最终可能会得到对 std 的模棱两可或令人惊讶的解析。
    • @Omnifarious 有些人也可以#define std foo。你不能为那里的每一个白痴做好准备。
    • @quant_dev:这并不是因为有人可能会在另一个命名空间中创建一个名为 std 的命名空间。所以你习惯于总是用你想要的名字来指代命名空间,而不是今天看起来最方便的名字。
    • 理论上,你是对的。在实践中,约定是没有人会使用std 作为他们的命名空间。
    【解决方案3】:

    您的问题已经得到了相当直接的答案。但是,根据您似乎要完成的工作,在我看来,一个不太直接的答案可能确实是一个更好的答案。

    至少在我阅读您的描述时,您有许多独特的学生,并且每个学生都有许多课程。学生完成课程后,您想寻找该学生。如果它们不在集合中,请添加它们。然后为他们完成的课程添加数据。

    在这种情况下,我认为向量不是理想的解决方案。你可以用几种不同的方式实现代码,但我可能会这样做:

    struct course { 
        std::string Quarter_, Course_, Credits_, Grade_;
    
        using std::string;
        course(string const &q, string const &c, string const &cr, string const &g) 
            : Quarter_(q), Course_(c), Credits_(cr), Grade_(g)
        {}
    };
    
    std::map<std::string, std::vector<course> > m_students;
    

    使用这个,您查找学生的整个序列,如果没有同名的新学生,则插入一个新学生,然后将课程作业添加到(新的或现有的)学生的记录中,结果如下:

    m_students[strName].push_back(course(strQuarter, strCourse, strCredits, strGrade));
    

    回到您最初的问题,标准容器旨在与 values 一起使用。您将一个值传递给它们,它们会存储该值的副本。这样做的一个后果是,像push_back(new XXX) 这样的东西本质上总是 是一个错误(几乎可以保证内存泄漏)。如果你有一个对象,只需传递它。如果你不这样做,只需创建一个临时文件并通过它。在 Java 中(例如)到处看到new XXX 是例行公事,几乎是不可避免的。虽然您也可以以这种方式编写 C++,但通常情况下这不是您所期望的。

    【讨论】:

    • +1 你说得对,这更像是我想要的……如果我的目标是创建一个真正的应用程序。不幸的是,这是一个学校作业,教授非常具体地说明了我们应该使用什么数据结构,并且没有空间进行这种创造性。
    猜你喜欢
    • 2014-01-01
    • 1970-01-01
    • 2011-10-01
    • 2011-02-11
    • 2021-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-19
    相关资源
    最近更新 更多