Как избежать многократного освобождения - PullRequest
1 голос
/ 26 декабря 2010

Структура Scene имеет указатель на (связанный список) объектов SceneObject.Каждый SceneObject ссылается на Mesh.Однако некоторые объекты SceneObject могут ссылаться на одну и ту же сетку (используя один и тот же указатель - или дескриптор, см. Позже - для сетки).Меши довольно большие, и у этого способа есть очевидные преимущества для скорости рендеринга.

typedef struct {
    Mesh *mesh; 
        ...
    struct SceneObject *next;
} SceneObject;

typedef struct Scene {
    SceneObject *objects;
    ...
} Scene;

Мой вопрос: как мне освободить сцену, избегая при этом многократного освобождения одного и того же указателя сетки?

Я думал, что смогу решить эту проблему, используя дескриптор Mesh (Mesh** mesh_handle) вместо указателя, чтобы я мог установить указатель Mesh, на который есть ссылка, равным 0, и позволить последовательным освобождениям его просто освободить 0, но я не могу заставить его работать.Я просто не могу придумать, как избежать множественного освобождения.

Я вынужден хранить ссылки для такого сценария?Или я вынужден поместить все объекты Mesh в отдельную таблицу Mesh и освободить ее отдельно?Есть ли способ справиться с этим, не делая эти вещи?Помечая объекты как экземпляры друг друга, я, естественно, могу настроить свободный алгоритм так, чтобы он решал проблему, но мне было интересно, есть ли более «чистое» решение для этой проблемы.

1 Ответ

1 голос
/ 26 декабря 2010

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

typedef struct T_Object
{
    int refcount;
    ....
} Object;

Object *newObject(....)
{
    Object *obj = my_malloc(sizeof(Object));
    obj->refcount = 1;
    ....
    return obj;
}

Object *ref(Object *p)
{
    if (p) p->refcount++;
    return p;
}

void deref(Object *p)
{
    if (p && p->refcount-- == 1)
        destroyObject(p);
}

Кто первым выделит объект, будет первым владельцем (следовательно, счетчик инициализирован в 1). Когда вам нужно хранить указатель в других местах каждый раз, когда вы должны хранить ref(p) instad, чтобы обязательно увеличить счетчик. Когда кто-то больше не собирается указывать на это, вам следует позвонить deref(p). Как только последняя ссылка на объект исчезнет, ​​счетчик станет равным нулю, а вызов deref фактически уничтожит объект.

Требуется некоторая дисциплина, чтобы заставить его работать (вы должны всегда думать, вызывая ref и deref), но с помощью этого подхода можно написать сложное программное обеспечение с нулевыми утечками.

Более простое решение, которое иногда применимо, заключается в том, что все ваши общие объекты также хранятся в отдельном списке ... вы свободно назначаете и изменяете сложные структуры данных, указывающие на эти объекты, но никогда не освобождаете их во время обычного использования. Только когда вам нужно все выбросить, вы освобождаете эти объекты, используя этот отдельный список. Обратите внимание, что такой подход возможен только в том случае, если вы не выделяете много объектов во время «обычного использования», поскольку в этом случае задержка уничтожения может оказаться нежизнеспособной.

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