C ++, «соединение» указателей-членов в нескольких объектах, скопированных из одного исходного объекта - PullRequest
4 голосов
/ 02 июня 2011
#include <iostream>
#include <vector>
#include <cstdlib>
#include <cassert>

struct s_A {
    bool bin;
    s_A(): bin(0) {}
};

class c_A {
public:
    s_A * p_struct;

    c_A(): p_struct(NULL) {p_struct = new s_A [16];}

    void Reset()
    {
        delete [] p_struct;
        p_struct = new s_A [16];
    }
};

int main () 
{   
    srand(1);
    int x = 30;
    std::vector <c_A> objects;
    objects.assign(x, c_A());
    std::vector <c_A> objects_copy;

    for(int q=0; q < x; q++)
    {
        objects_copy.push_back(objects[ rand() % x ]);
        objects_copy[q].Reset();
    }

    for(int q=0; q < 16; q++)
        for(int w=0; w < x; w++)
        {
            // Assertion should not fail, but it does
            assert(!objects_copy[w].p_struct[q].bin);
            objects_copy[w].p_struct[q].bin = true;
        }
}

Каким-то образом указатели в различных скопированных объектах в конечном итоге указывают на одну и ту же память, и утверждение в конечном итоге не выполняется. Такое поведение не происходит, если выполняется на не скопированном векторе. Я думал, что c_A.Reset () должен освободить указатели (через delete []) для указания на новый массив, но я, очевидно, что-то упустил.

1 Ответ

5 голосов
/ 02 июня 2011

Конкретным источником вашей проблемы являются следующие строки:

objects_copy.push_back(objects[ rand() % x ]);
objects_copy[q].Reset();

Проблема в том, что когда вы пытаетесь вставить копии объектов в objects_copy, вы в конечном итоге делаете мелкую копию объектовв objects vector.Это означает, что объекты в двух векторах будут иметь указатели, которые являются копиями друг друга.Следовательно, когда вы вызываете Reset для элементов objects_copy vector, вы освобождаете память, на которую все еще указывают элементы массива objects.

Проблема в том, что вашc_A класс нарушает правило трех .Поскольку ваш класс инкапсулирует ресурс, он должен иметь деструктор, конструктор копирования и оператор присваивания копии.Если вы определите эти три функции, то при попытке скопировать объекты в objects_copy vector у вас будет возможность управлять базовым ресурсом, возможно, путем дублирования или подсчета ссылок.Для получения подробной информации о том, как написать эти функции, посмотрите это описание о том, как написать эти функции.

EDIT : вот более подробное описание того, что происходит:

Проблема в том, что когда вы добавляете объект в vector, вы фактически не сохраняете этот объект в vector.Скорее, вы храните копию этого объекта.Таким образом, когда вы пишете objects_copy.push_back(objects[ rand() % x ]);, вы не сохраняете один и тот же объект в обоих vectors.Вместо этого вы создаете копию одного из объектов из objects и сохраняете ее в objects_copy.Так как ваш тип c_A не имеет определенных функций копирования, это приводит к созданию мелкой копии объекта, которая создает копию указателя.Это означает, что если вы думаете об исходном объекте из списка objects и его соответствующей копии в objects_copy, у каждого из них будет копия одного и того же указателя p_struct.Когда вы вызываете Reset для объекта в objects_copy vector, вы освобождаете память, на которую указывает указатель.Однако вы не обновили указатель исходного объекта, хранящегося в objects, и теперь этот указатель ссылается на мусорную память.Попытка использовать этот указатель приводит к неопределенному поведению, что приводит к сбою.

Добавление функций копирования исправит это, позволив вам контролировать процесс создания копии.Если вы определяете функцию копирования для c_A, которая заставляет копию указывать на новую копию объекта, на которую указывает оригинал, эта проблема не возникнет, поскольку у каждого объекта будет свой собственный отдельный указатель.Кроме того, если вы используете подсчет ссылок, то вы можете избежать проблемы, не удаляя ресурс, если знаете, что на него указывает какой-то другой объект.

Надеюсь, это поможет!

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