компилятор borland c ++ не отменит выделение памяти, когда исключение перешло границы конструктора? - PullRequest
1 голос
/ 11 июля 2011

Я использую компилятор командной строки bcc32 от Borland Embarcadero. Рассмотрим эту программу:

    int main(int, char **)
    {
        try
        {
            std::string *a = new string(0xf0000000, ' ');
            ...
            delete a;
        }
        catch(const std::bad_alloc &)
        {
            ...
        }
    }

Когда конструктор std :: string генерирует исключение памяти, стек разматывается и управление передается блоку catch. Компиляторы Gnu встраивают код для автоматического удаления памяти, выделенной для объекта std :: string, как было сказано кем-то, кто прокомментировал ответ в , который удаляет память, выделенную во время «новой» операции, которая имеет исключение в конструкторе? , который я написал. Я запустил программу в http://ideone.com/IRxHX, и в результате никто не освобождает память, выделенную оператором new, если генерируется исключение до того, как результат new будет сохранен в lvalue. В приведенном выше случае переменная «а».

Вопросы: 1 Есть ли способ удалить память, сгенерированную методом 'new' в случае исключения, как часть процедуры размотки стека? 2 Что стандарт C ++ требует от компиляторов в этом случае

Ответы [ 3 ]

2 голосов
/ 13 июля 2011

Я протестировал ваш пример кода с помощью следующего, пошагово просматривая код, чтобы убедиться, что все работает нормально (я взял ваш код QC и изменил его, чтобы он работал как положено):

#include <tchar.h>
#include <string>
#include <iostream>

int count = 0;
bool start = false;

// from your QC code, modified to count instead of std::cout
void *operator new(size_t cbytes)
{
    void *retval = std::malloc(cbytes);
    if (retval == NULL && cbytes != 0) throw std::bad_alloc();
    if (start) count++;
    return retval;
}

// from your QC code, modified to count instead of std::cout
void operator delete(void *block)
{
    if (block != NULL)
    {
        std::free(block);
        if (start) count--;
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    try
    {
        start = true;
        std::string *a = new std::string(0xf0000000, ' ');
        // ....
        delete a;
    }
    catch(const std::bad_alloc&)
    {
    }
    std::cout << count;
    return 0;
}

Я получаю 0 для count, указывая, что частично сконструированная строка освобождает свою память.Протестировано с MSVC ++ 2005/2010 с теми же результатами.

Протестировано с C ++ Builder 2010 (командная строка: bcc32 program.cpp).Он соответствует в соответствии со стандартами C ++.

РЕДАКТИРОВАТЬ: Ах, наконец, увидел, что ваш отчет о контроле качества указывает использование динамического RTL скомандная строка bcc32 -WCR program.cpp.И да, при компиляции с динамическим RTL я вижу проблему.Даже в этом случае мой тестовый пример был бы намного лучше при рассмотрении проблемы.

2 голосов
/ 11 июля 2011

Либо ваш компилятор сломан, либо происходит что-то еще смешное. Реализация требуется для освобождения памяти:

5.3.4 / 17:

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

0 голосов
/ 11 июля 2011

Стандарт C ++ требует, чтобы удаление вызывалось компилятором.

Стандартный раздел C +, который обращается к этому:

15.2 Конструкторы и деструкторы

1 Когда управление переходит от бросающего выражения к обработчику, деструкторы вызываются для всех автоматических объектов, созданных с момента ввода блока try. Автоматические объекты уничтожаются в порядке, обратном завершению их строительства.

2 Объект, который частично построен или частично разрушен, будет иметь деструкторы, выполняемые для всех его полностью построенных базовых классов и не вариантных членов, то есть для подобъектов, для которых главный конструктор (12.6. 2) завершил выполнение, а деструктор еще не начал выполнение. Точно так же, если не делегирующий конструктор для объекта завершил выполнение и делегирующий конструктор для этого объекта завершается с исключением, будет вызван деструктор объекта. Если объект был выделен в новом выражении, соответствующая функция освобождения (3.7.4.2, 5.3.4, 12.5), если таковая имеется, вызывается для освобождения памяти, занятой объектом.

3 Процесс вызова деструкторов для автоматических объектов, созданных на пути от блока try к throwexpression, называется «размоткой стека». [Примечание: если деструктор, вызванный во время разматывания стека, завершается с исключением, std: : прекращение называется (15.5.1). Таким образом, деструкторы должны, как правило, перехватывать исключения и не позволять им распространяться за пределы деструктора. —Конечная записка]

...