这是我的代码。
class IService {
};
class X_Service {
public:
void service1() {
std::cout<< "Service1 Running..."<<std::endl;
}
};
int main() {
IService service;
auto func = reinterpret_cast<void (IService::*)()>(&X_Service::service1);
(service.*(func))();
return 0;
}
我不知道这是如何工作的。我没有继承IService,也没有创建X_Service对象,但是它可以工作。有人可以解释吗?
您可以通过多种方式打破语言规则,仍然编写可编译和运行的代码。通过在此处使用reinterpret_cast
并进行无效的转换,您已经破坏了语言规则,并且您的程序具有未定义的行为。
这意味着它似乎可以工作,可能崩溃或只能做与您想要的完全不同的事情。
在您的情况下,它似乎可以正常工作,但是它仍然是UB,并且代码无效。
成员函数只是一个函数,除了局部变量和参数外,还具有一块存储类对象地址的内存。该内存保留使用this
关键字时要访问的地址。
如果您在错误的对象或nullptr上调用成员函数,则基本上只是使this
指针指向无效的对象。
您的功能无法访问this
,这就是您的程序不会崩溃的原因。
也就是说,这仍然是未定义的行为,任何事情都可能发生。
reinterpret_cast
指向不同类型的函数或成员函数指针时,除非您首先将其转换回其原始类型并对其进行调用,否则永远不允许调用结果指针。违反此规则将导致undefined behavior。这意味着您失去了任何语言保证,即程序将以任何特定方式运行,而没有来自特定编译器的其他保证。
reinterpret_cast
通常很危险,因为它完全绕过了类型系统。如果使用它,则需要始终通过查看语言规则来验证自己,是否明确定义了强制类型转换和使用结果的方式。 reinterpret_cast
告诉编译器您知道自己在做什么,并且即使结果是无意义的,也不希望出现任何警告或错误。
#include <iostream>
class IService {
public:
int x;
};
class X_Service {
public:
int x;
void service1() {
this->x = 65;
std::cout << this->x << std::endl;
}
};
int main() {
IService service;
auto func = reinterpret_cast<void (IService::*)()>(&X_Service::service1);
(service.*(func))();
std::cout << service.x << std::endl;
std::cin.get();
X_Service derp;
(derp.service1)();
std::cout << derp.x << std::endl;
return 0;
}
因此,从一开始,auto
赋予了创建无类型安全指针void (IService::*)()
的权力,而对象本身的实例为this->
,无论您从其隐身继承的任何类的成员函数是什么。唯一的问题是,变量是根据您隐身继承的类来解释的,如果类型不同,则可能导致堆栈损坏。方式很酷,但不可避免地会导致堆栈损坏,您可以做以下有趣的事情。
class IService { public: char x; };
您的IDE将检测到您的IService对象的堆栈损坏,但得到的输出是
65 A
是值得的,但是您会发现在进行这种隐式继承时会出现问题。我也在使用86x编译器。因此,基本上我的变量都已对齐。假设例如,如果我在Iservice的int x上方添加int y,则此程序将输出废话。基本上它仅能工作,因为我的类是二进制兼容的。