|
Программирование >> Инициализация объектов класса, структура
class BinaryQuery : public Query { public: BinaryQuery( Query *lop, Query *rop, string oper ) : lop(lop), rop(rop), oper(oper) {} ~BinaryQuery() { delete lop; delete rop; } ostream &print( ostream&=cout, ) const = 0; protected: Query * lop; Query * rop; string oper; Вот как реализована в BinaryQuery функция print() , которая будет вызываться из inline ostreams BinaryQuery:: print( ostream sos ) const { if ( lparen ) print lparen( lparen, os ); lop->print( os ); os << << oper << ; rop->print( os ); if ( rparen ) print rparen( rparen, os ); return os; производных классов AndQuery и OrQuery: Похоже, мы попали в парадоксальную ситуацию. С одной стороны, необходимо объявить этот экземпляр print() как чисто виртуальную функцию, чтобы компилятор воспринимал BinaryQuery как абстрактный базовый класс. Тогда в приложении определить независимые объекты BinaryQuery будет невозможно. С другой стороны, нужно определить в классе BinaryQuery виртуальную функцию print() и уметь вызывать ее через объекты AndQuery и OrQuery. Но как часто бывает с кажущимися парадоксами, мы не учли одного обстоятельства: чисто виртуальную функцию нельзя вызывать с помощью механизма виртуализации, но inline ostreams AndQuery:: print( ostream sos ) const { правильно: подавить механизм виртуазации вызвать BinaryQuery::print статически BinaryQuery::print( os ); можно вызывать статически: #include <iostream> class base { public: virtual int foo( int ival = 1024 ) { cout << base::foo() -- ival: << ival << endl; return ival; class derived : public base { public: virtual int foo( int ival = 2048 ) { cout << derived::foo() -- ival: << ival << endl; return ival; Рассмотрим следующую простую иерархию классов: Проектировщик класса хотел, чтобы при вызове без параметров реализации foo() из base b; base *pb = &b; вызывается base::foo( int ) предполагалось, что будет возвращено 1024 базового класса но умолчанию передавался аргумент 1024: pb->foo(); Кроме того, разработчик хотел, чтобы при вызове его реализации foo() без параметров derived d; base *pb = &d; вызывается derived::foo( int ) предполагалось, что будет возвращено 2048 использовался аргумент по умолчанию 2048: pb->foo(); Однако в C++ принята другая семантика механизма виртуализации. Вот небольшая программа для тестирования нашей иерархии классов: 17.5.4. Виртуальные функции и аргументы по умолчанию int main() { derived *pd = new derived; base *pb = pd; int val = pb->foo() ; cout << main() : val через base: << val << endl; val = pd->foo() ; cout << main() : val через derived: << val << endl; После компиляции и запуска программа выводит следующую информацию: main() :foo() -- ival: 1024 val через base: 1024 :foo() -- ival: 2048 derived::foo() -- ival: 2048 main() : val через derived: 2048 При обоих обращениях реализация foo() из производного класса вызывается корректно, поскольку фактически вызываемый экземпляр определяется во время выполнения на основе типа класса, адресуемого pd и pb. Но передаваем1й foo() аргумент по умолчанию определяется не во время выполнения, а во время компиляции на основе типа объекта, через который вызывается функция. При вызове foo() через pb аргумент по умолчанию извлекается из объявления base::foo() и равен 1024. Если же foo() вызывается через pd, то аргумент по умолчанию извлекается из объявления derived::foo() и равен 2048. Если реализации из производного класса при вызове через указатель или ссылку на базовый класс по умолчанию передается аргумент, указанный в базовом классе, то зачем задавать аргумент по умолчанию для реализации из производного класса? Нам могут понадобиться различные аргументы по умолчанию в зависимости не от реализации foo() в конкретном производном классе, а от типа указателя или ссылки, через которые функция вызвана. Например, значения 1024 и 2048 - это размеры изображений. Когда нужно получить менее детальное изображение, вызываем foo() через класс base, а когда более детальное - через derived. Но если мы все-таки хотим, чтобы аргумент по умолчанию, передаваемый foo() , зависел от фактически вызванного экземпляра? К сожалению, механизм виртуализации такую возможность не поддерживает. Однако разрешается задать такой аргумент по умолчанию, который для вызванной функции означает, что пользователь не передал никакого значения. Тогда реальное значение, которое функция хотела бы видеть в качестве аргумента по умолчанию, объявляется локальной переменной и используется, если ничего другого не передано:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |