Программирование >>  Разработка устойчивых систем 

1 ... 139 140 141 [ 142 ] 143 144 145 ... 196


одинаков. Это одна из форм слабой типизации, приемлемая для большинства современных программистов С-ы-.

Наследование реализации

Как уже упоминалось, в С-ы- поддерживается только реализация наследования, то есть все аспекты базового класса всегда наследуются полностью. Иногда это удобно, потому что программисту не приходится беспокоиться о самостоятельной реализации всех аспектов производного класса, как в рассмотренном примере с наследованием интерфейса. Одно из стандартных применений множественного наследования - так называемые подключаемые классы, то есть классы, предназначенные для наделения других luiaccoB новыми возможностями через механизм наследования. Самостоятельные экземпляры подключаемых классов создаваться не должны.

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

: C09:Database.h Прототип ресурсного класса #ifndef DATABASE H #define DATABASE H #include <iostream> iinclude <stdexcept> #inc1ude <string>

struct DatabaseError : runtime error { DatabaseErrorCconst string& msg) : std::runtime error(msg) {}

class Database {

std::string dbid: public:

DatabaseCconst string& dbStr) : dbid(dbStr) {} virtual ~Database(){} void openO throw(DatabaseError) { std::cout connected to dbid std::endl:

void closeO { std::cout dbid closed std::endl:

Прочие функции базы данных...

#endif DATABASE H /:-

Непосредственные операции с базой данных (сохранение, выборка и т. д.) не приводятся, для нас они несущественны. При конструировании объекта класса задается строка подключения. Пользователь подключается к базе данных функцией Database::open() и отключается от нее функцией Database():

: C09:UseDatabase.cpp #include Database.h

int mainO { Database db( MyDatabase ):



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

db.openO;

Вызовы других функций... db.closeO;

/* Выходные данные: connected to MyDatabase MyDatabase closed */ III:-

В типичной архитектуре клиент-сервер клиент работает с несколькими объектами, имеющими общее подключением к базе. База данных со временем должна быть закрыта, но только после того, как все операции с ней будут закончены. Обычно такая функциональность инкапсулируется в классе со счетчиком клиентских объектов, использующих подключение; когда счетчик падает до нуля, подключение автоматически закрывается. Чтобы добавить подсчет ссылок в класс Database, мы средствами множественного наследования смешиваем класс Database с классом Countable и создаем новый класс DBConnection. Подключаемый класс Countable выглядит так:

: C09:Countable.h Подключаемый класс #ifndef COUNTABLE H #define COUNTABLE H #1 nclude <cassert>

class Countable {

long count: protected:

CountableO { count = 0: }

virtual -CountableO { assertCcount ==0): } public:

long attache) { return ++count: } long detach О { return (--count > 0) ? count : (delete this, 0):

long refCountO const { return count: }

#endif COUNTABLE H III:-

Очевидно, что этот класс не является самостоятельным, поскольку его конструктор объявлен защищенным (protected); он может использоваться только из дружественных или производных классов. Важно, что деструктор объявлен виртуальным - он вызывается только из команды delete this в detachO, а мы хотим, чтобы производные объекты правильно уничтожались.

Класс DBConnection наследует как от Database, так и от Countable, и предоставляет статическую функцию create() для инициализации подобъекта Countable. Перед вами пример использования паттерна Фабричный метод, описанного в следующей главе:

: C09:DBConnection.h

Использование подключаемого класса

#ifndef DBCONNECTION H

#define DBCONNECTION H

#i nclude <cassert>



linclude <string> linclude Countable.h linclude Database.h using std::string:

class DBConnection : public Database, public Countable {

DBConnectionCconst DBConnectionS): Запрет копирования

DBConnectionS operator=(const DBConnectionS): protected:

DBConnectionCconst string& dbStr) throw(DatabaseError) : Database(dbStr) { openO: } -DBConnectionO { closeO: } public: static DBConnection*

createCconst string& dbStr) throw(DatabaseError) { DBConnection* con = new DBConnection(dbStr): con->attach():

assert(con->refCount() == 1): return con:

Другие нужные функции... }:

lendif /:- DBCONNECTION H /:-

Мы получаем подключение к базе данных с подсчетом ссылок без модификации класса Database. Новая система с подсчетом ссылок гарантирует, что связь с базой не окажется преждевременно прерванной. При открытии и закрытии подключения (соответственно конструктором и деструктором DBConnection) используется идиома получения ресурсов при инициализации (КАП), упоминавшаяся в главе 1. Это упрощает работу с классом DBConnection:

: C09:UseDatabase2.cpp Тестирование подсчета ссылок linclude <cassert> linclude DBConnection.h

class DBClient {

DBConnection* db: public:

DBClient(DBConnection* dbCon) { db = dbCon: db->attach():

-DBClientО { db->detach():

Другие запросы к базе данных с использованием db...

int mainO {

DBConnection* db = DBConnection::create( MyDatabase ): assert(db->refCountO == 1): DBClient cl(db): assert(db->refCountO == 2): DBClient c2(db): assert(db->refCount() == 3):

Используем базу данных, a затем освобождаем ресурс. захваченный при вызове create. db->detach():



1 ... 139 140 141 [ 142 ] 143 144 145 ... 196

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика