![]() |
1
82
客体
要调用虚函数,调用方需要知道调用函数的对象。这是因为对象本身决定了应该调用哪个函数。(这通常是通过给对象一个指向vtable的指针、一个函数指针列表来实现的,调用方只知道它应该调用列表中的第一个函数,而不预先知道该指针指向哪里。)
但要调用非虚拟函数,调用方不需要知道所有这些。编译器确切知道将调用哪个函数,因此它可以生成
现在,由于该函数的实现从未引用它所指向的对象的任何成员
正式的,打电话来 任何 函数“即使是空指针上的非虚拟函数”也是未定义的行为。未定义行为的一个允许结果是您的代码看起来完全按照您的预期运行。 你 不应该依赖它,尽管有时您会从编译器供应商那里找到库 做 依靠它。但是编译器供应商的优势是能够为不定义的行为添加进一步的定义。不要自己动手。 |
![]() |
2
16
这个
因为您不访问任何成员,所以调用成功(即使您按照标准输入了未定义的行为)。
|
![]() |
3
7
取消对空指针的引用会导致“未定义的行为”,这意味着任何事情都可能发生——您的代码甚至可能看起来工作正常。但是,您不能依赖于这一点——如果您在不同的平台上运行相同的代码(甚至可能在同一个平台上运行相同的代码),它可能会崩溃。 在您的代码中没有foo对象,只有一个用值null初始化的指针。 |
![]() |
4
5
这是未定义的行为。但大多数编译器都会发出指令,如果您不访问成员变量和虚拟表,这些指令将正确处理这种情况。 请参阅Visual Studio中的反汇编,了解发生的情况
如你所见,foo:说“hi called as normal function but with”
这
在ECX寄存器中。为了简化,你可以假设
这
作为隐式参数传递,我们在示例中从未使用过。
|
![]() |
5
2
a)它工作是因为它不会通过隐式“this”指针取消引用任何内容。一旦你这样做,砰。我不是100%确定,但我认为空指针取消引用是由rw完成的,它保护了内存空间的前1K,因此,如果只在1K行后取消对空指针的引用,则很可能不会捕获空指针取消引用(例如,一些实例变量将被分配得非常远,例如:
那么当a为空时,a->i可能未被捕获。 b)在任何地方,您只声明了一个指针,它在main():s堆栈上分配。 |
![]() |
6
2
打电话说你好是静态绑定的。所以计算机实际上只是对一个函数进行一个标准调用。函数不使用任何字段,因此没有问题。 对virtual-say-hi的调用是动态绑定的,因此处理器将转到虚拟表,由于那里没有虚拟表,因此它会随机跳到某个地方并使程序崩溃。 |
![]() |
7
1
在C++最初的日子里,C++代码被转换成C对象方法被转换成非对象方法(如你的例子):
当然,foo-say-hi这个名字被简化了。有关详细信息,请查阅C++名称。 如您所见,如果从未取消对thisptr的引用,那么代码是好的,并且是成功的。在您的例子中,没有使用实例变量或依赖于thisptr的任何东西。 但是,虚拟功能是不同的。有很多对象查找来确保将正确的对象指针作为参数传递给函数。这将取消引用thisptr并导致异常。 |
![]() |
8
1
重要的是要认识到 二者都 调用产生未定义的行为,并且该行为可能以意外的方式出现。即使电话 出现 为了工作,它可能正在铺设一个雷区。 考虑一下这个小小的改变:
从第一次打电话到
|
![]() |
Eris · 纯虚拟成员有什么优势吗(除了他们可能防止的人为错误)? 2 年前 |
![]() |
logonmanish · 虚拟com端口在Android上不工作 7 年前 |
|
AliS · 使用具有抽象基类指针的映射并调用派生类函数 9 年前 |
![]() |
Philip Borgström · Java虚拟游戏板 9 年前 |
![]() |
prestokeys · 具有完全可维护性的多重调度解决方案 10 年前 |
![]() |
Nick_K · RTSP流到Windows 8上的虚拟视频设备 10 年前 |
![]() |
Jay · 基于子类的属性对linq列表排序 11 年前 |
|
JLuc5 · C++父类,在两个不同的子类中实现了虚拟方法 11 年前 |