这个 C++ 程序会调用未定义的行为吗?
Does this C++ program invoke undefined behavior?
我正在阅读有关 static_cast 运算符的内容。
考虑以下示例:
#include <iostream>
class B { };
class D : public B
{
public:
void fun()
{
std::cout<<"fun() is called\n";
}
};
void f(B* pb,D* pd)
{
D* pd2=static_cast<D*>(pb);
B* pb2=static_cast<B*>(pd);
pd2->fun();
}
int main()
{
B b;
D d;
f(&b,&d);
}
上面写着:
In the example that follows, the line D* pd2 = static_cast(pb); is
not safe because D can have fields and methods that are not in B.
However, the line B* pb2 = static_cast(pd); is a safe conversion
because D always contains all of B.
In contrast to dynamic_cast, no run-time check is made on the
static_cast conversion of pb. The object pointed to by pb may not be
an object of type D, in which case the use of *pd2 could be
disastrous. For instance, calling a function that is a member of the D
class, but not the B class, could result in an access violation.
我在 gcc 4.8.1 和 MSVS 2010 上试过并得到输出 调用了 fun()。那么这个程序会调用未定义的行为吗?我的程序会在运行时崩溃吗? C++ 标准对此有何规定?如果我理解有误,请纠正我。
是的,当然是。
您正在通过强制从 B*
到 D*
的转换在仅是 B
的对象上调用 D
的成员函数。
它 "appears to work" 因为所涉及的函数不会尝试访问任何实际数据,因此,您的计算机不会在运行时注意到和抱怨内存访问。
事实上,我们甚至不需要费心去讨论函数调用是否有未定义的行为;演员表本身已经够糟糕了:
[C++14: 5.4.9/11]:
A prvalue of type “pointer to cv1 B
,” where B
is a class type, can be converted to a prvalue of type “pointer to cv2 D
,” where D
is a class derived (Clause 10) from B
, if a valid standard conversion from “pointer to D
” to “pointer to B
” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B
is neither a virtual base class of D
nor a base class of a virtual base class of D
. The null pointer value (4.10) is converted to the null pointer value of the destination type. If the prvalue of type “pointer to cv1 B
” points to a B
that is actually a subobject of an object of type D
, the resulting pointer points to the enclosing object of type D
. Otherwise, the behavior is undefined.
当您使用 static_cast
将 B*
向上转换为 D*
时,编译器相信您已经检查过这是正确的做法并且指针确实指向 D
。编译器或运行时无法代表您检查它(没有 RTTI)。因此,当您调用 foo
时,它会愉快地放入调用它的代码,并且因为它不是虚拟方法,所以会调用正确的 D::foo
。如果您访问 foo
中的任何 D
特定成员,应用程序将崩溃、烧毁等。这就是现实。
理论是,您当然会调用未定义的行为。
我正在阅读有关 static_cast 运算符的内容。
考虑以下示例:
#include <iostream>
class B { };
class D : public B
{
public:
void fun()
{
std::cout<<"fun() is called\n";
}
};
void f(B* pb,D* pd)
{
D* pd2=static_cast<D*>(pb);
B* pb2=static_cast<B*>(pd);
pd2->fun();
}
int main()
{
B b;
D d;
f(&b,&d);
}
上面写着:
In the example that follows, the line D* pd2 = static_cast(pb); is not safe because D can have fields and methods that are not in B. However, the line B* pb2 = static_cast(pd); is a safe conversion because D always contains all of B.
In contrast to dynamic_cast, no run-time check is made on the static_cast conversion of pb. The object pointed to by pb may not be an object of type D, in which case the use of *pd2 could be disastrous. For instance, calling a function that is a member of the D class, but not the B class, could result in an access violation.
我在 gcc 4.8.1 和 MSVS 2010 上试过并得到输出 调用了 fun()。那么这个程序会调用未定义的行为吗?我的程序会在运行时崩溃吗? C++ 标准对此有何规定?如果我理解有误,请纠正我。
是的,当然是。
您正在通过强制从 B*
到 D*
的转换在仅是 B
的对象上调用 D
的成员函数。
它 "appears to work" 因为所涉及的函数不会尝试访问任何实际数据,因此,您的计算机不会在运行时注意到和抱怨内存访问。
事实上,我们甚至不需要费心去讨论函数调用是否有未定义的行为;演员表本身已经够糟糕了:
[C++14: 5.4.9/11]:
A prvalue of type “pointer to cv1B
,” whereB
is a class type, can be converted to a prvalue of type “pointer to cv2D
,” whereD
is a class derived (Clause 10) fromB
, if a valid standard conversion from “pointer toD
” to “pointer toB
” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, andB
is neither a virtual base class ofD
nor a base class of a virtual base class ofD
. The null pointer value (4.10) is converted to the null pointer value of the destination type. If the prvalue of type “pointer to cv1B
” points to aB
that is actually a subobject of an object of typeD
, the resulting pointer points to the enclosing object of typeD
. Otherwise, the behavior is undefined.
当您使用 static_cast
将 B*
向上转换为 D*
时,编译器相信您已经检查过这是正确的做法并且指针确实指向 D
。编译器或运行时无法代表您检查它(没有 RTTI)。因此,当您调用 foo
时,它会愉快地放入调用它的代码,并且因为它不是虚拟方法,所以会调用正确的 D::foo
。如果您访问 foo
中的任何 D
特定成员,应用程序将崩溃、烧毁等。这就是现实。
理论是,您当然会调用未定义的行为。