我在理解下面的代码时遇到了一些问题。为什么我能调用c()函数?
#include <iostream>
#include <list>
using namespace std;
class A
{};
class B : public A
{
public:
void c() { cout << "c: B" << ++x << endl; }
int x;
};
int main(void) {
class A *a = new A();
static_cast<B*>(a)->c();
return 0;
}
ouptut是:
constr A
c: B1
答案 0 :(得分:6)
这是因为未定义的行为可能会产生意外结果,包括似乎可以正常工作。
任何事情都可能发生,这是未定义的行为。实际上可能发生的事情是B::c()
使用B::x
存储在B
实例中的位置,紧跟在 1 A
之后子对象。它会覆盖属于其他对象的内存。在您的情况下,该值之前为0
并变为1
,但它可能是该位置的真实所有者放置的任何值。
(1)如果编译器实现了空基类优化,B::x
和B::A
子对象实际上可能会重叠。
如果你问为什么编译器不会阻止它,那是因为你使用static_cast
覆盖了类型检查。在所有演员阵容中,只有dynamic_cast
会关注对象的真实类型,即使这很容易打破。
你正在编写两个以非常不安全的方式定义明确的操作。
A*
转换为B*
,即使它知道没有B
个对象,因为B*
非常有用为了再次投掷的单独目的。B*
来访问B
成员。问题在于你使用这个“假”B*
来表示其他东西,而不是回归到它的真实类型。
答案 1 :(得分:1)
dynamic_cast可以提供有关成功或失败的信息(如果您至少有一个我假设您的真实程序会有的虚函数),但static_cast不能。 static_cast不使用运行时类型标识。有正当理由进行投射,但您需要遵守规则以避免未定义的行为。
在向下转换时,首选动态转换为静态转换。当然我不知道为什么有人会编写一个带继承的程序,而不是一个虚函数(除非它只是一个简单的例子来演示一个问题),但也许有一个用例或理由。 http://en.cppreference.com/w/cpp/language/dynamic_cast
如果您绝对必须使用static_cast,那么您需要遵循有关何时有意义的规则。不幸的是,static_cast不会向您提供异常或失败通知,因此必须谨慎使用。 http://en.cppreference.com/w/cpp/language/static_cast
2)如果new_type是某个D类和类型的指针或引用 表达式是指向其非虚拟基础B的指针或引用, static_cast执行向下转换。这样的static_cast没有运行时间 检查以确保对象的运行时类型实际上是D,并且可以 只有在通过其他方式保证此前提条件时才能安全使用, 例如在实现静态多态时。安全垂头丧气可能是 用dynamic_cast完成。
要解决有关您的示例工作原因的问题,请允许我引用ANSI标准
1.3.24 [defns.unde fi ned]本国际标准没有要求的未定行为行为[注:未定义 当本国际标准遗漏任何行为时,可能会出现这种行为 明确的行为定义或程序使用错误的行为 构造或错误的数据。允许的未定义行为范围从 完全忽视这种情况,结果不可预测 在记录中的翻译或程序执行期间表现 环境的方式特征(有或没有发行 (诊断消息),终止翻译或执行 (发布诊断信息)。许多错误的计划 构造不会产生未定义的行为;他们必须是 诊断。 - 结束说明]
答案 2 :(得分:0)
随时避免演员表演 - 这通常意味着出现了问题。
在您的示例中,您正在尝试将对象转换为具有其处理的更多权力。
就像对某人说这辆车是卡车并指向汽车。
当一个Trebant到达而不是一辆马车时,它会流下眼泪