这个C++测验答案背后的解释是什么
What is the explanation behind the answer to this C++ quiz?
>我在C++测验中遇到了这个问题(初学者C++):我的答案不正确,我想了解正确答案背后的解释 - "未定义的行为"
问题:函数 foo() 返回后,以下代码会发生什么?
class base
{
public:
base() { }
~base() { }
};
class derived : public base
{
private:
int *p_pi_values;
public:
derived() : p_pi_values(new int[100]) { }
~derived() { delete [] p_pi_values; }
};
void foo(void)
{
derived *p_derived = new derived();
base *p_base = p_derived;
// Do some other stuff here.
delete p_base;
}
我给出了这个答案,结果是错误的==>整数数组将无法正确删除。
正确答案 ==> 行为未定义。
类的析构函数不是virtual
。这只是语言的规则,如果通过指向基子对象的指针删除对象,则相应的基类必须具有虚拟析构函数,否则就是未定义的行为。
(实际上,如果基类没有虚拟析构函数,则编译器可能不会发出必要的代码来执行派生对象的所有必要清理。它只会假设你的对象与指针属于同一类型,而懒得进一步查看,因为事实上,对派生最多的对象的多态查找是以你不想不必要地强加的代价的。
§5.3.5/3:
在第一种替代方法(删除对象)中,如果操作数的静态类型与其动态类型不同,则静态类型应为操作数的动态类型的基类,静态类型应具有虚拟析构函数或行为未定义
你应该在基类中使析构函数成为虚拟的。现在代码的问题在于delete p_base
将导致调用基类的析构函数。不会调用派生类中的 one,也不会释放为整数数组分配的内存。
发生这种情况是因为如果一个方法在基类中不是虚拟的,编译器只是查看指针类型并调用位于该类型中的方法(在本例中 - 它是一个基类),即在编译期间根据指针的类型而不是指针引用的对象的实际类型来决定调用什么方法。
出于好奇,我检查了C++规格。问题的答案是第 5.3.5 节中的第 3 项:
在第一个备选方案(删除对象)中,如果静态类型 要删除的对象不同于其动态类型,即静态 类型应是要成为的对象的动态类型的基类 已删除,静态类型应具有虚拟析构函数或 行为未定义。
就个人而言,我会以与您相同的方式回答。如果忽略编译器的警告,则最可能的结果是不会调用派生类的析构函数。
我想编译器可以优化此代码,因此永远不会发生p_derived分配给p_base。
更具体地说,编译器可以将代码优化为一行。
删除新派生();
因此,可以认为,未定义的行为可能会改变编译器真正优化代码的方式。
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- C++避免重复声明的语法是什么
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- 你能解释一下什么运行时错误是如何解决它的吗?
- 我在 2D 数组的动态内存分配中遇到了一些奇怪的代码C++? 请解释一下这是什么?
- 这个 FOR-IF 循环的解释是什么?
- 有人能解释一下这些说法背后的逻辑是什么吗
- 是什么解释了这个片段中如此多的移动和破坏
- 在C++中使用 fread() 来解释这个结果是什么?
- 任何人都解释下面的代码,它的输出是什么?
- Deque的解释是什么,任何人都可以帮助我
- 解释"Bit String"的最佳方式是什么
- 有人能解释一下最多一次不变和存在、所有权和守恒规则是什么吗?
- 在OpenCV中,std::vector而不是cv::Mat的"type"是什么解释,我该如何更改它?(C++)
- 这个C++测验答案背后的解释是什么
- 请解释一下这个朋友声明是什么意思
- 你能用英语尽可能简单地解释一下什么是复制构造函数,什么时候需要使用它吗?
- 解释gcov输出以提高覆盖率的技巧是什么
- 有人能详细解释一下cv和cv2的不同之处吗?是什么让cv2比cv更好更快?