Вход/Регистрация
Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ
вернуться

Майерс Скотт

Шрифт:

#include “Person.h” // поскольку мы реализуем класс Person,

// то должны включить его определение

#include “PersonImpl.h” // мы должны также включить определение класса

// PersonImpl, иначе не сможем вызывать его

// функции-члены; отметим, что PersonImpl имеет

// в точности те же функции-члены, что и

// Person: их интерфейсы идентичны

Person::Person(const std::string& name, const Date& birthday,

const Address& addr)

: pImpl(new Person(name, birthday, addr))

{}

std::string Person::name const

{

return pImpl->name;

}

Обратите внимание на то, как конструктор Person вызывает конструктор Personlmpl (используя new – см. правило 16), и как Person::name вызывает PersonImpl::name. Это важный момент. Превращение Person в класс-дескриптор не меняет его поведения – изменяется только место, в котором это поведение реализовано.

Альтернативой подходу с использованием класса-дескриптора – сделать Person абстрактным базовым классом специального вида, называемым интерфейсным классом. Его назначение – специфицировать интерфейс для производных классов (см. правило 34). В результате он обычно не содержит ни данных-членов, ни конструкторов, но имеет виртуальный деструктор (см. правило 7) и набор чисто виртуальных функций, определяющих интерфейс.

Интерфейсные классы сродни интерфейсам Java и. NET, но C++ не накладывают на интерфейсные классы тех ограничений, которые присущи этим языкам. Например, ни Java, ни. NET не допускают в интерфейсах наличия членов-данных и реализаций функций-членов. C++ этого не запрещает. Большая гибкость C++ в этом отношении может оказаться кстати. Как объясняется в правиле 36, реализация невиртуальных функций должна быть одинаковой для всех классов в иерархии, поэтому имеет смысл реализовать такие функции, как часть интерфейсного класса, в котором они объявлены.

Интерфейсный класс Person может выглядеть примерно так:

class Person {

public:

virtual ~Person;

virtual std::string name const = 0;

virtual std::string birthDate const = 0;

virtual std::string address const = 0;

...

};

Пользователи этого класса должны программировать в терминах указателей и ссылок на Person, потому что невозможно создать экземпляр класса, содержащего чисто виртуальные функции (однако можно создавать экземпляры классов, производных от Person – см. далее). Пользователям интерфейсных классов, как и пользователям классов-дескрипторов, нет нужды проводить перекомпиляцию до тех пор, пока не изменяется интерфейс.

Конечно, пользователи интерфейсных классов должны иметь способ создавать новые объекты. Обычно они делают это, вызывая функцию, играющую роль конструктора для производных классов, экземпляры которых необходимо создать. Такие функции часто называют функциями-фабриками (см. правило 13), или виртуальными конструкторами. Они возвращают указатели (и лучше бы интеллектуальные, см. правило 18) на динамически распределенные объекты, которые поддерживают интерфейс интерфейсного класса. Нередко подобные функции объявляют как статические внутри интерфейсного класса:

class Person {

public:

...

static std::tr1::shared_ptr<Person> // возвращает tr1::shared_ptr

create(const std::string& name, // на новый экземпляр Person,

const Date& birthday, // инициализированный заданными

const Address& addr); // параметрами: см. в правиле 18,

... // почему возвращается

}; // tr1::shared_ptr

а используют так:

std::string name;

Date datefBirth;

Address address;

...

// создать объект, поддерживающий интерфейс Person

std::tr1::shared_ptr<Person> pp(Person::create(name, dateOfBrth, address));

...

std::cout << pp->name // использовать объект через

<< “ родился ” // интерфейс Person

<< pp->birthDate

<< “ и теперь живет по адресу ”

<< pp->address;

... // объект автоматически

// удаляется, когда pp выходит

// из контекста – см. правило 13

Разумеется, где-то должны быть определены конкретные классы, поддерживающие интерфейс такого интерфейсного класса, и вызваны реальные конструкторы. Все это происходит «за кулисами», внутри файлов, содержащих реализацию виртуальных конструкторов. Например, интерфейсный класс Person может иметь конкретный производный класс RealPerson, предоставляющий реализацию унаследованных виртуальных функций:

  • Читать дальше
  • 1
  • ...
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: