C++ 多态性。为啥这行得通?

Posted

技术标签:

【中文标题】C++ 多态性。为啥这行得通?【英文标题】:C++ Polymorphism. Why is this working?C++ 多态性。为什么这行得通? 【发布时间】:2014-11-21 05:19:50 【问题描述】:

这不应该是不正确的吗? :

A* apb = (A*)&b; //a pointer to b

我预计会出现错误,或者至少是警告。

为什么这在 Visual C++ 2013 (v120) 或 g++ (gcc 4.8.2) 中没有给我任何警告?

#ifndef A_H
#define A_H

#include <stdio.h>

class A

public:
   A()
   virtual ~A()
   void print() printf("printA\n"); 
   virtual void printVirtual() printf("printVirtualA\n"); 
;

#endif // A_H

#ifndef B_H
#define B_H

#include "A.hpp"
class B : A

public:
   B()
   void print() printf("printB\n"); 
   virtual void printVirtual() printf("printVirtualB\n"); 
;

#endif //B_H

int main()

   A a;
   B b;
   A* apb = (A*)&b; //a pointer to b
   B* bpa = (B*)&a; //b pointer to a

   apb->print();         // printA
   apb->printVirtual();  // printVirtualB
   bpa->print();         // printB
   bpa->printVirtual();  // printVirtualA
   return 0;

输出:

printA
printVirtualB
printB
printVirtualA

我怀疑这是因为我的类在内存中对齐,所以有一个有效的 Vtable。

这是一个“隐式”dynamic_cast 吗?

据我所知,这样做不应该是正确的。

【问题讨论】:

您正在强制编译器使用 C 风格的转换进行转换,所以它不会抱怨。哎呀,你可以做float* bad = (float*)&amp;b;,编译器仍然不会抱怨。但这并不能使它有效。我相信你的演员都不合法(B 私下继承自 A,而 A 不是从 B 派生的)。 向下转型呢? B* bpa = (B*)&amp;a; //b pointer to a 是不是因为类有相同的结构才能正确执行? 这实际上是未定义的行为并且是非法的。但是,它似乎可以工作,因为AB 是非常简单的类(它们没有成员,并且这些函数只是打印文本而不做任何复杂的事情)。由于AB 非常简单并且非常接近相同,因此它们可以充当彼此的替代品。这有点像用凳子的腿代替桌腿:它们不一样,但足够接近它可能会起作用。但是,尝试用凳子的腿代替大象腿,事情不会那么顺利...... @Cornstalks 谢谢,这正是我正在寻找的答案。我仍然想知道为什么根本没有警告。 【参考方案1】:

这正是 vtables 的用途。非虚函数被称为匹配容器的类型,虚函数被称为匹配被指向对象的类型。

A* apb = (A*) &b;

.... 是完美的代码,是虚拟多态的经典例子。

【讨论】:

然而,B* bpa = (B*)&amp;a; 不是完美的代码...(另外:这些函数都不是静态的)。另外,私有继承不会破坏这段代码的合法性吗? 确实如此。我将非虚拟函数称为静态,因为它以静态方式调用,而不是按照 c++ 关键字。但你是对的。 这有点像,但不完全如此。该函数使用this 指针调用(只是碰巧没有使用它),这意味着它的调用约定与static 函数不太一样。 静态我的意思是调用地址不是在运行时确定的,而是在编译时确定的,而不是在运行时使用 vtable 虚拟/动态地确定【参考方案2】:
A* apb = (A*)&b;

从派生类初始化基类指针是完全可以的(假设bB 类型,如您所示)。你甚至不需要强制转换操作

A* apb = &b;

void print() 成员函数没有标记为virtual,因此成员函数是在您手头的实际实例上调用的。

【讨论】:

我不认为这完全没问题。请注意,他是私人继承,而不是公开继承。 @Cornstalks 好地方。是的,强制转换可能会隐藏为A* apb = &amp;b; 给出的错误。虽然公开继承应该可以解决这个问题,但行为并没有真正的改变。关键是,print() 函数不是 virtual【参考方案3】:
A* apb = (A*) &b;

这完全没问题。指向派生对象的基指针。这种方案主要是通过抽象类(或接口)来实现多态性

【讨论】:

以上是关于C++ 多态性。为啥这行得通?的主要内容,如果未能解决你的问题,请参考以下文章

Rails:has_many 通过多态关联——这行得通吗?

为啥以及何时使用多态性将基类指向 C++ 中的派生类 [重复]

c++中只有使用指针才能多态吗?普通实例和引用为啥不能多态?

为啥 AbstractFactoryUnit 具有动态而不是静态多态性?

c++中为啥要用new 函数()实现多态?

C++基础语法多态