Эффективное использование 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 );

>}


Другими словами, если вы не можете вызывать виртуальные функции из конструктора базового класса, то можете компенсировать это передачей необходимой информации конструктору базового класса из конструктора производного.