В C ++, как я помещаю объект в вектор, поддерживая указатель на объект? - PullRequest
0 голосов
/ 12 октября 2009

В моем коде у меня есть вектор объектов 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" заканчивается копией объекта Student, на который указывает "targetStudent" в то время.

Последующая попытка добавления в targetStudent не изменяет объект, содержащийся в векторе.

Как я могу, начиная с указателя на объект, добавить этот объект в вектор и затем получить доступ к объекту, который находится в векторе?

Ответы [ 3 ]

9 голосов
/ 12 октября 2009

Получить указатель на объект после того, как он был вставлен в вектор, поэтому вместо

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

использовать

m_students.push_back(Student(strName));
targetStudent = &m_students.back();

Также обратите внимание, что в вашем примере утечка памяти, targetStudent, скопированный в вектор, никогда не удаляется.

Кроме того, имейте в виду, что указатели на вектор становятся недействительными при добавлении новых элементов (если вектор увеличивается в физическом размере и элементы должны быть скопированы в новый, больший вектор, все указатели в предыдущий буфер становятся недействительными).

6 голосов
/ 12 октября 2009

Контейнеры STL копируют содержащиеся в них объекты. Обойти это невозможно.

Вы можете, однако, иметь std::vector<std::shared_ptr<Student> >, который позволит вам иметь контейнер умных указателей. Однако, чтобы это работало, все ваши объекты должны быть прикреплены к 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 определено в заголовке <memory> в C ++ 11. (В TR1 вы можете использовать std::tr1::shared_ptr вместо этого.) Если вы используете C ++ 98 без TR1 или вам нужно переносить его, вы можете использовать boost::shared_ptr вместо этого; скачать с Boost .

5 голосов
/ 12 октября 2009

Вы уже получили достаточно прямой ответ на свой вопрос. Однако, исходя из того, что вы, похоже, пытаетесь выполнить, мне кажется, что менее прямой ответ действительно может быть лучше.

По крайней мере, когда я читаю ваше описание, у вас есть несколько уникальных студентов и количество курсов для каждого. Когда студент закончил курс, вы хотите найти его. Если их нет в коллекции, добавьте их. Затем добавьте данные для курса, который они закончили.

В таком случае вектор кажется мне не идеальным решением. Вы можете реализовать код несколькими различными способами, но я бы, вероятно, сделал это так:

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));

Возвращаясь к исходному вопросу, стандартные контейнеры предназначены для работы с значениями . Вы передаете им значение, и они сохраняют копию этого значения. Одним из следствий этого является то, что что-то вроде push_back(new XXX) по сути всегда ошибка (в значительной степени гарантированная утечка памяти). Если у вас есть объект, просто передайте его. Если вы этого не сделаете, просто создайте временный и передать это. В Java (для одного примера) видение new XXX повсюду является рутинным и почти неизбежным. В то время как вы можете также написать C ++ таким же образом, этого, как правило, не следует ожидать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...