在无效对象上调用成员函数

时间:2015-10-06 10:44:20

标签: c++ c++11 visual-c++

在visual studio中,您可以在已删除的对象上调用函数:

class Foo {
    void bar() {
        ...
    }
};

您实际上可以执行以下代码而不会出错:

Foo * foo = new Foo();
delete foo;
foo->bar();

并且只要Foo :: bar中的代码不对this指针执行任何操作。我的第一个问题是,这种未定义的行为是否正常工作或是否符合标准?

第二个问题:

如果我们将代码更改为:

Foo * foo = new Foo();
delete foo;
foo = nullptr;
foo->bar();

我们可以在Foo :: bar中使用以下检查

if (this == nullptr) {
    ...
}

以确定我们是否对有效对象进行操作。虽然这样做似乎不太好主意,但是有没有任何架构/设计可能有用呢?

4 个答案:

答案 0 :(得分:6)

两种用途都只是简单的UB。

编译器甚至可以优化

if (this == nullptr),因为合法代码中的this永远不会nullptr。 Clang警告说这个顺便说一句。

更新:从gcc 6开始,gcc实际上会根据假设this != nullptr进行优化。他们还添加了警告。见live

答案 1 :(得分:3)

如果方法不执行任何类成员,则它应该是static。可以始终在没有任何对象的情况下调用静态方法,如果不是这样,编译器会给出错误。

支票

  

if ( this )

对我来说非常肮脏。仅出于测试目的,assert听起来很有帮助。但是如果我必须担心一个方法可以在没有对象的情况下调用,那么就没有机会得到指针(this)设置为nullptr有效的保存。这是错误转移而不是解决方案。

如果方法是virtual,那么对this==nullptr的检查就会迟到,因为无法找到vtable ptr。

程序的设计应该以适当的方式处理新的/删除。也许使用任何类型的智能指针都可以提供帮助。

答案 2 :(得分:2)

foo->bar()始终取消引用foo

删除指针后取消引用指针会产生未定义的行为。如果Visual Studio碰巧没有出错,那就是偶然。任何结果都是允许未定义的行为,包括没有明显的错误。

foo设置为nullptr,然后调用foo->bar()也会产生未定义的行为,因为它取消引用了NULL指针。在this == nullptr内测试Foo::bar()并不会改变这一点,因为调用者甚至在调用Foo::bar()之前就已经表现出未定义的行为。

答案 3 :(得分:0)

<Window x:Class="ProtocolAnalyzer.createByProtocol"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="createByProtocol" Height="506" Width="384">

    <Grid Margin="0,0,2,4">
       <DataGrid x:Name="dataGridTable" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="452" Width="245" SelectedCellsChanged="dataGridTable_SelectedCellsChanged" >
                  <DataGrid.Columns>           
                 </DataGrid.Columns>
       </DataGrid>
       <Button Content="Create" HorizontalAlignment="Left" Margin="275,165,0,0" VerticalAlignment="Top" Width="75" Height="75" Click="Button_Click"/>

     </Grid>
</Window>

此语句表示解码foo中的地址。并将其放入调用堆栈,然后将bar的变量内容放入调用堆栈。最后,执行该功能。

如果:: bar()函数完全为空。没关系。

否则,即使您只是声明一些变量。你的函数调用已经弄脏了* foo的地址。

1)如果foo == nullptr。 0x0000000是一个只读部分,你的程序会崩溃,或者当你的main结束时抛出异常。

2)如果foo!= nullptr,但它被删除了。堆将损坏,内存屏障可能会检测到您的操作,它可能会崩溃

3)如果foo!= nullptr,那就是堆栈变量。堆栈很脏,会崩溃。