Разыменование через указатель базового класса

0

Недавний вопрос о 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. Таким образом, первый вызов функции должен быть таким же, как и отправить.

Просьба уточнить концепцию.

  • 0
    Нет четкого ответа в ссылке выше, я не думаю, что это должно быть закрыто на упомянутом дубликате.
  • 0
    @MarounMaroun, тогда четкий ответ должен быть предоставлен в двух экземплярах. Но я думаю, что там есть ответ.
Показать ещё 3 комментария
Теги:

5 ответов

4

То, что вы делаете, называется "скрытие метода", и это запах кода, плохая привычка, чего не следует делать.

Вы должны определить свой метод печати как 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;}
};
2

Вам не удалось объявить функцию print() virtual? ;)

class Base{
 public:
     virtual void print() { cout<<"In Base"<<endl;}
};
1

Всегда решение основано на типе указателя, а не на содержании указателя. Первое связывание называется 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;
      }
};

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

При вызове функции с использованием указателей или ссылок применяются следующие правила:

  • Вызов виртуальной функции разрешен в соответствии с базовым типом объекта, для которого он вызывается.
  • Вызов не виртуальной функции разрешен в соответствии с типом указателя или ссылки

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

0

Компилятор использует статические типы переменных. Переменная bp имеет статический тип Base *, поэтому компилятор проверяет, существует ли такая функция в определении класса Base и вызывает его. Эффект, который вы хотите достичь, может быть выполнен с использованием виртуальной функции. В этом случае компилятор вызывает функцию, используя таблицу указателей на виртуальные функции. В этом случае, когда bp имеет динамический тип Derived *, тогда таблица будет содержать указатель на виртуальную функцию, определенную в классе Derived. Все, что вам нужно сделать, это добавить функциональный спецификатор virtual в определение функции в классе Base

class Base{
 public:
     virtual void print() { cout<<"In Base"<<endl;}
     virtual ~Base() = default};
0

Base может иметь много производных классов. В момент (*bp).print() вы не знаете, какой из них (если есть) является реальным классом базового объекта. Таким образом, все, что может сделать компилятор, это вызвать Base::print.

Но если вы включите некоторую информацию о фактическом классе (vtable), тогда вы сможете делать то, что хотите (используя virtual функции, как это было предложено другими).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню