Освобождение локальных буферов при создании исключений в C ++ - PullRequest
2 голосов
/ 06 декабря 2011

Предположим, у меня есть следующий конструктор в классе C ++:

MyClass::MyClass()
{
    char* buffer = malloc(100);
    if (0 != someD3DXCallThatCanFail(..., buffer, ...))
    {
        free(buffer);
        throw MyException(L"some message");
    }
    char* buffer2 = malloc(200);
    if (0 != anotherD3DCallThatCanFail(..., buffer2, ...))
    {
        free(buffer);
        free(buffer2);
        throw MyException(L"another message");
    }
    .. more code here where buffer and buffer2 are still used

    free(buffer);
    free(buffer2);
}

РЕДАКТИРОВАТЬ: я ненавижу malloc / free и new / delete, но, к сожалению, мне нужно использовать буферы для загрузки текстур, которые затем передаются в ID3D10ShaderResourceView, ID3D10Buffer, буфер вершин и тому подобное. Все те требуют указателей на буфер.

Что я пытаюсь сделать, чтобы использовать исключения вместо возврата кодов ошибок. Также я хочу создать буферы там, где они нужны, и освободить их сразу после того, как они больше не нужны.

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

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

Это также раздувает код.

Ключевое слово finally решит эту проблему в Java или C #. Независимо от того, где в коде произошло исключение, я все равно очистил бы все эти буферы в "finally" (кстати, это не понадобится при сборке мусора). В C ++, насколько я понимаю, мне, возможно, придется создать переменную-член для любого такого буфера, а в деструкторе убедиться, что буферы очищены. Мне это тоже кажется уродливым, поскольку переменная-член с именем "pBuffer", даже приватная, является просто мусором, поскольку она используется только в одном методе (в нашем случае, конструкторе) и всегда будет NULL в остальной части время.

Должно быть, общая проблема, однако мне не удалось найти ответ с помощью поиска. Спасибо!

Ответы [ 6 ]

8 голосов
/ 07 декабря 2011

Прекратите управлять памятью вручную, и у вас не возникнет подобных проблем. Используйте что-то вроде std::vector<char>.

В качестве альтернативы, вы можете использовать что-то вроде Boost's shared_array, но это излишне для того, что вы пытаетесь сделать здесь:

http://www.boost.org/doc/libs/1_41_0/libs/smart_ptr/shared_array.htm

Более общее замечание, которое следует здесь сделать, заключается в том, что вам следует использовать идиому RAII - то есть, когда вы получаете ресурсы, вы сохраняете их в экземпляре класса, деструктор которого снова их освобождает. Затем, несмотря на то, что экземпляр этого класса, владеющего ресурсами, выходит из области видимости, ресурсы гарантированно освобождаются.

Смотрите здесь:

http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

4 голосов
/ 07 декабря 2011

Каноническим ответом является принцип «Приобретение ресурсов - инициализация» (RAII) и умные указатели. Вы создаете экземпляр класса в стеке, который освобождает память в его деструкторе, например boost::scoped_ptr.

2 голосов
/ 07 декабря 2011

Вместо этого используйте raii :

MyClass::MyClass()
{
    std::vector<char> buffer(100);
    if (0 != someD3DXCallThatCanFail(...))
    {
        throw MyException(L"some message");
    }
    std::vector<char> buffer2c(200);
    if (0 != anotherD3DCallThatCanFail(...))
    {
        throw MyException(L"another message");
    }
    .. more code here 
}
1 голос
/ 07 декабря 2011

Каноническим ответом на это будет unique_ptr в C ++ 11.До тех пор, вероятно, scoped_ptr (http://www.boost.org/doc/libs/1_47_0/libs/smart_ptr/scoped_ptr.htm) для синглетонов и scoped_array (http://www.boost.org/doc/libs/1_47_0/libs/smart_ptr/scoped_array.htm) для массивов, оба из Boost. Или вы можете кодировать эквивалент самостоятельно.

1 голос
/ 07 декабря 2011

Выполните поиск снова для «умных указателей C ++». Вам нужна безопасная для исключений обертка в памяти вместо raw malloc, которая, как вы заметили, приносит много головной боли и, кстати, может быть лучше заменить на operator new теперь вы пишете на C ++, а не на C-коде.

Предыдущий ответ здесь охватывает эту область.

1 голос
/ 07 декабря 2011

Используйте для этого идиоматический подход C ++: RAII . На этой странице Википедии есть образец C ++.

...