thisがnullかどうかをチェックするのは意味がありますか?
メソッドを持つクラスがあるとします。そのメソッド内で、this == NULL
、もしそうなら、エラーコードを返します。
thisがnullの場合、それはオブジェクトが削除されることを意味します。メソッドは何かを返すことさえできますか?
更新:メソッドは複数のスレッドから呼び出すことができ、別のスレッドがメソッド内にある間にオブジェクトが削除される可能性があることを忘れていました。
This == nullをチェックする意味がありますか?コードレビューをしているときにこれを見つけました。
標準のC++では、nullポインターの呼び出しは既に未定義の動作であるため、このようなチェックは行われません。したがって、このようなチェックに依存するコードは非標準です(チェックが実行される保証さえありません)。
これは、非仮想関数にも当てはまることに注意してください。
ただし、一部の実装では_this==0
_が許可されているため、これらの実装専用に作成されたライブラリは、ハックとして使用する場合があります。このようなペアの良い例はVC++とMFCです。正確なコードは思い出せませんが、MFCソースコードでif (this == NULL)
チェックがどこかにあるのをはっきり覚えています。
過去のある時点で、呼び出し元のミスによりこのコードが_this==0
_でヒットしたため、デバッグの補助としても使用できます。そのため、その将来のインスタンスをキャッチするためのチェックが挿入されました。ただし、そのようなことを主張する方が理にかなっています。
この== nullの場合、オブジェクトが削除されていることを意味します。
いいえ、それはそれを意味しません。これは、nullポインター、またはnullポインターから取得した参照でメソッドが呼び出されたことを意味します(ただし、そのような参照の取得はすでにU.B.です)。これはdelete
とは関係がなく、このタイプのオブジェクトが存在する必要はありません。
スレッドについてのメモは気になります。クラッシュにつながる可能性のある競合状態があると確信しています。スレッドがオブジェクトを削除し、ポインターをゼロにすると、別のスレッドがこれらの2つの操作の間でそのポインターを介して呼び出しを行い、this
がnullでなく無効になり、クラッシュする可能性があります。同様に、別のスレッドがオブジェクトの作成中にスレッドがメソッドを呼び出すと、クラッシュする場合があります。
簡単に言えば、この変数へのアクセスを同期させるには、ミューテックスなどを使用する必要があります。 this
がnever nullであるか、問題が発生することを確認する必要があります。
これは古いことは知っていますが、誰かがラムダに言及する必要があるC++ 11-17を扱っているように感じます。 これを後の時点で非同期的に呼び出されるラムダにキャプチャする場合、そのラムダが呼び出される前に「this」オブジェクトが破棄される可能性があります。
つまり、別のスレッドから実行されるか、一般に非同期で実行される時間のかかる関数へのコールバックとして渡す
編集:明確にするために、質問は「これがnullであるかどうかを確認するのは理にかなっています」というものでした。
不自然な例:このコードは完全に実行可能です。安全でない動作を確認するには、安全な動作の呼び出しをコメントアウトし、安全でない動作の呼び出しのコメントを外します。
#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;
}
FWIW、私はアサーションで(this != NULL)
のデバッグチェックを使用したことがありますが、その前に欠陥のあるコードをキャッチできました。コードがクラッシュせずに必ずしも行き過ぎたというわけではありませんが、メモリ保護のない小さな組み込みシステムでは、アサーションが実際に役立ちました。
メモリ保護を備えたシステムでは、NULL this
ポインターを使用してOSを呼び出すと、一般にアクセス違反が発生するため、this != NULL
をアサートする値は小さくなります。ただし、保護されたシステムでも必ずしも価値がないわけではない理由については、Pavelのコメントを参照してください。
これは古い質問ですが、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)
_
_std::cout
_から_lambda_func
_ステートメントを削除すると、コードは最後まで実行されます。
このステートメントは、メンバー関数が呼び出される前にラムダキャプチャを処理するf->lambda_func([f = std::move(f)] () mutable {
のようです。
あなたのメソッドはほとんどの場合(コンパイラによって異なる可能性があります)実行でき、値を返すこともできます。インスタンス変数にアクセスしない限り。これを試みるとクラッシュします。
他の人が指摘したように、このテストを使用してオブジェクトが削除されたかどうかを確認することはできません。たとえできたとしても、テストの直後であるがテストの次の行を実行する前に別のスレッドによってオブジェクトが削除される可能性があるため、機能しません。代わりにスレッド同期を使用してください。
this
がnullの場合、プログラムにバグがあります。おそらくプログラムの設計にあります。