检查这是否为空
是否有意义,检查这是否为空?
说我有一个方法类; 在那个方法里面,我检查this == NULL
,如果是,返回一个错误码。
如果这是空的,那就意味着该对象被删除。 该方法甚至能够返回任何东西?
更新:我忘了提及可以从多个线程调用该方法,并可能导致该对象被删除,而另一个线程在该方法内。
检查这个== null是否合理? 我在做代码审查的时候发现了这个。
在标准的C ++中,它没有,因为任何对空指针的调用都已经是未定义的行为,所以任何依赖这种检查的代码都是非标准的(不保证检查甚至会被执行)。
请注意,这也适用于非虚拟function。
但是,有些实现允许this==0
,因此专门为这些实现编写的库有时会用它作为破解。 一个很好的例子是VC ++和MFC – 我不记得确切的代码,但我清楚地记得看到if (this == NULL)
检查MFC源代码的地方。
它也可能作为一个debugging帮助,因为在过去的某个时候,由于调用者的错误,这个代码被this==0
命中,因此插入了一个检查来捕获未来的实例。 然而,断言对于这样的事情会更有意义。
如果这个== null,那么意味着对象被删除。
不,这并不意味着。 这意味着一个方法被调用在一个空指针或从一个空指针获得的引用(尽pipe获得这样的引用已经是UB)。 这与delete
没有任何关系,并且不需要这种types的任何对象。
你关于线程的说明是令人担忧的。 我很确定你有一个可能导致崩溃的竞赛条件。 如果一个线程删除了一个对象,并且指针为零,另一个线程可以通过这两个操作之间的指针进行调用,导致this
非空并且无效,导致崩溃。 同样,如果一个线程调用另一个线程正在创build对象的过程中,您也可能会崩溃。
简单的答案,你真的需要使用互斥或什么东西来同步访问这个variables。 你需要确保this
不是空的,否则你将会遇到问题。
FWIW,我已经在断言中使用了debugging检查(this != NULL)
,之前有助于捕获有缺陷的代码。 并不是说代码在崩溃的时候一定会过度,而是在没有内存保护的小型embedded式系统中,这些断言实际上是有帮助的。
在具有内存保护的系统上,如果使用NULL指针调用操作系统,操作系统通常会遇到访问冲突,所以断言this != NULL
值较小。 但是,请参阅Pavel的评论,为什么在受保护的系统上它不一定是毫无价值的。
我知道这是旧的,但我觉得现在我们正在处理C ++ 11-17有人应该提到lambda。 如果你把它捕获到一个将在后面被asynchronous调用的lambda中,那么在调用lambda之前,你的“this”对象可能会被销毁。
即将其作为callback函数传递给从一个单独的线程运行的一些耗时的函数,或者通常只是asynchronous运行
编辑:只是要清楚,问题是“它是否有意义,检查这是否为空”我只是提供一个情况下,它是有道理的,可能会更广泛地使用现代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; }
你的方法很可能(可能会在编译器之间有所不同)能够运行,也能够返回一个值。 只要它不访问任何实例variables。 如果它尝试这个将会崩溃。
正如其他人指出的,你不能使用这个testing来查看一个对象是否被删除。 即使可以,也不行,因为在testing之后但在testing之后执行下一行之前,该对象可能被另一个线程删除。 改用线程同步。
如果this
是空的,那么你的程序中就有一个bug,很可能是你程序的devise。
我知道这是一个古老的问题,但我想我会分享我的经验与使用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; }
这段代码段错误
$ g++ -std=c++14 uniqueptr.cpp $ ./a.out Segmentation fault (core dumped)
如果我从lambda_func
删除std::cout
语句代码运行完成。
看来,这个语句f->lambda_func([f = std::move(f)] () mutable {
在成员函数调用之前处理lambda捕获。
我还补充说,通常避免null或NULL通常会更好。 我认为标准在这里再次发生变化,但现在0是真正的你想要检查,以确保你得到你想要的。
这只是一个作为函数的第一个parameter passing的指针(这正是使它成为方法的原因)。 只要你不是在谈论虚拟方法和/或虚拟inheritance,那么是的,你可以发现自己正在执行一个实例方法,一个空实例。 正如其他人所说的,在出现问题之前,你几乎肯定不会执行这样的执行,但是强大的编码应该可能会检查这种情况,并用断言。 至less,当你怀疑它可能出于某种原因而发生时是有意义的,但是需要确切地追踪它发生在哪个类/调用堆栈中。
这个== NULL可以有一个回退行为(例如一个可选的分配器委托机制,将回退到malloc /免费)。 我不确定它的标准,但是如果你永远不会调用任何虚函数,当然在该分支中的方法里面没有成员访问,那么没有真正的原因会崩溃。 否则,你有一个非常差的编译器,当它们不需要的时候使用虚函数指针,然后在严格遵守标准之前,最好更改你的编译器。
有时候这很有用,因为在极less数情况下,可读性和重构可能会胜过标准(对于所有现有的编译器来说,其显然不是未定义的行为)。
关于这篇文章:“仍然比较”这个“空指针”可能是因为文章的作者比编写MFC的微软软件团队对编译器的function有一点了解。