Здравствуйте DemAS, Вы писали:
DAS> Механизм понятен, не понятно назначение. А зачем может понадобится вызывать функцию класса через указатель на предка класса ? То есть зачем делать так:
DAS>
DAS> что не пойдет ?
DAS> Поясните, ГДЕ ВЫГОДНО использовать виртуальные функции ?
DAS>
Выгодно использовать в параметрах функций, например. Функции можно передавать параметр типа указатель на предка, а в рантайме туда будет посылаться реальный указатель, на какого-нить потомка. Код будет один, а поведение определяться конкретной реализацией виртуальной функции в классе-потомке. Удобно для похожих объектов, но по-разному например отображающих свое состояние и т.п.
DAS> что не пойдет ?
без преведения типа будет использоваться виртуальная таблица CheckingAccount класса, поэтому вызовется не Account::PrintBalance, а CheckingAccount::PrintBalance.
DAS> Поясните, ГДЕ ВЫГОДНО использовать виртуальные функции ?
Выгодно использовать в ООП
избитый пример.
у тебя есть классы собака, кошка, курица, лось и слон.
их можно разбить на три класса Домашние животные, животные, дикие животные.
у каждого из класса есть виртальный метод, среднее время жизни.
для каждого животного ты переопределяешь эту функцию.
далее у тебя есть
Животные rgЖивотные[5] = {слон, кошка и т.д};
и ты можешь вызывая виртуальный метод о среденем времени жизни не приводя тип узнать для каждого его.
DAS>
// deriv_VirtualFunctions.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
class __declspec(dllexport) Account
{
public:
Account( double d ); // Constructor.
virtual double GetBalance(); // Obtain balance.
virtual void PrintBalance(); // Default implementation.
private:
double _balance;
};
// Implementation of constructor for Account.
Account::Account( double d )
{
_balance = d;
}
// Implementation of GetBalance for Account.
double Account::GetBalance()
{
return _balance;
}
// Default implementation of PrintBalance.
void Account::PrintBalance()
{
cerr << "Error. Balance not available for base type."
<< endl;
}
class __declspec(dllexport) CheckingAccount : public Account
{
public:
void PrintBalance();
};
// Implementation of PrintBalance for CheckingAccount.
void CheckingAccount::PrintBalance()
{
cout << "Checking account balance: " << GetBalance();
}
class __declspec(dllexport) SavingsAccount : public Account
{
public:
void PrintBalance();
};
// Implementation of PrintBalance for SavingsAccount.
void SavingsAccount::PrintBalance()
{
cout << "Savings account balance: " << GetBalance();
}
void main()
{
}
The PrintBalance function in the derived classes is virtual because it is declared as virtual in the base class, Account. To call virtual functions such as PrintBalance, code such as the following can be used:
// Create objects of type CheckingAccount and SavingsAccount.
CheckingAccount *pChecking = new CheckingAccount( 100.00 );
SavingsAccount *pSavings = new SavingsAccount( 1000.00 );
// Call PrintBalance using a pointer to Account.
Account *pAccount = pChecking;
pAccount->PrintBalance();
// Call PrintBalance using a pointer to Account.
pAccount = pSavings;
pAccount->PrintBalance();
In the preceding code, the calls to PrintBalance are identical, except for the object pAccount points to.
Механизм понятен, не понятно назначение. А зачем может понадобится вызывать функцию класса через указатель на предка класса ? То есть зачем делать так:
Здравствуйте DemAS, Вы писали:
DAS> Поясните, ГДЕ ВЫГОДНО использовать виртуальные функции ?
В этом примере показано: несмотря на то, что указатель — на базовый класс, вызовется виртуальный метод именно того класса-потомка, на который этот указатель ссылается.
Более распространен другой случай: передача объекта с помощью указателя на базовый тип.
Иллюстрация разницы между обычными и виртуальными методами
class Base
{
public:
virtual void method1() { cout << "Base::method1"; }
void method2() { cout << "Base::method2; } // не виртуальный
...
};
class Ancestor1 : public Base
{
public:
virtual void method1() { cout << "Ancestor1::method1"; } // метод перекрыт
void method2() { cout << "Ancestor1::method2; } // метод переопределен
...
};
class Ancestor2 : public Base
{
public:
// method1 унаследованvoid method2() { cout << "Ancestor2::method2; } // метод переопределен
...
};
// функция с параметром базового типаvoid take_object(Base* object, ......)
{
...
object->method1(); // вызовется метод соответствующего класса
object->method2(); // а вот здесь - всегда вызовется Base::method2 - потому что не виртуальный
}
// функция возвращающая объект базового типа
Base* get_object(.....) { ...... }
// и ее реципиентvoid use_object(.....)
{
Base* object = get_object(.....);
object->method1(); // та же история, что и с take_object
object->method2();
}
void use_all_classes(.....)
{
Base object0;
object0.method1(); // Base::method1
object0.method2(); // Base::method2
take_object(&object0, ....);
// выполнятся Base::method1, Base::method2
Ancestor1 object1;
object1.method1(); // Ancestor1::method1
object1.method2(); // Ancestor1::method2
take_object(&object1, ....);
// выполнятся Ancestor::method1 (перекрыт), Base::method2
Ancestor2 object2;
object2.method1(); // Base::method1
object2.method2(); // Ancestor2::method2
take_object(&object2, ....);
// выполнятся Base::method1 (унаследован), Base::method2
}
Пардон, малмала очепятку сделал — закрывающие кавычки.
class Base
{
public:
virtual void method1() { cout << "Base::method1"; }
void method2() { cout << "Base::method2"; } // не виртуальный
...
};
class Ancestor1 : public Base
{
public:
virtual void method1() { cout << "Ancestor1::method1"; } // метод перекрытvoid method2() { cout << "Ancestor1::method2"; } // метод переопределен
...
};
class Ancestor2 : public Base
{
public:
// method1 унаследованvoid method2() { cout << "Ancestor2::method2"; } // метод переопределен
...
};
// функция с параметром базового типаvoid take_object(Base* object, ......)
{
...
object->method1(); // вызовется метод соответствующего класса
object->method2(); // а вот здесь - всегда вызовется Base::method2 - потому что не виртуальный
...
}
// функция возвращающая объект базового типа
Base* get_object(.....) { ...... }
// и ее реципиентvoid use_object(.....)
{
...
Base* object = get_object(.....);
...
object->method1(); // та же история, что и с take_object
object->method2();
...
}
void use_all_classes(.....)
{
Base object0;
object0.method1(); // Base::method1
object0.method2(); // Base::method2
take_object(&object0, ....);
// выполнятся Base::method1, Base::method2
Ancestor1 object1;
object1.method1(); // Ancestor1::method1
object1.method2(); // Ancestor1::method2
take_object(&object1, ....);
// выполнятся Ancestor::method1 (перекрыт), Base::method2
Ancestor2 object2;
object2.method1(); // Base::method1
object2.method2(); // Ancestor2::method2
take_object(&object2, ....);
// выполнятся Base::method1 (унаследован), Base::method2
}
Перекуём баги на фичи!
Re[2]: Virtual function
От:
Аноним
Дата:
25.10.02 18:52
Оценка:
Здравствуйте whiteForest, Вы писали:
F>без преведения типа будет использоваться виртуальная таблица CheckingAccount класса, поэтому вызовется не Account::PrintBalance, а CheckingAccount::PrintBalance.
А причем тут приведение? Как ни крути, все равно CheckingAccount::PrintBalance вызовется, указатель-то на pChecking.
Здравствуйте Аноним, Вы писали:
А>Здравствуйте whiteForest, Вы писали:
F>>без преведения типа будет использоваться виртуальная таблица CheckingAccount класса, поэтому вызовется не Account::PrintBalance, а CheckingAccount::PrintBalance.
А>А причем тут приведение? Как ни крути, все равно CheckingAccount::PrintBalance вызовется, указатель-то на pChecking.