C++ 检查这是否为空

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1844005/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:21:47  来源:igfitidea点击:

Checking if this is null

c++pointersnull

提问by user156144

Does it ever make sense to check if thisis null?

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

Say I have a class with a method; inside that method, I check this == NULL, and if it is, return an error code.

假设我有一个带有方法的类;在该方法中,我检查this == NULL,如果是,则返回错误代码。

If thisis null, then that means the object is deleted. Is the method even able to return anything?

如果是空的,那么这意味着该对象被删除。该方法甚至能够返回任何东西吗?

Update:I forgot to mention that the method can be called from multiple threads and it may cause the object to be deleted while another thread is inside the method.

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

回答by Pavel Minaev

Does it ever make sense to check for this==null? I found this while doing a code review.

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

In standard C++, it does not, because any call on a null pointer is already undefined behavior, so any code relying on such checks is non-standard (there's no guarantee that the check will even be executed).

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

Note that this holds true for non-virtual functions as well.

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

Some implementations permit this==0, however, and consequently libraries written specifically for those implementations will sometimes use it as a hack. A good example of such a pair is VC++ and MFC - I don't recall the exact code, but I distinctly remember seeing if (this == NULL)checks in MFC source code somewhere.

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

It may also be there as a debugging aid, because at some point in the past this code was hit with this==0because of a mistake in the caller, so a check was inserted to catch future instances of that. An assert would make more sense for such things, though.

它也可以作为调试帮助,因为在过去的某个时刻,this==0由于调用者的错误,这段代码被击中,因此插入了一个检查以捕获未来的实例。不过,断言对此类事情更有意义。

If this == null then that means the object is deleted.

如果 this == null 则表示该对象已被删除。

No, it doesn't mean that. It means that a method was called on a null pointer, or on a reference obtained from a null pointer (though obtaining such a reference is already U.B.). This has nothing to do with delete, and does not require any objects of this type to have ever existed.

不,不是那个意思。这意味着在空指针或从空指针获得的引用上调用了一个方法(尽管获得这样的引用已经是 UB)。这与 无关delete,并且不需要任何这种类型的对象曾经存在过。

回答by Tim Sylvester

Your note about threads is worrisome. I'm pretty sure you have a race condition that can lead to a crash. If a thread deletes an object and zeros the pointer, another thread could make a call through that pointer between those two operations, leading to thisbeing non-null and also not valid, resulting in a crash. Similarly, if a thread calls a method while another thread is in the middle of creating the object, you may also get a crash.

您关于线程的注释令人担忧。我很确定你有可能导致崩溃的竞争条件。如果一个线程删除了一个对象并将指针归零,另一个线程可以在这两个操作之间通过该指针进行调用,导致this非空且无效,从而导致崩溃。同样,如果一个线程调用一个方法而另一个线程正在创建对象,您也可能会崩溃。

Short answer, you really need to use a mutex or something to synchonize access to this variable. You need to ensure that thisis nevernull or you're going to have problems.

简短的回答,你真的需要使用互斥锁或其他东西来同步对这个变量的访问。你需要确保this从来没有空,否则你会遇到问题。

回答by Michael Burr

FWIW, I have used debugging checks for (this != NULL)in assertions before which have helped catch defective code. Not that the code would have necessarily gotten too far with out a crash, but on small embedded systems that don't have memory protection, the assertions actually helped.

FWIW,我(this != NULL)之前在断言中使用了调试检查,这有助于捕获有缺陷的代码。并不是说代码在没有崩溃的情况下一定会走得太远,但是在没有内存保护的小型嵌入式系统上,断言实际上有帮助。

On systems with memory protection, the OS will generally hit an access violation if called with a NULL thispointer, so there's less value in asserting this != NULL. However, see Pavel's comment for why it's not necessarily worthless on even protected systems.

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

回答by Josh Sanders

I know that this is old but I feel like now that we're dealing with C++11-17 somebody should mention lambdas. If you capture this into a lambda that is going to be called asynchronously at a later point in time, it is possible that your "this" object gets destroyed before that lambda is invoked.

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

i.e passing it as a callback to some time-expensive function that is run from a separate thread or just asynchronously in general

即将它作为回调传递给一些从单独的线程运行或一般异步运行的耗时函数

EDIT: Just to be clear, the question was "Does it ever make sense to check if this is null" I am merely offering a scenario where it does make sense that might become more prevalent with the wider use of modern C++.

编辑:为了清楚起见,问题是“检查这是否为空是否有意义”我只是提供一个场景,在这种场景中,随着现代 C++ 的广泛使用,它可能变得更加普遍。

Contrived example: This code is completely runable. To see unsafe behavior just comment out the call to safe behavior and uncomment the unsafe behavior call.

人为的例子:这段代码是完全可以运行的。要查看不安全行为,只需注释掉对安全行为的调用并取消对不安全行为调用的注释。

#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;
}

