C++;:编译对无关派生类、bug或功能的函数调用? 在C++中,我偶然发现了一些东西,我认为这是CLAN+++(6)和G++(5.3)的错误:我可以欺骗编译器调用它不能执行的函数。

C++;:编译对无关派生类、bug或功能的函数调用? 在C++中,我偶然发现了一些东西,我认为这是CLAN+++(6)和G++(5.3)的错误:我可以欺骗编译器调用它不能执行的函数。,c++,abstract-class,void-pointers,C++,Abstract Class,Void Pointers,简而言之,我声明了一个抽象基类,从中派生出两个类:非抽象类和抽象类,它们仅通过它们的公共基类相关,但具有不同的函数。实例化非抽象基类并欺骗编译器创建指向非派生类的抽象派生类指针,我能够通过该指针调用原始非抽象类和抽象类的函数 最简单的例子是,让我头疼的两行在底部,用一个带星星的评论行隔开: #include <cassert> #include <iostream> #include <typeinfo> class MYBase { public: v

简而言之,我声明了一个抽象基类,从中派生出两个类:非抽象类和抽象类,它们仅通过它们的公共基类相关,但具有不同的函数。实例化非抽象基类并欺骗编译器创建指向非派生类的抽象派生类指针,我能够通过该指针调用原始非抽象类和抽象类的函数

最简单的例子是,让我头疼的两行在底部,用一个带星星的评论行隔开:

#include <cassert>
#include <iostream>
#include <typeinfo>

class MYBase {
public:
  virtual void fun1() = 0;
};

class MYDer1 : public MYBase {
public:
  ~MYDer1() { std::cout << "~MYDer1()\n"; }
  void fun1() { std::cout << "MYDer1 fun1\n"; }
};

class MYDer2 : public MYBase {
public:
  ~MYDer2() { std::cout << "~MYDer2()\n"; }
  void something2() { std::cout << "MYDer2 something2\n"; }
};


int main () {
  MYDer1 x1;

  // Good: cannot instantiate a 'MYDer2' as it is an abstract class
  //  due to unimplemented pure virtual method 'fun1' in 'MYDer2'
  //
  // MYDer2 x2;

  // Good, clang++ and g++ rightfully refuse to compile this:
  //
  //   auto * pd2 = static_cast<MYDer2 *>(&x1);

  // So let's be 'clever' and take a detour via 'void':
  auto * pd2 = static_cast<MYDer2 *>(static_cast<void *>(&x1));

  // Just to be sure the compiler got it right
  assert(typeid(pd2) == typeid(MYDer2 *));

  /***********************************************************/
  // There is no "fun1" declared in MYDer2
  // So why does this even compile?
  pd2->fun1();

  // And why does this execute correctly?
  pd2->something2();

  return 0;
}
#包括
#包括
#包括
类MYBase{
公众:
虚空fun1()=0;
};
类MYDer1:公共MYBase{
公众:


~MYDer1(){std::cout这是未定义的行为,所以这次你击中了自己的腿,但没有击中。程序员有责任确保被取消引用的指针确实指向指定类型的对象。

这是未定义的行为,所以这次你击中了自己的腿,但没有击中。程序员有责任确保被取消引用的指针实际上指向指定类型的对象。

这是未定义的行为,因为
x1
属于
MYDer1
类型,您可以强制将其强制转换为
MYDer2
。代码可能无法工作,因为您没有真正对函数
进行任何操作这是未定义的行为,因为
x1
属于
MYDer1
类型,您可以强制将其强制转换为
MYDer2
。代码可能无法工作,因为您没有对函数
something2
执行任何操作。请尝试向
MYDer2添加成员变量e> 然后打印出来查看问题。

FWIW
auto*pd2=static\u cast(static\u cast(&x1));
是一种聪明但未定义的行为。编译器在以后不警告您并不是因为您违反了合同而真正需要做的事情。“MYDer2中没有声明“fun1”不,但是在direct基类
MYBase
@NathanOliver中声明了一个可访问的
fun1
,我不认为这是原因。同样编译的是:
MYDer2*pdn=nullptr;pdn->fun1()
(尽管它显然会崩溃).@MSalters如何访问?MYBase已将其声明为虚拟,并且MYDer2与MYDer1之间没有直接连接,因此MYDer2不应最终能够调用fun1()MYDer1的。@BaCh:它是
public
基类中的
public
成员。
virtual
不影响访问,只
private/protected/public
影响访问。FWIW
auto*pd2=static_cast(static_cast(&x1));
是一种聪明但未定义的行为。编译器不会在以后警告您,因为您违反了合同。“MYDer2中没有声明“fun1”不,但是在direct基类
MYBase
@NathanOliver中声明了一个可访问的
fun1
,我不认为这是原因。同样编译的是:
MYDer2*pdn=nullptr;pdn->fun1()
(尽管它显然会崩溃).@MSalters如何访问?MYBase已将其声明为虚拟,并且MYDer2与MYDer1之间没有直接连接,因此MYDer2不应最终能够调用fun1()MYDer1的。@BaCh:它是
public
基类中的
public
成员。
virtual
不会影响访问,只有
private/protected/public
会影响访问。我完全知道上面的内容是在攻击我自己。我真正不明白的是编译器为什么不在
pd2->fun1()上发出警告
@BaCh UB通常发生在程序员违反某些编译器无法正确执行的语言约定时。因此编译器根本不必警告UBs。MYDer2*pdn=nullptr;pdn->fun1()是否也违反约定?(此处为nullptr)“内存中可能有有效MYDer2对象的任何位置)。@BaCh
nullptr
代表“无效指针值”,因此取消引用无效指针也是违反约定的(无论它指向的对象类型如何)。我完全知道上面的内容是在攻击我自己。我真正不明白的是为什么编译器没有在
pd2->fun1()上发出警告
@BaCh UB通常发生在程序员违反某些编译器无法正确执行的语言约定时。因此编译器根本不必警告UBs。MYDer2*pdn=nullptr;pdn->fun1()是否也违反约定?(此处为nullptr)“内存中可能有有效MYDer2对象的任何位置)。@BaCh
nullptr
代表“无效指针值”,因此取消引用无效指针也是违反约定的(无论它指向的对象类型如何)。