Virtual function
От: DemAS http://demas.me
Дата: 25.10.02 10:30
Оценка:
Еще один вопрос:

Virtual function — в MSDN есть такой пример:

// 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.

Механизм понятен, не понятно назначение. А зачем может понадобится вызывать функцию класса через указатель на предка класса ? То есть зачем делать так:

Account *pAccount = pChecking;
pAccount->PrintBalance();


А вот так:

pChecking->PrintBalance();


что не пойдет ?

Поясните, ГДЕ ВЫГОДНО использовать виртуальные функции ?
Re: Virtual function
От: Syd Россия  
Дата: 25.10.02 10:51
Оценка: 1 (1)
Здравствуйте DemAS, Вы писали:

DAS> Механизм понятен, не понятно назначение. А зачем может понадобится вызывать функцию класса через указатель на предка класса ? То есть зачем делать так:


DAS>
DAS>Account *pAccount = pChecking;
pAccount->>PrintBalance();
DAS>


DAS> А вот так:


DAS>
pChecking->>PrintBalance();
DAS>


DAS> что не пойдет ?


DAS> Поясните, ГДЕ ВЫГОДНО использовать виртуальные функции ?


DAS>


Выгодно использовать в параметрах функций, например. Функции можно передавать параметр типа указатель на предка, а в рантайме туда будет посылаться реальный указатель, на какого-нить потомка. Код будет один, а поведение определяться конкретной реализацией виртуальной функции в классе-потомке. Удобно для похожих объектов, но по-разному например отображающих свое состояние и т.п.
I'm just talking about having fun
Re: Virtual function
От: whiteForest  
Дата: 25.10.02 11:02
Оценка: 1 (1)
Здравствуйте DemAS, Вы писали:

<....>

DAS>
DAS>Account *pAccount = pChecking;
pAccount->>PrintBalance();
DAS>


DAS> А вот так:


DAS>
pChecking->>PrintBalance();
DAS>


DAS> что не пойдет ?

без преведения типа будет использоваться виртуальная таблица CheckingAccount класса, поэтому вызовется не Account::PrintBalance, а CheckingAccount::PrintBalance.

DAS> Поясните, ГДЕ ВЫГОДНО использовать виртуальные функции ?


Выгодно использовать в ООП

избитый пример.
у тебя есть классы собака, кошка, курица, лось и слон.

их можно разбить на три класса Домашние животные, животные, дикие животные.

схема наследования
Животные
Домашние животные Дикие животные
собака, кошка, курица лось, слон

у каждого из класса есть виртальный метод, среднее время жизни.
для каждого животного ты переопределяешь эту функцию.

далее у тебя есть
Животные rgЖивотные[5] = {слон, кошка и т.д};
и ты можешь вызывая виртуальный метод о среденем времени жизни не приводя тип узнать для каждого его.

DAS>
Re: Virtual function
От: Кодт Россия  
Дата: 25.10.02 12:38
Оценка:
Здравствуйте 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

}
Перекуём баги на фичи!
Re[2]: Virtual function
От: Кодт Россия  
Дата: 25.10.02 12:44
Оценка:
Пардон, малмала очепятку сделал — закрывающие кавычки.

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.
Re[3]: Virtual function
От: whiteForest  
Дата: 25.10.02 19:01
Оценка:
Здравствуйте Аноним, Вы писали:

А>Здравствуйте whiteForest, Вы писали:


F>>без преведения типа будет использоваться виртуальная таблица CheckingAccount класса, поэтому вызовется не Account::PrintBalance, а CheckingAccount::PrintBalance.


А>А причем тут приведение? Как ни крути, все равно CheckingAccount::PrintBalance вызовется, указатель-то на pChecking.


Сорри, по запарке. иммелось ввиду

CheckingAccount *pChecking;
((Account &) *pChecking).PrintBalance(...);

а так вы абсолютно правы.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.