Недавний вопрос о SO заставил меня задуматься о следующем.
Рассмотрим следующий код:
class Base{
public:
void print() { cout<<"In Base"<<endl;}
};
class Derived: public Base{
public:
void print() { cout<<"In Derived"<<endl;}
};
int main(void)
{
Base *bp;
Derived ob;
bp = &ob;
(*bp).print(); // prints: In Base
ob.print(); // print: In Derived
return 0;
}
Почему (*bp),print()
не ведет себя аналогично ob.print()
.
Я думаю, что (* bp) должен вернуть объект ob
поскольку bp
ссылается на объект ob
и когда мы де-ссылку с помощью *
оператора, все, что мы получаем значение на этом адресе, а объект ob
хранится по адресу, указанному в bp
. Таким образом, первый вызов функции должен быть таким же, как и отправить.
Просьба уточнить концепцию.
То, что вы делаете, называется "скрытие метода", и это запах кода, плохая привычка, чего не следует делать.
Вы должны определить свой метод печати как virtual
в базовом классе. (А также не забывайте о virtual
деструкторе в Base
классе.)
class Base{
public:
virtual void print() { cout<<"In Base"<<endl;}
virtual ~Base(){}
};
class Derived: public Base{
public:
void print() { cout<<"In Derived"<<endl;}
};
Вам не удалось объявить функцию print()
virtual
? ;)
class Base{
public:
virtual void print() { cout<<"In Base"<<endl;}
};
Всегда решение основано на типе указателя, а не на содержании указателя. Первое связывание называется Early Binding
где последнее является Late Binding
.
Если мы переопределим метод print(), которому предшествует ключевое слово virtual
результат будет таким, как требуется.
class Base
{
public:
virtual void print()
{
cout<<"In Base"<<endl;
}
};
class Derived: public Base
{
public:
void print()
{
cout<<"In Derived"<<endl;
}
};
Когда вы ссылаетесь на объект производного класса, используя указатель или ссылку на базовый класс, вы можете вызвать виртуальную функцию для этого объекта и выполнить версию функции производного класса. Виртуальные функции гарантируют, что для объекта вызывается правильная функция, независимо от выражения, используемого для вызова функции.
При вызове функции с использованием указателей или ссылок применяются следующие правила:
Вы можете ознакомиться с концепциями виртуальных таблиц и виртуальных функций для получения более подробной информации.
Компилятор использует статические типы переменных. Переменная bp имеет статический тип Base *, поэтому компилятор проверяет, существует ли такая функция в определении класса Base и вызывает его. Эффект, который вы хотите достичь, может быть выполнен с использованием виртуальной функции. В этом случае компилятор вызывает функцию, используя таблицу указателей на виртуальные функции. В этом случае, когда bp имеет динамический тип Derived *, тогда таблица будет содержать указатель на виртуальную функцию, определенную в классе Derived. Все, что вам нужно сделать, это добавить функциональный спецификатор virtual в определение функции в классе Base
class Base{
public:
virtual void print() { cout<<"In Base"<<endl;}
virtual ~Base() = default};
Base
может иметь много производных классов. В момент (*bp).print()
вы не знаете, какой из них (если есть) является реальным классом базового объекта. Таким образом, все, что может сделать компилятор, это вызвать Base::print
.
Но если вы включите некоторую информацию о фактическом классе (vtable
), тогда вы сможете делать то, что хотите (используя virtual
функции, как это было предложено другими).