Я действительно запутался в полиморфных указателях. У меня есть 2 класса, полученных из интерфейса, как показано ниже.
#include <iostream>
using namespace std;
class Base {
public:
virtual ~Base() { }
virtual void addTest() = 0;
};
class B: public Base {
public:
B(){}
~B(){}
void addTest(){
cout << "Add test B\n";
}
};
class C: public Base {
public:
C(){}
~C(){}
void addTest(){
cout << "Add test C\n";
}
private:
void deleteTest(){
}
};
int main()
{
Base *base = new B();
base->addTest();
base = new C();
base->addTest();
return 0;
}
Я хочу динамически изменять указатель в соответствии с условием во время выполнения, чтобы использовать один и тот же указатель в различных сценариях.
Производные классы отличаются друг от друга, так что же происходит в памяти при изменении объекта полиморфного указателя?
Если такое использование не является хорошей практикой, как я могу динамически изменять объект полиморфного указателя во время выполнения?
Я подозреваю, что вы предполагаете, что это намного сложнее, чем есть на самом деле. Все, что происходит, это то, что base
присваивается другое значение — местоположение вновь созданного C
— вместо исходного значения. То есть не сложнее, чем int x = 1; x = 2;
.
@user253751 user253751 да, один из вопросов таков.
Ну это вопрос а не более одного вопроса
Это совершенно нормально для изменить то, на что указывает указатель. Base*
не является экземпляром Base
, это указатель на то, что указывает на является экземпляром Base
(или чем-то производным от него — в данном случае B
или C
).
Таким образом, в вашем коде base = new B()
устанавливает его так, чтобы он указывал на новый экземпляр B
, а затем base = new C()
устанавливает его так, чтобы он указывал на новый экземпляр C
.
Derived classes are different from each other, so what happens in memory when the polymorphic pointer object changes?
Поскольку Base*
указывает на является экземпляром Base
, все, что он делает, — это меняет экземпляр (или производный экземпляр), на который указывает Base*
. По сути, он просто меняет адрес памяти указателя.
Из этого указателя Base*
у вас по-прежнему есть доступ ко всему, что определено в этом классе Base
, который по-прежнему разрешает полиморфные вызовы функций, удовлетворяющих производным типам, если функция определена как virtual
.
Точный механизм того, как это отправляется производным типам, технически является деталью реализации языка, но обычно это делается с помощью процесса, называемого двойной отправкой, который использует «V-таблицу». Это дополнительная информация о типах, хранящаяся вместе с любыми классами, которые содержат virtual
функции (концептуально это просто struct
указателей на функции, где указатели на функции удовлетворяются конкретными типами).
См. Зачем нам виртуальный стол? для получения дополнительной информации о виртуальных таблицах.
Однако проблематичным является использование здесь new
. new
выделяет память, которую необходимо очистить с помощью delete
, чтобы избежать утечки памяти. Выполнив следующие действия:
Base *base = new B();
base->addTest();
base = new C(); // overwriting base without deleting the old instance
base->addTest();
Деструктор объекта B
никогда не запускается, никакие ресурсы не очищаются, а память для самого B
никогда не освобождается. Это должно быть:
Base *base = new B();
base->addTest();
delete base;
base = new C(); // overwriting base without deleting the old instance
base->addTest();
delete base;
Или, что еще лучше, это должны быть умные указатели, такие как std::unique_ptr
, чтобы сделать это за вас. В этом случае вы не используете new
и delete
явно, вы используете std::make_unique
для выделения памяти, а деструктор автоматически делает это за вас:
auto base = std::make_unique<B>();
base->addTest();
base = std::make_unique<C>(); // destroy's the old instance before reassigning
base->addTest();
Это рекомендуемый/современный способ написания динамических распределений.
Я чувствую, что этот ответ слишком много внимания уделяет утечкам памяти и недостаточно полиморфизму. Хотя утечка памяти является проблемой, я хочу прояснить, что delete
и make_unique
не являются частью полиморфизма.
@ user253751 Это справедливо; Я расширил полиморфный аспект, хотя IMO действительно важная часть ответа заключается в том, что Base*
является указателем, а не конкретным экземпляром Base
.
В чем вопрос? Это вопрос? «Что происходит в памяти при изменении объекта полиморфного указателя?»