运行时多态指针更改
我对多态指针真的很困惑。我有两个从接口派生的类,如下代码所示。
#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;
}
我想根据运行时的条件动态更改指针,以便在不同类型的场景中使用相同的指针。
派生类彼此不同,那么当多态指针对象发生变化时,内存中会发生什么情况呢?
如果这种用法不是好的做法,那么如何在运行时动态更改多态指针对象?
I am really confused about polymorphic pointers. I have 2 classes derived from an interface as shown below code.
#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;
}
I want to change the pointer dynamically according to a condition at run time to use the same pointer with different kinds of scenarios.
Derived classes are different from each other, so what happens in memory when the polymorphic pointer object changes?
If that usage is not good practice, how can I change the polymorphic pointer object dynamically at the run time?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
更改指针指向的内容是完全可以的。
Base*
不是Base
的实例,它是一个指向
Base
实例的指针(或从它派生的东西 - 在本例中为B
或C
)。因此,在您的代码中,
base = new B()
将其设置为指向B
的新实例,然后base = new C()
code> 将其设置为指向C
的新实例。因为
Base*
指向Base
的一个实例,所以所做的只是更改哪个实例(或派生实例)Base*
指向。实际上,它只是改变了指针的内存地址。从该
Base*
指针,您仍然可以访问该Base
类中定义的任何内容——如果该函数定义为,则该类仍然允许对派生类型满足的函数进行多态调用虚拟
。从技术上讲,如何将其分派到派生类型的确切机制是语言的实现细节,但通常这是通过称为双分派的过程来完成的,该过程使用“V-Table”。这是与包含虚拟函数的任何类一起存储的附加类型信息(它在概念上只是函数指针的结构,其中函数指针由具体类型满足)。
有关 vtable 的更多信息,请参阅:为什么我们需要虚拟表?。
然而,有问题的是这里使用了
new
。new
分配必须使用delete
清理的内存,以避免内存泄漏。通过执行以下操作:B
对象的析构函数永远不会运行,不会清理任何资源,并且永远不会回收B
本身的内存。这应该是:或者,更好的是,应该使用像 std::unique_ptr 这样的智能指针来为您执行此操作。在这种情况下,您不显式使用
new
和delete
,而是使用std::make_unique
进行分配,析构函数会自动执行此操作你:这是编写动态分配的推荐/现代方法
It's perfectly fine to change what a pointer points to. A
Base*
is not an instance ofBase
, it is a pointer that points to an instance of aBase
(or something derived from it -- in this caseB
orC
).Thus in your code,
base = new B()
sets it to point to a new instance of aB
, and thenbase = new C()
sets it to point to a new instance of aC
.Because
Base*
points to an instance of aBase
, all this is doing is changing which instance (or derived instance)Base*
points to. In effect, it just changes the memory address of the pointer.From that
Base*
pointer, you still have access to anything defined in thatBase
class -- which still allows polymorphic calls to functions satisfied by derived types if the function is defined asvirtual
.The exact mechanism for how this is dispatched to derived types is technically an implementation-detail of the language, but generally this is done through a process called double-dispatch, which uses a "V-Table". This is additional type-information stored alongside any classes that contain
virtual
functions (it's conceptually just astruct
of function pointers, where the function pointers are satisfied by the concrete types).See: Why do we need a virtual table? for more information on vtables.
What is problematic, however, is the use of
new
here.new
allocates memory that must be cleaned up withdelete
to avoid a memory leak. By doing the following:The
B
object's destructor is never run, no resources are cleaned up, and the memory forB
itself is never reclaimed. This should be:Or, better yet, this should be using smart-pointers like
std::unique_ptr
to do this for you. In which case you don't usenew
anddelete
explicitly, you usestd::make_unique
for allocation, and the destructor automagically does this for you:This is the recommended/modern way to write dynamic allocations