检查这是否为空

use*_*144 60 c++ null pointers

检查是否为空是否有意义?

说我有一个方法的课; 在该方法中,我检查this == NULL,如果是,则返回错误代码.

如果为空,则意味着该对象被删除.该方法甚至能够返回任何东西吗?

更新:我忘了提到可以从多个线程调用该方法,它可能导致该对象被删除而另一个线程在该方法内.

Pav*_*aev 75

检查这个== null是否有意义?我在进行代码审查时发现了这一点.

在标准C++中,它没有,因为对空指针的任何调用都是未定义的行为,因此依赖于此类检查的任何代码都是非标准的(不能保证甚至会执行检查).

请注意,这也适用于非虚函数.

this==0但是,某些实现允许,因此专门为这些实现编写的库有时会将其用作黑客.这样一对的一个很好的例子是VC++和MFC - 我不记得确切的代码,但我清楚地记得if (this == NULL)在某处看到MFC源代码中的检查.

它也可能作为调试辅助工具,因为在过去的某些时候,this==0由于调用者的错误而导致此代码被命中,因此插入检查以捕获其未来的实例.但是,对于这样的事情,断言会更有意义.

如果这= = null则表示该对象已被删除.

不,这并不意味着.这意味着在空指针或从空指针获得的引用上调用方法(尽管获得这样的引用已经是UB).这与此无关delete,并且不需要任何此类对象存在.

  • 使用`delete`,相反通常是正确的 - 如果你`删除'一个对象,它不会被设置为NULL,然后你尝试在它上面调用一个方法,你会发现`this!= NULL `,但如果其内存已被回收以供其他对象使用,它可能会崩溃或表现得很奇怪. (9认同)
  • Visual Studio 6于1998年6月发布.C++标准于9月发布.好的,所以微软本可以预见到这个标准,但原则上许多预标准编译器没有实现标准就不足为奇了;-) (8认同)
  • `delete`可能并不总是得到一个l值,考虑`删除这个+ 0;` (5认同)
  • 请注意,MFC有点特殊,因为它从来没有真正用于VC++之外的其他任何东西,并且由于团队有联系,MFC团队可以依赖这样的实现细节(并确保他们会这样做)在需要的时候陪伴,并按照预期行事).由于即使对于VC++,此行为也未以其他方式记录或保证,因此第三方库不能依赖它. (5认同)
  • 只是为了好玩,这里有一篇关于MFC的好文章,它使用if(this == 0)http://www.viva64.com/en/b/0226/ (2认同)

Tim*_*ter 27

你对线程的说明令人担忧.我很确定你的比赛条件会导致车祸.如果一个线程删除一个对象并将指针归零,则另一个线程可以在这两个操作之间通过该指针进行调用,从而导致this非空且无效,从而导致崩溃.类似地,如果一个线程调用一个方法,而另一个线程正在创建该对象,你也可能会崩溃.

简而言之,您确实需要使用互斥锁或其他东西来同步访问此变量.你需要确保this从来没有空,否则你会遇到问题.

  • "你需要确保它永远不会为空" - 我认为更好的方法是确保`operator->`的左操作数永远不为空:)但除此之外,我希望我能+10这个. (6认同)

Mic*_*urr 6

FWIW,我(this != NULL)在断言中使用了调试检查,之前有助于捕获有缺陷的代码.并不是说代码在崩溃时必然会变得太过分,但在没有内存保护的小型嵌入式系统上,断言实际上有所帮助.

在具有内存保护的系统上,如果使用NULL this指针调用操作系统通常会遇到访问冲突,因此断言的值较小 this != NULL.但是,请参阅Pavel的评论,为什么即使受保护的系统也不一定无价值.


Jos*_*ers 6

我知道这已经过时了,但我觉得现在我们正在处理C++ 11-17,有人应该提一下lambdas.如果将其捕获到将在稍后的某个时间异步调用的lambda中,则可能在调用lambda之前销毁"this"对象.

即将它作为回调传递给一些时间昂贵的函数,该函数从一个单独的线程运行或者一般只是异步运行

编辑:只是要清楚,问题是"检查这是否为空是否有意义"我只是提供了一个有意义的场景,它可能会随着现代C++的广泛使用而变得更加普遍.

受控示例:此代码完全可运行.要查看不安全的行为,只需注释掉对安全行为的调用并取消注释不安全的行为调用.

#include <memory>
#include <functional>
#include <iostream>
#include <future>

class SomeAPI
{
public:
    SomeAPI() = default;

    void DoWork(std::function<void(int)> cb)
    {
        DoAsync(cb);
    }

private:
    void DoAsync(std::function<void(int)> cb)
    {
        std::cout << "SomeAPI about to do async work\n";
        m_future = std::async(std::launch::async, [](auto cb)
        {
            std::cout << "Async thread sleeping 10 seconds (Doing work).\n";
            std::this_thread::sleep_for(std::chrono::seconds{ 10 });
            // Do a bunch of work and set a status indicating success or failure.
            // Assume 0 is success.
            int status = 0;
            std::cout << "Executing callback.\n";
            cb(status);
            std::cout << "Callback Executed.\n";
        }, cb);
    };
    std::future<void> m_future;
};

class SomeOtherClass
{
public:
    void SetSuccess(int success) { m_success = success; }
private:
    bool m_success = false;
};
class SomeClass : public std::enable_shared_from_this<SomeClass>
{
public:
    SomeClass(SomeAPI* api)
        : m_api(api)
    {
    }

    void DoWorkUnsafe()
    {
        std::cout << "DoWorkUnsafe about to pass callback to async executer.\n";
        // Call DoWork on the API.
        // DoWork takes some time.
        // When DoWork is finished, it calls the callback that we sent in.
        m_api->DoWork([this](int status)
        {
            // Undefined behavior
            m_value = 17;
            // Crash
            m_data->SetSuccess(true);
            ReportSuccess();
        });
    }

    void DoWorkSafe()
    {
        // Create a weak point from a shared pointer to this.
        std::weak_ptr<SomeClass> this_ = shared_from_this();
        std::cout << "DoWorkSafe about to pass callback to async executer.\n";
        // Capture the weak pointer.
        m_api->DoWork([this_](int status)
        {
            // Test the weak pointer.
            if (auto sp = this_.lock())
            {
                std::cout << "Async work finished.\n";
                // If its good, then we are still alive and safe to execute on this.
                sp->m_value = 17;
                sp->m_data->SetSuccess(true);
                sp->ReportSuccess();
            }
        });
    }
private:
    void ReportSuccess()
    {
        // Tell everyone who cares that a thing has succeeded.
    };

    SomeAPI* m_api;
    std::shared_ptr<SomeOtherClass> m_data = std::shared_ptr<SomeOtherClass>();
    int m_value;
};

int main()
{
    std::shared_ptr<SomeAPI> api = std::make_shared<SomeAPI>();
    std::shared_ptr<SomeClass> someClass = std::make_shared<SomeClass>(api.get());

    someClass->DoWorkSafe();

    // Comment out the above line and uncomment the below line
    // to see the unsafe behavior.
    //someClass->DoWorkUnsafe();

    std::cout << "Deleting someClass\n";
    someClass.reset();

    std::cout << "Main thread sleeping for 20 seconds.\n";
    std::this_thread::sleep_for(std::chrono::seconds{ 20 });

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

  • 正是!在这种情况下检查"this"== nullptr或NULL是否足够,因为"this"将悬空.我只是提到它,因为有些人甚至怀疑这种语义甚至需要存在. (3认同)
  • 但即使对象被破坏,lambda也不会以悬空的非空捕获的`this`指针结束吗? (2认同)
  • 这个答案非常有用,我认为它属于这个问题的线程,因为它解决了我们应该检查 this 是否为空的特定情况。 (2认同)