|
Программирование >> Разработка устойчивых систем
assert(db->refCount() == 2); } III:- Функция DBConnection::create() вызывает attach(), поэтому после завершения работы с объектом необходимо освободить захваченное подключение явным вызовом detach(). Обратите внимание: класс DBClient также управляет соединением при помощи RAIL При завершении программы деструкторы двух объектов DBClient уменьшают счетчик ссылок (вызовом функции detachQ, унаследованной DBConnection от Countable). Подключение к базе данных закрывается (из-за виртуального деструктора Countable) при падении счетчика до нуля после уничтожения объекта с1. Подключение функциональности через наследование чаще осуществляется с применением шаблонов, чтобы пользователь мог на стадии компиляции выбрать нужную разновидность. Это позволяет задействовать разные механизмы подсчета ссылок без повторного определения DBConnection. Вот как это делается: : C09:DBConnection2.h Параметризованное подключение функциональности #ifndef DBCONNECTION H #def1ne DBCONNECTION H #1nclude Database.h #include <cassert> #include <string> using std::string: tempiate<class Counter> class DBConnection : public Database, public Counter { DBConnection(const DBConnectionS): Запрет копирования DBConnectionSt operator=(const DBConnection&): protected: DBConnection(const 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: Другие нужные функции... #endif DBC0NNECTI0N2 H III:- Единственное изменение - появление шаблонного префикса в определении класса (и переименование Countable в Counter для ясности). Класс доступа к базе данных тоже можно было бы оформить в виде параметра шаблона (если бы у нас было несколько классов доступа, из которых выбирался бы нужный вариант), но на этот раз класс получился вполне самостоятельным. В следующем примере исходная реализация Countable передается в качестве аргумента шаблона, но с таким же успехом можно было бы использовать любой тип, реализующий нужный интерфейс (attach(), detach() и т. д.): : C09:UseDatabase3.cpp Подключение функциональности через шаблон #include <cassert> #include Countable.h Дублирование подобъектов При наследовании в производный класс включаются копии всех переменных базового класса. Следующая программа демонстрирует возможное размещение нескольких базовых подобъектов в памяти: : С09:Offset.срр Размещение подобъектов в памяти при множественном наследовании linclude <iostream> using namespace std: class A { int x: }: class В { int у: }: class С : public A. public В { int z: }: int mainO { cout sizeof(A) == sizeof(A) endl cout sizeof(B) == sizeof(B) endl cout sizeof(C) == sizeof(C) endl С с: cout &c == &c endl: Конкретный вид результатов зависит от компилятора. linclude DBConnection2.h class DBClient { DBConnection<Countable>* db: public: DBClient(DBConnection<Countable>* dbCon) { db = dbCon: db->attach(): -DBClientО { db->detach(): } int mainO { DBConnection<Countable>* db = DBConnection<Countable>::create( MyDatabase ): assert(db->refCount() ==1): DBClient cl(db): assert(db->refCount() == 2): DBClient c2(db): assert(db->refCount() == 3): db->detach(): assert(db->refCount() == 2); } III:- Общий паттерн для нескольких подключаемых классов выглядит так: tempiate<class Mixinl. class Mixin2.....class MixinK> class Subject : public Mixinl. public Mixin2. public MixinK {...}: As data Bs data Cs data Объект с начинается с подобъекта А, затем следует подобъект В и, наконец, -данные самого типа С. Поскольку С является частным случаем А и В, объекты этого класса могут быть преобразованы к любому из базовых типов. При повышении до типа А полученный указатель ссылается на подобъект А, начало которого совпадает с началом объекта С, поэтому адрес ар совпадает со значением &с. Но при повышении до типа В полученный указатель должен указывать на фактическое начало подобъекта В, так как класс В ничего не знает о классе С (не говоря уже о классе А). Другими словами, объект, на который ссылается bp, должен быть способен вести себя как автономный объект В (за исключением механизма полиморфного вызова). Что же происходит при обратном преобразовании bp в С*? Поскольку исходный объект все-таки относился к типу С, местонахождение подобъекта В известно, поэтому указатель снова переводится на исходный адрес объекта производного класса. Если бы указатель bp изначально ссылался на автономный объект В вместо объекта С, такое преобразование было бы недопустимым. Более того, в сравнении bp == sp указатель ср неявно преобразуется к В*, поскольку это един- Хотя ко.мпилятор бы не обнаружил ошибку. Впрочем, проблема решается при помощи оператора dynamiccast - за подробностями обращайтесь к предыдущей главе. А* ар = &с: В* bp = &с; cout ар == static cast<vo1cl*>(ap) endl: cout bp == static cast<vo1d*>(bp) endl: C* cp = static cast<C*>(bp): cout cp == static cast<void*>(cp) endl: cout bp == cp? boolalpha (bp == cp) endl: cp = 0: bp = cp: cout bp endl: /* Результат: sizeof(A) == 4 s1zeof(B) == 4 sizeof(C) == 12 &c == 1245052 ap == 1245052 bp == 1245056 cp == 1245052 bp == cp? true 0 */ III:- Как видите, подобъект В объекта с находится на расстоянии 4 байт от начала всего объекта. Можно предположить следующую структуру памяти:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки. |