Пользователи этого класса должны программировать в терминах указателей и ссылок на Person, потому что невозможно создать экземпляр класса, содержащего чисто виртуальные функции (однако можно создавать экземпляры классов, производных от Person – см. далее). Пользователям интерфейсных классов, как и пользователям классов-дескрипторов, нет нужды проводить перекомпиляцию до тех пор, пока не изменяется интерфейс.
Конечно, пользователи интерфейсных классов должны иметь способ создавать новые объекты. Обычно они делают это, вызывая функцию, играющую роль конструктора для производных классов, экземпляры которых необходимо создать. Такие функции часто называют функциями-фабриками (см. правило 13), или
class Person {
public:
...
static std::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
...
std::cout << pp->name() // использовать объект через
<< “ родился ” // интерфейс Person
<< pp->birthDate()
<< “ и теперь живет по адресу ”
<< pp->address();
... // объект автоматически
// удаляется, когда pp выходит
// из контекста – см. правило 13
Разумеется, где-то должны быть определены конкретные классы, поддерживающие интерфейс такого интерфейсного класса, и вызваны реальные конструкторы. Все это происходит «за кулисами», внутри файлов, содержащих реализацию виртуальных конструкторов. Например, интерфейсный класс Person может иметь конкретный производный класс RealPerson, предоставляющий реализацию унаследованных виртуальных функций:
class RealPerson public Person {
public:
RealPerson(const std::string& name, const Date& birthday,
const Address& addr)
: theName(name), theBirthDate(birthday), theAddress(addr)
{}
virtual ~RealPerson() {}
std::string name() const; // реализация этих функций
std::string birthDate() const; // не показана, но ее
std::string address() const; // легко представить
private:
std::string theName;
Date theBirthDaye;
Address theAddress;
};
Имея класс RealPerson, очень легко написать Person::create:
std::tr1::shared_ptr
const Date& birthday,
const Address& addr)
{
return std::tr1::shared_ptr
}
Более реалистическая реализация Person::create должна создавать разные типы объектов классов-наследников, в зависимости, например, от дополнительных параметров функции, данных, прочитанных из файла или базы данных, переменных окружения и т. п.
RealPerson демонстрирует один из двух наиболее распространенных механизмов реализации интерфейсных классов: он наследует спецификации своего интерфейса от интерфейсного класса Person, а затем реализует функции этого интерфейса. Второй способ реализации интерфейсного класса предполагает использование множественного наследования (см. правило 40).