回答by Prasad Joshi

I know this is a old question, however I thought I will share my experience with use of Lambda capture

我知道这是一个老问题,但是我想我会分享我使用 Lambda 捕获的经验

#include <iostream>
#include <memory>

using std::unique_ptr;
using std::make_unique;
using std::cout;
using std::endl;

class foo {
public:
    foo(int no) : no_(no) {

    }

    template <typename Lambda>
    void lambda_func(Lambda&& l) {
        cout << "No is " << no_ << endl;
        l();
    }

private:
    int no_;
};

int main() {
    auto f = std::make_unique<foo>(10);

    f->lambda_func([f = std::move(f)] () mutable {
        cout << "lambda ==> " << endl;
        cout << "lambda <== " << endl;
    });

    return 0;
}

This code segment faults

此代码段错误

$ g++ -std=c++14  uniqueptr.cpp  
$ ./a.out 
Segmentation fault (core dumped)

If I remove the std::coutstatement from lambda_funcThe code runs to completion.

如果我std::coutlambda_func代码运行到完成中删除语句。

It seems like, this statement f->lambda_func([f = std::move(f)] () mutable {processes lambda captures before member function is invoked.

看起来,这个语句f->lambda_func([f = std::move(f)] () mutable {在调用成员函数之前处理 lambda 捕获。

回答by Carsten

Your method will most likely (may vary between compilers) be able to run and also be able to return a value. As long as it does not access any instance variables. If it tries this it will crash.

您的方法很可能(可能因编译器而异)能够运行并且还能够返回一个值。只要它不访问任何实例变量。如果它尝试这样做,它将崩溃。

As others pointed out you can not use this test to see if an object has been deleted. Even if you could, it would not work, because the object may be deleted by another thread just after the test but before you execute the next line after the test. Use Thread synchronization instead.

正如其他人指出的那样,您不能使用此测试来查看对象是否已被删除。即使可以,它也不起作用,因为该对象可能会在测试之后但在测试后执行下一行之前被另一个线程删除。改用线程同步。

If thisis null there is a bug in your program, most likely in the design of your program.

如果this为 null,则程序中存在错误,很可能是程序设计中的错误。

回答by Carsten

This is just a pointer passed as the first argument to a function (which is exactly what makes it a method). So long as you're not talking about virtual methods and/or virtual inheritance, then yes, you can find yourself executing an instance method, with a null instance. As others said, you almost certainly won't get very far with that execution before problems arise, but robust coding should probably check for that situation, with an assert. At least, it makes sense when you suspect it could be occuring for some reason, but need to track down exactly which class / call stack it's occurring in.

这只是一个作为第一个参数传递给函数的指针(这正是使它成为方法的原因)。只要您不是在谈论虚拟方法和/或虚拟继承,那么是的,您会发现自己正在执行一个带有空实例的实例方法。正如其他人所说,您几乎肯定不会在问题出现之前执行该操作,但是健壮的编码可能应该检查这种情况,并带有断言。至少,当您怀疑它可能出于某种原因发生时,这是有道理的,但需要准确地追踪它发生在哪个类/调用堆栈中。

回答by Charles Eli Cheese

I'd also add that it's usually better to avoid null or NULL. I think the standard is changing yet again here but for now 0 is really what you want to check for to be absolutely sure you're getting what you want.

我还要补充一点,通常最好避免 null 或 NULL。我认为这里的标准再次发生变化,但现在 0 确实是您想要检查的内容,以绝对确定您得到了想要的东西。