Pav*_*aev 75
检查这个== null是否有意义?我在进行代码审查时发现了这一点.
在标准C++中,它没有,因为对空指针的任何调用都是未定义的行为,因此依赖于此类检查的任何代码都是非标准的(不能保证甚至会执行检查).
请注意,这也适用于非虚函数.
this==0但是,某些实现允许,因此专门为这些实现编写的库有时会将其用作黑客.这样一对的一个很好的例子是VC++和MFC - 我不记得确切的代码,但我清楚地记得if (this == NULL)在某处看到MFC源代码中的检查.
它也可能作为调试辅助工具,因为在过去的某些时候,this==0由于调用者的错误而导致此代码被命中,因此插入检查以捕获其未来的实例.但是,对于这样的事情,断言会更有意义.
如果这= = null则表示该对象已被删除.
不,这并不意味着.这意味着在空指针或从空指针获得的引用上调用方法(尽管获得这样的引用已经是UB).这与此无关delete,并且不需要任何此类对象存在.
Tim*_*ter 27
你对线程的说明令人担忧.我很确定你的比赛条件会导致车祸.如果一个线程删除一个对象并将指针归零,则另一个线程可以在这两个操作之间通过该指针进行调用,从而导致this非空且无效,从而导致崩溃.类似地,如果一个线程调用一个方法,而另一个线程正在创建该对象,你也可能会崩溃.
简而言之,您确实需要使用互斥锁或其他东西来同步访问此变量.你需要确保this是从来没有空,否则你会遇到问题.
FWIW,我(this != NULL)在断言中使用了调试检查,之前有助于捕获有缺陷的代码.并不是说代码在崩溃时必然会变得太过分,但在没有内存保护的小型嵌入式系统上,断言实际上有所帮助.
在具有内存保护的系统上,如果使用NULL this指针调用操作系统通常会遇到访问冲突,因此断言的值较小 this != NULL.但是,请参阅Pavel的评论,为什么即使受保护的系统也不一定无价值.
我知道这已经过时了,但我觉得现在我们正在处理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)