Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ | страница 47
>class Transaction {
>public:
>Transaction()
>{ init(); } // вызов невиртуальной функции
>Virtual void logTransaction() const = 0;
>...
>private:
>void init()
>{
>...
>logTransaction(); // а это вызов виртуальной
>// функции!
>}
>};
Концептуально этот код не отличается от приведенного выше, но он более коварный, потому что обычно будет скомпилирован и скомпонован без предупреждений. В этом случае, поскольку logTransaction – чисто виртуальная функция класса Transaction, в момент ее вызова большинство систем времени исполнения прервут программу (обычно выдав соответствующее сообщение). Однако если logTransaction будет «нормальной» виртуальной функцией, у которой в классе Transaction есть реализация, то эта функция и будет вызвана, и программа радостно продолжит работу, оставляя вас в недоумении, почему при создании объекта производного класса была вызвана неверная версия logTransaction. Единственный способ избежать этой проблемы – убедиться, что ни один из конструкторов и деструкторов не вызывает виртуальных функций при создании или уничтожении объекта, и что все функции, к которым они обращаются, следуют тому же правилу.
Но как вы можете убедиться в том, что вызывается правильная версия log-Transaction при создании любого объекта из иерархии Transaction? Понятно, что вызов виртуальной функции объекта из конструкторов не годится.
Есть разные варианты решения этой проблемы. Один из них – сделать функцию logTransaction невиртуальной в классе Transaction, затем потребовать, чтобы конструкторы производного класса передавали необходимую для записи в протокол информацию конструктору Transaction. Эта функция затем могла бы безопасно вызвать невиртуальную logTransaction. Примерно так:
>class Transaction {
>public:
>explicit Transaction(const std::string& loginfo);
>void logTransaction(const std::string& loginfo) const; // теперь –
>// невиртуальная
>// функция
>...
>};
>Transaction::Transaction(const std::string& loginfo)
>{
>...
>logTransaction(loginfo); // теперь –
>// невиртуальный
>// вызов
>}
>class BuyTransaction : public Transaction {
>public:
>BuyTransaction( parameters )
>: Transaction(createLogString( parameters )) // передать информацию
>{...} // для записи в протокол
>... // конструктору базового
>// класса
>private:
>static std::string createLogString( parameters );
>}
Другими словами, если вы не можете вызывать виртуальные функции из конструктора базового класса, то можете компенсировать это передачей необходимой информации конструктору базового класса из конструктора производного.