Например:

class HttpRequest {

public:

 HttpRequest();

 void open(const std::string& hostname);

 void send(std::string soapMsg);

 void close();

 ~HttpRequest();

private:

 Socket* sock_;

};

При таком подходе соответствующая версия sendMyData может выглядеть так.

void sendMyData(std::string soapMsg, std::string host) {

 HttpRequest req;

 try {

  req.open();

  req.send(soapMsg);

  req.close();

 } catch (std::exception& e) {

  req.close(); // Do something useful...

 }

}

Здесь требуется выполнить больше работы без каких бы то ни было преимуществ. Этот дизайн заставляет пользователя писать больше кода и работать с исключениями, очищая ваш класс (при условии, что в деструкторе close не вызывается).

Подход RAII имеет широкое применение, особенно когда требуется гарантировать, что при выбрасывании исключения будет выполнен «откат» каких-либо действий, позволяя при этом не загромождать код бесконечными try/catch. Рассмотрим настольное приложение, которое в процессе выполнения какой-либо работы отображает в строке состояния или заголовка сообщение.

void MyWindow : thisTakesALongTime() {

 StatusBarMessage('Copying files...');

 // ...

}

Все, что класс StatusBarMessage должен сделать, — это использовать информацию о статусе для обновления соответствующего окна при создании и вернуть его первоначальное состояние при удалении. Вот ключевой момент: если функция завершает работу или выбрасывается исключение, StatusBarMessage все равно выполнит работу. Компилятор гарантирует, что при выходе из области видимости стековой переменной для нее будет вызван ее деструктор. Без этого подхода автор thisTakesALongTime должен был бы принять во внимание все пути передачи управления, чтобы неверное сообщение не осталось в окне при неудачном завершении операции, ее отмене пользователем и т.п. И снова повторю, что этот подход приводит к уменьшению кода и снижению числа ошибок автора вызывающего кода.

RAII не является панацеей, но если вы его еще не использовали, то вы, скорее всего, найдете немало возможностей для его применения. Еще одним хорошим примером является блокировка. При использовании RAII для управления блокировками ресурсов, таких как потоки, объекты пулов, сетевые соединения и т.п., этот подход позволяет создавать более надежный код меньшего размера. На самом деле именно так многопоточная библиотека Boost реализует блокировки, делая программирование пользовательской части более простым. За обсуждением библиотеки Boost Threads обратитесь к главе 12.

8.4. Автоматическое добавление новых экземпляров класса в контейнер

Проблема

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

Решение

Включите в класс статический член, являющийся контейнером, таким как list, определенный в <list>. Добавьте в этот контейнер адрес объекта при его создании и удалите его при уничтожении. Пример 8.4 показывает, как это делается.

Пример 8.4. Отслеживание объектов

#include <iostream>

#include <list>

#include <algorithm>

using namespace std;

class MyClass {

protected:

 int value_;

public:

 static list<MyClass*> instances_;

 MyClass(int val);

 ~MyClass();

 static void showList();

};

list<MyClass*> MyClass::instances_;

MyClass::MyClass(int val) {

 instances_.push_back(this);

 value_ = val;

}

MyClass::~MyClass() {

 list<MyClass*>::iterator p =

  find(instances_.begin(), instances_.end(), this);

 if (p != instances_.end()) instances_.erase(p);

}

void MyClass::showList() {

 for (list<MyClass*>::iterator p = instances_.begin();

  p != instances_.end(); ++p)

  cout << (*p)->value_ << endl;

}

int main() {

 MyClass a(1);

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату