CInterface {
private:
CComplexObject* co; // Укаэатель на объект
public:
CInterface (CComplexObject _co){}; // Это конструктор
};
Немного подумав, решаем перенести обязанности по порождению интерфейсов на объект. Конструктор интерфейса перекладываем в private, объявляем класс объекта дружественным классу интерфейса, в классе объекта перегружаем операторы преобразования (или русским языком говоря - рисуем операторы преобразования объекта к интерфейсу).
// Это объект
class CComplexObject {
operator CInterface() { return new CInterface(this); } // оператор преобразования
};
// Это интерфейс
CInterface {
private:
CComplexObject* co; // Укаэатель на объект
CInterface (CComplexObject* _co) {} // Это частный конструктор
};
Думаем еще раз: перенести ответственность за преобразование интерфейсов на специально выделенный smart-указатель, и временно назовем его Super- указателем. Идея с супером просто счастливая - мало того, что не надо изменять объект (код класс объекта), так еще и преобразование упрощается: сначала получим супер по интерфейсу, а потом другой интерфейс по суперу. Да, конечно, два преобразования подряд, но это все же лучше чем в каждом интерфейсе определять преобразование ко всем остальным. Зато интерфейсы ничего не знают друг о друге, им нет нужды, если им известен супер. И потом, поскольку интерфейсы являются простыми smart-ами, надо пожалуй задать функциюшечку, которая бы проверяла - есть ли вообще в природе изрядно подзабытый нами объект. Это место небезуспешно может занять перегруженный оператор operator!().
// Предварительные объявления классов
class CComplexObject;
class CInterface;
class CSuperObject;
// Определение объекта пропускаю, это Ваше занятие.
// Определение супера
class CSuperObject {
private:
CComplexObject* co; // указатель на объект
public:
// конструктор супера,
CSuperObject(CComplexObject* _co): co(_co) {}
// Живой ли наш объект? Дима! Помаши рукой маме!
bool operator!(){ return co==NULL; }
// преобразование к интерфейсу
operator CInterface();
}
// Это интерфейс
CInterface {
private:
CComplexObject* co; // Укаэатель на объект
CSuperObject* cs; // указатель на супер
CInterface (CComplexObject* _co) {} // Это частный конструктор
public:
bool operator!(){ return co==NULL; } //проверка на существование объекта
operator CSuperObject (); //преобразование к суперу
};
Ну все, с этой темой я закругляюсь, но думаю, что идея понятна. Комбинации умных, ведущих, интерфейсных указателей, наследование смартов от абстрактных базовых классов, наследование смартов и указываемых объектов от одних и тех же базовых классов позволяют Вам достичь удивительной гибкости. Помните, как Шалтай-Болтай говорил Алисе 'с таким именем ты можешь оказаться кем угодно… просто КЕМ УГОДНО!'? Мы лучше. Мы оказываемся кем угодно, когда угодно, и по собственному желанию.
Напоследок прописная истина для тех, кто не знает: общие определения полиморфизма, наследования, инкапсуляции, понимание перегрузки операторов, перегрузки и переопределения функций, абстрактных классов, виртуальных и чистых виртуальных функций, конструкторов и деструкторов КРИТИЧЕСКИ ВАЖНЫ ПРИ ПОИСКЕ РАБОТЫ ЗА БУГРОМ!!! Если Вы запнетесь хоть на одном из этих терминов, то Ваш интервьюер никогда Вам больше не позвонит, а если он еще и работает на крупную фирму, то Вас занесут в базу данных как никчемного ламера, и с ней будут сверяться десятки рекрутеров по всем Юнидос Эстадос. Вот так.
Шаг 11 - Нетривиальное конструирование объектов.
В прошлом шаге мы уже столкнулись с ситуацией, когда явное конструирование объектов нежелательно. Выход в таком случае - убрать конструкторы из открытой части объявления. Возможны два варианта - если Вы планируете наследование от этого класса, то конструкторы перемещается в защищенную часть:
CClass {
protected:
CClass () {}
};
А если Вы еще хотите так же и запретить наследование, то конструкторы перемещаются в закрытую часть объявления:
CClass {
private:
CClass () {}
};
Но как же тогда их вообще создавать, если их конструкторы недоступны? Да ясно как, ведь сам вопрос неверен: конструкторы не недоступны, они доступны, да только не для всех. Мы же как-то уже замечали, что класс по определению имеет несколько интерфейсов для разных клиентов, и помним, что самый полный, самый неограниченный интерфейс класс имеет для себя и для своих друзей. Следовательно, производящая функция-член класса или дружественная функция может свободно штамповать экземпляры класса и размещать где угодно, кроме стека; функция-член класса должна быть кроме того статической (то есть независимой от экземпляров), иначе в ней нет смысла.
// Вариант 1: производящая функция-член.