【问题标题】: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 谢谢,这正是我正在寻找的答案。我仍然想知道为什么根本没有警告。

标签: c++ visual-c++ gcc polymorphism vtable


【解决方案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;

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-22
    • 2011-11-20
    • 2016-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-02
    相关资源
    最近更新 更多