【问题标题】:How to prevent calling a non-static function of an uninitialized C++ object如何防止调用未初始化的 C++ 对象的非静态函数
【发布时间】:2021-07-20 19:38:36
【问题描述】:

以下是我目前正在处理的一个大型遗留项目中出现的问题的一个最小示例。

我有一个 main 函数,它应该创建一个新对象 ob 一个全局对象指针 B* B。 由于配置错误,这没有完成,并且调用了非静态方法b->do_something()。 我的期望是这样做会导致分段错误。

代码确实会产生分段错误,但仅在比较 a == nullptr 时。 这让我非常困惑,因为我正在寻找 a 的初始化问题。 我不知道即使该对象从未初始化,也可以调用对象的非静态方法。

我的问题是将来如何防止这种混乱?那么如何在调用 NULL 对象的非静态方法时使执行崩溃呢?

我还不满意的可能解决方案:

  • 将每次调用b->do_something() 包装为if(b != nullptr)
    • 非常冗长
  • do_something() 更改为if (&a == nullptr)
    • 工作但被 clang-tidy 标记(CI 检查失败)
    • 根据this"A reference can not be NULL"
  • do_something() 扩展为if (this == nullptr || a == nullptr)
    • 工作但被 clang-tidy 标记(CI 检查失败)
#include <stdio.h>
#include <stdexcept>

class A {};

class B {
    A *a = nullptr;
public:
    B(){
        // Sometimes doing this
        a = new A();
    }
    void do_something() {
        if(a == nullptr){ // SegFault appears here because B was never created and accessing this->a evaluates to nullptr->a
            throw std::runtime_error("Error");
        } else {
            printf("Doing Something");
        }
    } 
};


// Global variable
B *b;

int main()
{
    // Long code that should have called:
    // b = new B();
    b->do_something();

    return 0;
}

【问题讨论】:

  • 与其说B 没有被构造,不如b 没有分配给任何东西。作为一个全局变量,它最初应该为空。你可以试试if (b) b-&gt;do_something();
  • 理想情况下(为了安全),应该检查每个指针访问。 (一旦检查,您可能会通过引用而不是指针传递以避免额外检查)。

标签: c++ class object static


【解决方案1】:

你不能阻止用户犯所有可能的错误,而且大多数时候尝试都是徒劳的(这不是 Java ;)。您不应在未初始化的指针上调用成员(实际上在您的代码中它已被初始化,因为它是全局的,但使用 nullptr)。这不是特定于您的班级的,但在使用指针时必须始终考虑这一点。

但是,有一些方法可以缓解这个问题。您可以将B::do_something 设为私有和成为朋友的免费功能:

void do_something(B* b) {
    if (b) b->do_something();
}

从您的最小示例中不是很清楚,但是如果 do_something 可以实现为非成员,那么您首先不需要成员函数。

我的问题是将来如何防止这种混乱?那么如何在调用 NULL 对象的非静态方法时使执行崩溃呢?

没有便携的方法可以做到这一点。在 nullptr 上调用成员函数是未定义的行为。任何事情都可能发生,包括它似乎有效。如果您删除检查 a 是否为 nullptrdo_something 实际上没有使用 this 并且很可能会与大多数编译器一起使用。

在方法内部检查this != nullptr也无济于事,因为一旦调用,你已经处于未定义的行为中。

【讨论】:

  • 感谢您的快速答复!你是对的do_something 可以实现为非成员函数。我只是喜欢它的“Java 风格”对象方法。但如果它会导致这样的调试问题,我可能需要改变主意。
  • @Kaio1012 正如我所写,问题不在于你的班级,问题在于有人在写b-&gt;do_something() 却不确定b 指向一个对象,这不是你所在的对象控制。
  • 你完全正确,我只是没想到会这样。也许还有一个问题,然后从已编译的程序视图中添加 static 关键字有什么区别?在该函数中使用this 时是否会出现编译器错误?
  • @Kaio1012 小心static,它有不同的含义取决于上下文。静态方法属于类,而非静态方法属于实例,即没有可以在静态方法中使用的this
  • 好的,知道了!非常感谢您的帮助:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-14
  • 2015-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多