2012-03-13 6 views
2

Я хочу лучше понять, как реализовать идиому RAII с моими классами, например: Какой рекомендуемый метод для обеспечения указателей является бесплатным()? Правильно в моем классе?RAII - Указатели и область видимости

У меня есть класс, который должен существовать в течение всего времени программы. В духе RAII и потому, что мне нужно передать ссылку на этот класс другим классам, я держу его в shared_ptr (не уверен, что он действительно должен храниться в shared_ptr, но для удовольствия он есть).

В классе ctor я использую 2 буфера (указатели), а затем цикл несколько раз malloc() 'ing, используя буфер, а затем free()' ing. Dtor должен содержать защищенный код для освобождения буферов в случае неудачи.

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

Пример:

class Input 
{ 
private: 
    PSOMETYPE buffer1; 
public: 
    Input(); 
    ~Input(); 
} 

Input::Input() : buffer1(NULL) 
{ 
    for(blahblah) 
    { 
     buffer1 = (PSOMETYPE)malloc(sizeof(SOMETYPE)); 
     // Do work w/buffer1 
     if(buffer1 != NULL) { free(buffer1); buffer1 = NULL } 
    } 
} 

Input::~Input() 
{ 
    if(buffer1 != NULL) { free(buffer1); buffer1 = NULL } 
} 

Учитывая я только использовать буфер в CTOR, имеет смысл объявить его как переменная частный класс? Если я объявлю это в рамках ctor, dtor не будет знать, что он должен освободить.

Я знаю, что это тривиальный пример, и, честно говоря, я мог бы реализовать это, как легко забывая об использовании умного указателя для ссылки на мой класс и имеющего пустой dtor, просто free(), поскольку я делаю внутри цикла , У меня нет наставника или школы, и я не уверен, когда следует придерживаться идиомы RAII.

+3

Это странное сочетание идей C и C++ ... Есть ли причина, по которой вы используете malloc вместо новых или даже лучших STL-шаблонов? Они лучше подходят для концепции RAII. – tmpearce

+0

@tmpearce: Это связано с моей неопытности и использованием MSDN в качестве учебного ресурса. Я ценю ссылки на альтернативы/улучшения. – Lokked

ответ

6

Дух RAII будет использовать локальный объект для управления локально выделенного объекта, а не искусственно привязывая его жизни к объекту, который строится:

class Input 
{ 
    // no pointer at all, if it's only needed in the constructor 
public: 
    Input(); 
    // no explicit destructor, since there's nothing to explicitly destroy 
}; 

Input::Input() 
{ 
    for(blahblah) 
    { 
     std::unique_ptr<SOMETYPE> buffer1(new SOMETYPE); 

     // or, unless SOMETYPE is huge, create a local object instead: 
     SOMETYPE buffer1; 

     // Do work w/buffer1 
    } // memory released automatically here 
} 

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

Когда вам нужно написать свой собственный класс управления, всегда помните Rule of Three: если ваш деструктор удаляет что-то, то поведение копирования по умолчанию для класса почти наверняка приведет к двойному удалению, поэтому вам нужно объявить конструктор копии и оператор копирования-назначения для предотвращения этого. Например, с классом я мог бы написать следующий неправильный код:

{ 
    Input i1;  // allocates a buffer, holds a pointer to it 
    Input i2(i1); // copies the pointer to the same buffer 
}     // BOOM! destroys both objects, freeing the buffer twice 

Самый простой способ предотвратить это, чтобы удалить операции копирования, поэтому код, как это не будет компилироваться:

class Input { 
    Input(Input const&) = delete; // no copy constructor 
    void operator=(Input) = delete; // no copy assignment 
}; 

Старые компиляторы могут не поддерживать = delete; в этом случае вы можете получить почти такой же эффект, объявив их конфиденциально без = delete и не реализуя их.

+0

Спасибо. Это имеет смысл для меня. Ваше первое предложение подвело итоги. В вашем примере неправильного кода было бы исключено исключение из-за копирования уникального_ptr?Мне определенно понадобится реализовать оператор копирования ctor и copy-assign. Еще раз спасибо! – Lokked

+0

@Lokked: Если класс содержит 'unique_ptr', тогда вы получите ошибку компилятора, если попытаетесь скопировать его. Вместо этого вы можете использовать 'shared_ptr', чтобы получить безопасное копирование указателя или вставить объект, а не указатель, чтобы получить копирование объекта. В моем примере, 'unique_ptr' не был бы членом вообще, поскольку он нужен только в конструкторе. –

Смежные вопросы