检查“this”是否有意义?为空?
假设我有一个带有成员函数的类;在该方法中,我检查 this == nullptr,如果是,则返回错误代码。
如果 this
为 null,则意味着该对象被删除。该方法是否能够返回任何内容?
更新:我忘了提到该方法可以从多个线程调用,并且当另一个线程位于成员函数内部时,它可能会导致对象被删除。
Say I have a class with a member function; inside that method, I check this == nullptr
, and if it is, return an error code.
If this
is 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 member function.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
在标准 C++ 中,情况并非如此,因为对空指针的任何调用都已经是未定义的行为,因此依赖此类检查的任何代码都是非标准的(甚至无法保证检查会被执行)。
请注意,这也适用于非虚拟函数。
然而,某些实现允许
this==0
,因此专门为这些实现编写的库有时会使用它作为 hack。 VC++ 和 MFC 就是此类组合的一个很好的例子 - 我不记得确切的代码,但我清楚地记得在 MFC 源代码中的某处看到if (this == NULL)
检查。它也可能作为调试辅助工具,因为在过去的某个时刻,由于调用者的错误,此代码被
this==0
命中,因此插入了一个检查以捕获未来的实例的。不过,断言对于此类事情更有意义。不,不是这个意思。这意味着在空指针上或在从空指针获得的引用上调用了方法(尽管获得这样的引用已经是UB)。这与删除无关,并且不需要任何该类型的对象曾经存在。
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).
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 seeingif (this == NULL)
checks in MFC source code somewhere.It may also be there as a debugging aid, because at some point in the past this code was hit with
this==0
because 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.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.您关于线程的注释令人担忧。我很确定你有可能导致崩溃的竞争条件。如果一个线程删除了一个对象并将指针归零,另一个线程可能会在这两个操作之间通过该指针进行调用,从而导致
this
为非空且无效,从而导致崩溃。同样,如果一个线程调用一个方法,而另一个线程正在创建对象,您也可能会崩溃。简而言之,您确实需要使用互斥锁或其他东西来同步对此变量的访问。您需要确保
this
决不为空,否则将会遇到问题。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
this
being 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.Short answer, you really need to use a mutex or something to synchonize access to this variable. You need to ensure that
this
is never null or you're going to have problems.我知道这已经很旧了,但我觉得现在我们正在处理 C++11-17,有人应该提到 lambda。 如果您将其捕获到将在稍后某个时间点异步调用的 lambda 中,则您的“this”对象可能会在调用该 lambda 之前被销毁。
即将其作为回调传递给某个从单独线程运行或一般只是异步运行的耗时函数
编辑:为了清楚起见,问题是“检查是否有意义”这是空的”我只是提供一个确实有意义的场景,随着现代 C++ 的更广泛使用,这种场景可能会变得更加普遍。
人为的例子:
这段代码是完全可以运行的。要查看不安全行为,只需注释掉对安全行为的调用并取消注释不安全行为调用即可。
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.
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++.
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.
FWIW,我已经在断言中使用了调试检查
(this != NULL)
,之前这有助于捕获有缺陷的代码。并不是说代码一定会走得太远而不会崩溃,而是在没有内存保护的小型嵌入式系统上,断言实际上有所帮助。在具有内存保护的系统上,如果使用 NULL
this
指针调用,操作系统通常会遇到访问冲突,因此断言this != NULL
的价值较小。然而,请参阅 Pavel 的评论,了解为什么它即使在受保护的系统上也不一定毫无价值。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.On systems with memory protection, the OS will generally hit an access violation if called with a NULL
this
pointer, so there's less value in assertingthis != NULL
. However, see Pavel's comment for why it's not necessarily worthless on even protected systems.是的,在至少一个编译器上,检查是有意义的。它将按照您的预期工作,并且可以触发它。它是否有用是值得怀疑的,因为格式良好的代码永远不应该在空指针上调用成员函数,但
assert(this);
很便宜。以下代码将在 MSVC 19.37 上编译并按预期运行。即使使用
/std:c++20 /Wall /external:anglebrackets /external:W0
,它也不会发出警告。程序打印
Null thispointer!
两次。Clang 16.0.0 会警告您
this
不能为 null,将检查转为无操作,并打印hello, world!
两次。 GCC 13.2 还会警告您正在对空指针调用成员函数,并且还会打印hello, world!
两次。在现实世界中,实际使用中,永远不需要取消引用
this
的成员函数将被声明为static
,因此使用 Clang 或 GCC 编译的实际代码会触发此错误(例如传递包含对象指针的默认初始化的struct
)在现代操作系统上会出现段错误。然而,健全性检查对于优化它的编译器来说是没有用的。On at least one compiler, yes, the check makes sense. It will work as you expect and it is possible to trigger it. Whether it’s useful is questionable, since well-formed code should never call a member function on a null pointer, but
assert(this);
is cheap.The following code will compile on MSVC 19.37 and run as expected. It will not issue a warning, even with
/std:c++20 /Wall /external:anglebrackets /external:W0
.The program prints
Null this pointer!
twice.Clang 16.0.0 will warn you that
this
cannot be null, turn the check into a no-op, and printhello, world!
twice. GCC 13.2 will additionally warn you that you are calling a member function on a null pointer, and also printhello, world!
twice.In real-world, practical use, a member function that never needs to dereference
this
would have been declaredstatic
, so realistic code compiled with Clang or GCC that triggers this bug (such as passing around a default-initializedstruct
containing an object pointer) would segfault on modern OSes. However, the sanity check would be useless on compilers that optimize it away.您的方法很可能(可能因编译器而异)能够运行并且也能够返回一个值。只要它不访问任何实例变量即可。如果尝试这样做就会崩溃。
正如其他人指出的那样,您不能使用此测试来查看对象是否已被删除。即使可以,它也不起作用,因为该对象可能会在测试之后但在测试后执行下一行之前被另一个线程删除。请改用线程同步。
如果
this
为 null,则您的程序中存在错误,很可能是您的程序设计中存在错误。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
this
is null there is a bug in your program, most likely in the design of your program.我还要补充一点,通常最好避免 null 或 NULL。我认为这里的标准再次发生变化,但现在 0 确实是您想要检查的内容,以绝对确定您得到了您想要的东西。
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.
这只是作为第一个参数传递给函数的指针(这正是它成为方法的原因)。只要您不谈论虚拟方法和/或虚拟继承,那么您就可以发现自己正在使用空实例执行实例方法。正如其他人所说,在问题出现之前,您几乎肯定不会在执行过程中走得太远,但健壮的编码可能应该使用断言来检查这种情况。至少,当您怀疑它可能由于某种原因发生,但需要准确追踪它发生在哪个类/调用堆栈时,这是有意义的。
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.
我知道这是一个老问题,但我想我会分享使用 Lambda 捕获
此代码段错误的
经验如果我从
lambda_func
中删除std::cout
语句代码运行完成。看起来,这个语句
f->lambda_func([f = std::move(f)] () mutable {
在调用成员函数之前处理 lambda 捕获。I know this is a old question, however I thought I will share my experience with use of Lambda capture
This code segment faults
If I remove the
std::cout
statement fromlambda_func
The code runs to completion.It seems like, this statement
f->lambda_func([f = std::move(f)] () mutable {
processes lambda captures before member function is invoked.