Не определять оператор преобразования, а определять вместо них функции, типа operator int() ‹-› asInt()
В определении конструктора использовать модификатор explicit для подавления неявных вызовов.
Использовать proxy-object - промежуточный объект наподобие курсора из Шага 16, все назначение которого - быть другим объектом когда нужно, и не быть им, когда не нужно. Словами больно заумно, проще нарисовать код.
// Класс прокси-объекта
class CProxyInt {
friend class CInt;
private:
int m_i;
public:
CProxyInt (int _i): m_i(_i) {}
int getInt () const { return m_i; }
};
// Предыдущий класс инт.
class CInt {
friend class CProxyInt;
private:
int m_i;
int m_instance;
static int iCounter;
public:
// Конструктор по умолчанию изменен
CInt (CProxyInt);
CInt (const CInt&);
~CInt();
CInt operator+(const CInt&);
CInt& operator+=(const CInt&);
CInt& operator= (const CInt&);
// operator int ();
};
int CInt::iCounter = 0;
// Реализация конструктора, вместо инта стоит прокси
CInt::CInt (CProxyInt _i=0): m_i(_i.m_i) {
m_instance = ++iCounter;
cout‹‹"defa constr " ‹‹ m_instance ‹‹ " "‹‹ m_i ‹‹ endl;
}
CInt a(5); // Это компилируется нормально
CInt a = 5; // А это нет. И все неявные вызовы тоже.
Видите, мы используем технику proxy уже второй раз, но совершенно в другом контексте. Общее то, что proxy применяется в том случае, если мы хотим определить свои законы преобразования типов и классов.
В этом смысле smart-указатель несомненно тоже рroxy, (уменьш. ласк. проксятник, проксятничек).
Шаг 21 - О тщете сущего.
Прежде чем использовать приемы, описанные в предыдущих Шагах, тщательно подумайте - надо ли Вам это? (Примеры с памятью еще и упрощены до свинства, не вздумайте применять в таком виде).
Средний компилятор управляет памятью примерно так, как описано в Шаге 18-19, а именно запрашивает большие куски по необходимости у операционки через calloc(), потом раздает кусочки объектам. Если объект уничтожен, то (по возможности) использует свободное место повторно. Память вернется в операционку только после того, как все объекты в ней уничтожены. Если мы будем писать свой менеджер памяти не почитав теории для начала, то вернее всего ухудшим использование памяти.
Неявные преобразования через конструкторы и операторы преобразований хороши, конечно. Но почему-то пришлось вводить ограничения на них. Чтоб неявно не вызывались. Сколь мне известно, компания Borland/Inprise собирась вводить в Delphi 4/5 перегрузку операторов, но как-то передумала…
Неявные объекты в большинстве случаев не мешают нам жить. Более того, запись в предыдущем шаге, где вызывается 7 конструкторов вместо 4, более читабельна-сопровождабельна, красива, соответствует духу и букве C++ (семантике и синтаксису). Если функция исполняется в программе раз в час, неважно, сколько раз вызовется в ней конструктор - 4 или 44. Скринсавер вообще выполняет море абсолютно бесполезных и сверхсложных вычислений - Вам это мешает?
А что касается виртуальных функций, то и MFC, и OWL, и VCL - все используют их как можно реже - на то веские причины! Если бы все функции в них были виртуальными, то с полметра памяти уходило бы в каждой программе только на поддержание виртуальных таблиц, да по лишнему указателю в каждом объекте.
Есть такое правило "80-20": 20 процентов кода вызывает 80 процентов затруднений, 20 процентов кода занимает 80 процентов процессорного времени. Возможно, оно даже сильнее - "90-10". В данном Шаге это значит - не зашивайтесь в "дешевой" части кода.
В общем, я хочу сказать - перед тем, как применять какую-то технику, оцените - какие усилия Вы затратите на ее освоение, ее поддержание, и какой результат Вы получите (ожидаете получить), и пригодятся ли Вам эти знания в будущем, что тоже важно. Программирование - всегда поиск компромисса между затратами времени, пространства и (!)труда, не забывайте что Ваш день стоит минимум как небольшой DIMM. Надеюсь.
Шаг 22 - Классы объектов, поддерживающие транзакции.
Бывает особенно приятно, когда занимаешься теорией. Занимаешься, думаешь: "ну никакой связи с жизнью, хоть бы минимум пользы"… и вдруг раз! и польза является во всей своей красе, блистая в лучах солнца и хрустя пачками денег. Что чувствовал Менделеев, когда после долгих изысканий, жутких таблиц, являвшихся ему в ночных кошмарах, вдруг получил-таки нормальную, не паленую, 40-градусную водку? Или Эйлер, когда, после терзаний и депрессий, извлек таки сопротивляющийся, визжащий и цепляющийся щупальцами и жвалами квадратный корень из минус единицы? К чему это я? А вот к чему: концепция smart-указателей может предложить простые и прозрачные решения для некоторых сложных задач; и если Вы считаете, что поддержка транзакций (а так же многоуровневой отмены и повтора) есть сложная задача, то смарты помогут Вам с замечательной легкостью.