Как безопасно освободить ресурсы, выделенные конструктором?

#c #oop

#c #ооп

Вопрос:

Я использую функцию C, которая должна записывать текст в буфер, выделенный в конструкторе. Проблема в том, что мне нужно передать строку конструктору исключения, но после создания исключения память, выделенная для текста, теряется:

 class SomeClass{
public:
    SomeClass(){
        //important code ->
        //...
        //<- important code

        if(0 != some_func_to_get_error()){
            int bufferLen = some_func_to_get_buff_len();
            char* bufferText = new char[bufferLen   1];
            some_func_to_get_text(bufferText, bufferLen); //contains error text
            throw runtime_error(bufferText); //exit from constructor body
            //???
        }
    }
};
  

Теперь текст где-то потерян. Пожалуйста, скажите мне, как решить эту проблему.

Комментарии:

1. Это не «где-то потеряно». Это в объекте exception. Он должен быть освобожден в своем деструкторе, как и любой другой ресурс, приобретенный в конструкторе.

2. @user207421 однако вам придется написать дополнительный код, чтобы освободить буфер. А если пользователь класса не знает, как его освободить?

3. Есть ли возможность использовать std::string вместо этого?

4. @vahancho Какую конструкцию нужно написать, чтобы std :: string мог работать с этой функцией C, которая работает с указателями char?

Ответ №1:

Я предполагаю, что runtime_error в вашем коде выше имеется в виду std::runtime_error ? В этом случае вы можете просто передать ему std::string и освободить буфер. Например

 int bufferLen = some_func_to_get_buff_len();
auto bufferText = std::unique_ptr<char[]> { new char[bufferLen   1] };
some_func_to_get_text(bufferText.get(), bufferLen);
throw runtime_error({ bufferText.get(), bufferText.get()   bufferLen });
  

В качестве альтернативы, определите свой собственный тип исключения:

 #include <utility>
#include <exception>
#include <memory>


class my_error : public std::exception
{
    std::unique_ptr<char[]> msg;

public:
    my_error(std::unique_ptr<char[]>amp;amp; msg) noexcept
      : msg(std::move(msg))
    {
    }

    const char* what() const noexcept override
    {
        return msg.get();
    }
};

…

int bufferLen = some_func_to_get_buff_len();
auto bufferText = std::unique_ptr<char[]> { new char[bufferLen   1] };
some_func_to_get_text(bufferText.get(), bufferLen);
throw my_error(std::move(bufferText));
  

Основное преимущество определения собственных типов исключений заключается в том, что это позволяет обрабатывать эти типы ошибок отдельно. Дополнительным бонусом здесь было бы то, что это также позволяет избежать копирования строки ошибки. Поскольку исключения должны происходить только в исключительных условиях, на самом деле это не должно быть проблемой…

Ответ №2:

Как и в большинстве распределений ресурсов в целом, этот случай может быть обработан с помощью контейнера. Стандартным контейнером для строки является std::string :

 // add   1 to bufferLen if using older standard than C  17
std::string bufferText(bufferLen);
  

Когда строка будет уничтожена, она освободит свой внутренний буфер.

Комментарии:

1. Спасибо. если я передам указатель на строку символов в конструкторе std :: string , освободит ли это память в деструкторе?

2. @sddvxd нет. Строка копирует только содержимое указанной строки. Он не присваивает права собственности на указатель.