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

Учитывая тот факт, что квадратные скобки не всегда приемлемы для пользователей PersonInfo, в классе предусмотрены виртуальные функции valeDelimOpen и valeDelimClose, позволяющие производным классам задать другие открывающие и закрывающие строки-разделители. Функции-члены PersonInfo вызывают эти виртуальные функции для добавления разделителей к возвращаемым значениям. Так, функция PersonInfo::theName могла бы выглядеть следующим образом:


const char *PersonInfo::valueDelimOpen const

{

return “[“; // открывающий разделитель по умолчанию

}

const char *PersonInfo::valueDelimClose const

{

return “]“; // закрывающий разделитель по умолчанию

}

const char * PersonInfo::theName const

{

// резервирование буфера для возвращаемого значения; поскольку он

// статический, автоматически инициализируется нулями

static char value[Max_Formatted_Field_Value_Length];

// скопировать открывающий разделитель

std::strcpy(value, valueDelimOpen);

добавить к строке value значение из поля name объекта (будьте осторожны –

избегайте переполнения буфера!)

// скопировать закрывающий разделитель

std::strcpy(value, valueDelimClose);

return value;

}


Кто-то может посетовать на устаревший подход к реализации PersonInfo::theName (особенно это касается использования статического буфера фиксированного размера, опасного возможностью переполнения и потенциальными проблемами в многопоточной среде – см. правило 21), но оставим этот вопрос в стороне и сосредоточимся вот на чем: функция theName вызывает valueDelimOpen для получения открывающего разделителя, вставляемого в возвращаемую строку, затем дописывает имя и в конце вызывает valueDelimClose.

Поскольку valueDelimOpen и valueDelimClose – виртуальные функции, возвращаемый результат theName зависит не только от PersonInfo, но и от классов, производных от него.

Для разработчика CPerson это хорошая новость, потому что, внимательно просматривая документацию по функциям печати из класса IPerson, вы обнаруживаете, что функции name и birthDate должны возвращать неформатированные значения, то есть без добавления разделителей. Другими словами, если человека зовут Homer, то вызов функции name должен возвращать «Homer», а не «[Homer]».

Взаимосвязь между CPerson и PersonInfo можно описать так: PersonInfo упрощает реализацию некоторых функций CPerson. И это все! Стало быть, речь идет об отношении «реализован посредством», и, как мы знаем, такое отношение можно представить двумя способами: с помощью композиции (см. правило 38) или закрытого наследования (см. правило 39). В правиле 39 отмечено, что композиция в общем случае более предпочтительна, но если нужно переопределять виртуальные функции, то требуется наследование. В данном случае CPerson должен переопределить valueDelimOpen и valueDelimClose – задача, которая с помощью композиции не решается. Самое очевидное решение – применить закрытое наследование CPerson от PersonInfo, хотя, как объясняется в правиле 39, это потребует несколько больше работы. Можно также при реализации CPerson воспользоваться сочетанием композиции и наследования с целью переопределения виртуальных функций PersonInfo. Но мы остановимся просто на закрытом наследовании.

Однако CPerson также должен реализовать интерфейс IPerson, а для этого требуется открытое наследование. Вот мы и пришли к множественному наследованию: сочетанию открытого наследования интерфейса с закрытым наследованием реализации:


class IPerson { // класс описывает интерфейс,

public: // который должен быть реализован

virtual ~IPerson;

virtual std::string name const = 0;

virtual std::string birthDate const = 0;

};

class DatabaseID {...}; // используется далее;

// детали не существенны

class PersonInfo { // в этом классе имеет функции,

public: // помогающие при реализации

explicit PersonInfo(DatabaseID pid) // интерфейса IPerson

virtual ~PersonInfo;

virtual const char *theName const;

virtual const char *theBirthDate const;

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже