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

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


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 байт от начала всего объекта. Можно предположить следующую структуру памяти:



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

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