Использование RAII с указателем символов - PullRequest
4 голосов
/ 02 февраля 2010

Я вижу много примеров классов RAII, обертывающих дескрипторы файлов.

Я безуспешно пытался адаптировать эти примеры к указателю символов.

В библиотеке, которую я использую, есть функции, которые принимают адрес указателя символа (объявлен как get_me_a_string (char ** x)). Эти функции выделяют память для этого символьного указателя и предоставляют конечному пользователю библиотеки возможность очищать ее в своем собственном коде.

Итак, у меня есть код, который выглядит следующим образом ...

char* a = NULL;
char* b = NULL;
char* c = NULL;

get_me_a_string(&a);
if(a == NULL){
    return;
}


get_me_a_beer(&b);
if(b == NULL){
    if(a != NULL){
        free(a);
    }
    return;
}


get_me_something(&c);
if(c == NULL){
    if(a != NULL){
        free(a);
    }
    if(b != NULL){
        free(b);
    }
    return;
}

if(a != NULL){
    free(a);
}
if(b != NULL){
    free(b);
}
if(a != NULL){
    free(b);
}

Звучит так, будто RAII - это ответ на этот беспорядок, который я описал выше. Может ли кто-нибудь предоставить простой класс C ++, который заключает в себе символ *, а не ФАЙЛ *?

Спасибо

Ответы [ 8 ]

5 голосов
/ 02 февраля 2010

В стандартной библиотеке уже есть что-то доступное: оно называется std::string.

Редактировать: В свете новой информации:

Он выделит память и заполнит ее вверх. Я мог бы скопировать содержимое в новый объект std :: string, но я бы все равно должны освободить память, которая была назначается функцией.

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

Хорошо, теперь, когда я получил это из моей системы: вы можете использовать boost::shared_ptr для освобождения.

template<typename T>
struct free_functor
{
    void operator() (T* ptr)
    {
        free(ptr);
        ptr=NULL;            
    }
};
shared_ptr<X> px(&x, free_functor());
2 голосов
/ 02 февраля 2010

Очень базовая реализация (которую вы должны сделать некопируемой и т. Д.).

struct CharWrapper {
    char* str;
    CharWrapper(): str() {}  // Initialize NULL
    ~CharWrapper() { free(str); }
    // Conversions to be usable with C functions
    operator char**() { return &str; }
    operator char*() { return str; }
};

Технически это не RAII, так как правильная инициализация происходит позже, чем в конструкторе, но она позаботится об очистке.

1 голос
/ 02 февраля 2010

Вы можете попробовать что-то вроде этого:

template <typename T>
class AutoDeleteArray
{
public:
    explicit AutoDeleteArray(const T* ptr)
        : ptr_(ptr)
    {}
    ~AutoDeleteArray()
    {
        delete [] ptr_;
        // if needed use free instead
        // free(ptr_);
    }

private:
    T *ptr_;
};

// and then you can use it like:
{
    char* a = NULL;

    get_me_a_string(&a);
    if(a == NULL)
      return;

    AutoDeleteArray<char> auto_delete_a(a);
}

Это не самое надежное решение, но может быть достаточно для этой цели.

PS: Мне интересно, будет std::tr1::shared_ptr с нестандартной работой удалителя?

0 голосов
/ 03 февраля 2010

Поскольку вы говорите, что не можете использовать повышение, не очень сложно написать очень простой умный указатель, который не разделяет и не передает ресурсы.Вы можете указать функтор удаления в качестве параметра шаблона.Я не особенно люблю операторы преобразования, поэтому вместо этого используйте метод get ().

Добавьте другие методы, такие как release () и reset (), по желанию.

0 голосов
/ 02 февраля 2010

Альтернативное решение было бы примерно таким: я бы написал этот код на C:

char* a = NULL;
char* b = NULL;
char* c = NULL;

get_me_a_string(&a);
if (!a) {
    goto cleanup;
}

get_me_a_beer(&b);
if (!b) {
    goto cleanup;
}

get_me_something(&c);
if (!c) {
    goto cleanup;
}

/* ... */

cleanup:
/* free-ing a NULL pointer will not cause any issues
 * ( see C89-4.10.3.2 or C99-7.20.3.2)
 * but you can include those checks here as well
 * if you are so inclined */
free(a);
free(b);
free(c);
0 голосов
/ 02 февраля 2010

Спасибо всем за ваши ответы.

К сожалению, я не могу использовать boost или другие библиотеки в этом проекте ... поэтому все эти предложения для меня бесполезны.

Я посмотрелна такие вещи, как обработка исключений в C, как здесь ... http://www.halfbakery.com/idea/C_20exception_20handling_20macros

А потом я посмотрел на то, почему в C ++ нет окончательно, как в Java, и натолкнулся на этот материал RAII.Я до сих пор не уверен, пойду ли я деструктором и сделаю код только на C ++, или останусь с макросом исключений C (который использует страшный goto:)

Tronic предложил что-то вроде следующего.С RAII, или деструкторами в целом, они должны быть защищены от сегрегаций?Наверное, нет.

Единственное, что мне не нравится, это то, что мне теперь приходится использовать приведение (char *) в моих выражениях printf.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct CharWrapper {
    char* str;
    CharWrapper(): str() {}  // Initialize NULL
    ~CharWrapper() {
        printf("%d auto-freed\n", str);
        free(str);
    }
    // Conversions to be usable with C functions
    operator char*()  { return  str; }
    operator char**() { return &str; }
};

// a crappy library function that relies
// on the caller to free the memory
int get_a_str(char **x){
    *x = (char*)malloc(80 * sizeof(char));
    strcpy(*x, "Hello there!");
    printf("%d allocated\n", *x);
    return 0;
}


int main(int argc, char *argv[]){
    CharWrapper cw;
    get_a_str(cw);
    if(argc > 1 && strcmp(argv[1], "segfault") == 0){
        // lets segfault
        int *bad_ptr = NULL;
        bad_ptr[8675309] = 8675309;
    }
    printf("the string is : '%s'\n", (char*)cw);
    return 0;
}
0 голосов
/ 02 февраля 2010

Либо используйте обычный std::string, либо boost :: scoped_array для локальных массивов, либо boost :: shared_array для общих строк (последний позволяет вам предоставить пользовательское средство удаления для вызова free().)

0 голосов
/ 02 февраля 2010

я думаю, что auto_ptr это то, что вы хотите

или увеличьте shared_ptr, если семантика auto_ptr не работает для вас

...