サーチ…
備考
this
ポインタは、これを実装するために必要なライブラリがないため、C ++のキーワードです。 this
がポインタであることを忘れないでください!そうすることはできません:
this.someMember();
矢印記号->
を使用して、ポインタからメンバー関数またはメンバー変数にアクセスすると:
this->someMember();
this
ポインタの理解を深めるための他の役に立つリンク:
http://www.geeksforgeeks.org/this-pointer-in-c/
https://www.tutorialspoint.com/cplusplus/cpp_this_pointer.htm
このポインタ
すべての非静的メンバ関数には、 this
名前のクラスのインスタンスへのポインタである隠しパラメータがあります。このパラメータは静かにパラメータリストの先頭に挿入され、コンパイラによって完全に処理されます。クラスのメンバがメンバ関数の中でアクセスされると、それを通して静かにアクセスされthis
。コンパイラはすべてのインスタンスに対して単一の非静的メンバ関数を使用でき、メンバ関数は他のメンバ関数を多相的に呼び出すことができます。
struct ThisPointer {
int i;
ThisPointer(int ii);
virtual void func();
int get_i() const;
void set_i(int ii);
};
ThisPointer::ThisPointer(int ii) : i(ii) {}
// Compiler rewrites as:
ThisPointer::ThisPointer(int ii) : this->i(ii) {}
// Constructor is responsible for turning allocated memory into 'this'.
// As the constructor is responsible for creating the object, 'this' will not be "fully"
// valid until the instance is fully constructed.
/* virtual */ void ThisPointer::func() {
if (some_external_condition) {
set_i(182);
} else {
i = 218;
}
}
// Compiler rewrites as:
/* virtual */ void ThisPointer::func(ThisPointer* this) {
if (some_external_condition) {
this->set_i(182);
} else {
this->i = 218;
}
}
int ThisPointer::get_i() const { return i; }
// Compiler rewrites as:
int ThisPointer::get_i(const ThisPointer* this) { return this->i; }
void ThisPointer::set_i(int ii) { i = ii; }
// Compiler rewrites as:
void ThisPointer::set_i(ThisPointer* this, int ii) { this->i = ii; }
コンストラクタでは、 this
安全に親クラスですでに初期化されている任意のフィールド、または任意のフィールドにアクセスする(暗黙的または明示的)に使用することができます。逆に、まだ初期化されていないフィールドや派生クラスのフィールドには(暗黙的にまたは明示的に)安全ではありません(派生クラスがまだ構築されていないため、フィールドも初期化も既存もありません)。それを介して仮想メンバ関数を呼び出すことも危険でありthis
いずれかの派生クラスの関数が考慮されないように、コンストラクタに(これはまだ構築されていない派生クラスに、したがってそのコンストラクタはまだ仮想テーブルを更新しません)。
また、コンストラクタでは、オブジェクトの型はそのコンストラクタが構築する型です。これは、オブジェクトが派生型として宣言されていても当てはまります。例えば、以下の例では、 ctd_good
とctd_bad
型でCtorThisBase
内部CtorThisBase()
およびタイプCtorThis
内部CtorThis()
、それらの標準型であっても、 CtorThisDerived
。より派生したクラスが基本クラスの周りに構築されると、インスタンスは、意図された型の完全に構築されたインスタンスになるまで、徐々にクラス階層を通過します。
class CtorThisBase {
short s;
public:
CtorThisBase() : s(516) {}
};
class CtorThis : public CtorThisBase {
int i, j, k;
public:
// Good constructor.
CtorThis() : i(s + 42), j(this->i), k(j) {}
// Bad constructor.
CtorThis(int ii) : i(ii), j(this->k), k(b ? 51 : -51) {
virt_func();
}
virtual void virt_func() { i += 2; }
};
class CtorThisDerived : public CtorThis {
bool b;
public:
CtorThisDerived() : b(true) {}
CtorThisDerived(int ii) : CtorThis(ii), b(false) {}
void virt_func() override { k += (2 * i); }
};
// ...
CtorThisDerived ctd_good;
CtorThisDerived ctd_bad(3);
これらのクラスとメンバ関数を使用すると:
- 良いコンストラクタでは、
ctd_good
ために:-
CtorThisBase
は、CtorThis
コンストラクターが入力されるまでに完全に構築されます。したがって、s
初期化しつつ、有効な状態であるi
、したがってアクセスすることができます。 -
i
がj(this->i)
に達する前に初期化されます。したがって、i
はj
を初期化している間は有効な状態にあり、アクセスすることができます。 -
j
はk(j)
に達する前に初期化される。したがって、j
はk
を初期化している間有効な状態にあり、従ってアクセスすることができる。
-
- 不正なコンストラクタでは、
ctd_bad
:-
k
はj(this->k)
に達した後に初期化される。したがって、j
を初期化している間、k
は無効な状態にあり、それにアクセスすると未定義の動作が発生します。 -
CtorThisDerived
は、CtorThis
が構築されるまで構築されません。したがって、b
は無効な状態にあり、k
を初期化し、それにアクセスすると未定義の動作が発生します。 - オブジェクト
ctd_bad
まだあるCtorThis
それが去るまでCtorThis()
および使用するように更新されることはありませんCtorThisDerived
までのvtableのをCtorThisDerived()
したがって、virt_func()
はそれを呼び出すか、CtorThisDerived::virt_func()
を呼び出すかにかかわらず、CtorThis::virt_func()
を呼び出します。
-
このポインタを使用してメンバーデータにアクセスする
このコンテキストでは、 this
ポインタの使用は完全には必要ではありませんが、指定された関数または変数がクラスのメンバーであることを示すことによって、コードを読み取り者に明瞭にします。この状況の例:
// Example for this pointer
#include <iostream>
#include <string>
using std::cout;
using std::endl;
class Class
{
public:
Class();
~Class();
int getPrivateNumber () const;
private:
int private_number = 42;
};
Class::Class(){}
Class::~Class(){}
int Class::getPrivateNumber() const
{
return this->private_number;
}
int main()
{
Class class_example;
cout << class_example.getPrivateNumber() << endl;
}
このポインタを使用してメンバーデータとパラメータを区別する
これはメンバーデータをパラメータと区別するための実際の便利な戦略です...この例を考えてみましょう:
// Dog Class Example
#include <iostream>
#include <string>
using std::cout;
using std::endl;
/*
* @class Dog
* @member name
* Dog's name
* @function bark
* Dog Barks!
* @function getName
* To Get Private
* Name Variable
*/
class Dog
{
public:
Dog(std::string name);
~Dog();
void bark() const;
std::string getName() const;
private:
std::string name;
};
Dog::Dog(std::string name)
{
/*
* this->name is the
* name variable from
* the class dog . and
* name is from the
* parameter of the function
*/
this->name = name;
}
Dog::~Dog(){}
void Dog::bark() const
{
cout << "BARK" << endl;
}
std::string Dog::getName() const
{
return this->name;
}
int main()
{
Dog dog("Max");
cout << dog.getName() << endl;
dog.bark();
}
ここでは、コンストラクタで以下を実行します:
this->name = name;
ここでは、クラスDog(this-> name)のプライベート変数の名前にパラメータ名を代入していることがわかります。
上記のコードの出力を確認するには: http : //cpp.sh/75r7
このポインタCV-修飾子
this
は他のポインタと同じcv修飾されたものでもあります。ただし、 this
パラメータがパラメータリストにリストされていないため、 this
ために特別な構文が必要です。 cv修飾子は、パラメータリストの後、関数の本体の前にリストされます。
struct ThisCVQ {
void no_qualifier() {} // "this" is: ThisCVQ*
void c_qualifier() const {} // "this" is: const ThisCVQ*
void v_qualifier() volatile {} // "this" is: volatile ThisCVQ*
void cv_qualifier() const volatile {} // "this" is: const volatile ThisCVQ*
};
this
はパラメータなので、 this
cv-qualifierに基づいて関数をオーバーロードすることができます 。
struct CVOverload {
int func() { return 3; }
int func() const { return 33; }
int func() volatile { return 333; }
int func() const volatile { return 3333; }
};
this
がconst
( const volatile
を含む)の場合、暗黙的または明示的に関わらず、関数はメンバー変数に書き込むことができません。唯一の例外はmutable
メンバ変数であり、定数にかかわらず記述できます。このため、 const
は、物理的な状態(オブジェクトがフードの下でどのように見えるか)を変更しても、メンバ関数がオブジェクトの論理状態(オブジェクトが外界に現れる方法)を変更しないことを示すために使用されます)。
論理状態は、オブジェクトがオブザーバーの外に出現する方法です。それは物理的状態に直接結びついておらず、実際には物理的状態として保存されていないかもしれません。外部の観察者が変更を見ることができない限り、オブジェクト内のすべてのビットを反転しても、論理状態は一定です。
ビット単位の状態とも呼ばれる物理状態は、オブジェクトがメモリにどのように格納されるかです。これは、データを構成する生の1秒と0秒のオブジェクトです。オブジェクトは、メモリ内の表現が決して変化しない場合、物理的にのみ一定です。
C ++塩基ことに注意してくださいconst
論理的な状態ではなく、物理的な状態にネス。
class DoSomethingComplexAndOrExpensive {
mutable ResultType cached_result;
mutable bool state_changed;
ResultType calculate_result();
void modify_somehow(const Param& p);
// ...
public:
DoSomethingComplexAndOrExpensive(Param p) : state_changed(true) {
modify_somehow(p);
}
void change_state(Param p) {
modify_somehow(p);
state_changed = true;
}
// Return some complex and/or expensive-to-calculate result.
// As this has no reason to modify logical state, it is marked as "const".
ResultType get_result() const;
};
ResultType DoSomethingComplexAndOrExpensive::get_result() const {
// cached_result and state_changed can be modified, even with a const "this" pointer.
// Even though the function doesn't modify logical state, it does modify physical state
// by caching the result, so it doesn't need to be recalculated every time the function
// is called. This is indicated by cached_result and state_changed being mutable.
if (state_changed) {
cached_result = calculate_result();
state_changed = false;
}
return cached_result;
}
技術的には this
をconst_cast
- const_cast
で使用することができますが、 本当に本当に本当に本当に本当のはずですが、代わりにmutable
を使うべきです。 const_cast
は、実際に const
であるオブジェクトに対して使用されるときは未定義の動作を呼び出すのに対し、 mutable
は安全に使用するように設計されています。しかし、非常に古いコードでこれを実行する可能性があります。
class CVAccessor {
int arr[5];
public:
const int& get_arr_element(size_t i) const { return arr[i]; }
int& get_arr_element(size_t i) {
return const_cast<int&>(const_cast<const CVAccessor*>(this)->get_arr_element(i));
}
};
これにより、不要なコードの重複が防止されます。
通常のポインタと同様に、 this
がvolatile
( const volatile
を含む)の場合、キャッシュされずにアクセスされるたびにメモリからロードされます。これは、他のポインタがvolatile
と宣言するのと同じように最適化に影響しますので、注意が必要です。
インスタンスがcv修飾されている場合、アクセスが許可されるメンバ関数は、 this
ポインタが少なくともインスタンス自体のcv修飾されたメンバ関数だけであることに注意してください。
- 非cvインスタンスは、任意のメンバ関数にアクセスできます。
-
const
インスタンスは、const
およびconst volatile
関数にアクセスできます。 -
volatile
インスタンスはvolatile
およびconst volatile
関数にアクセスできます。 -
const volatile
インスタンスはconst volatile
関数にアクセスできます。
これはconst
正確さの重要な教義の1つです。
struct CVAccess {
void func() {}
void func_c() const {}
void func_v() volatile {}
void func_cv() const volatile {}
};
CVAccess cva;
cva.func(); // Good.
cva.func_c(); // Good.
cva.func_v(); // Good.
cva.func_cv(); // Good.
const CVAccess c_cva;
c_cva.func(); // Error.
c_cva.func_c(); // Good.
c_cva.func_v(); // Error.
c_cva.func_cv(); // Good.
volatile CVAccess v_cva;
v_cva.func(); // Error.
v_cva.func_c(); // Error.
v_cva.func_v(); // Good.
v_cva.func_cv(); // Good.
const volatile CVAccess cv_cva;
cv_cva.func(); // Error.
cv_cva.func_c(); // Error.
cv_cva.func_v(); // Error.
cv_cva.func_cv(); // Good.
このポインタ参照修飾子
this
cv修飾子と同様に、 ref修飾子を*this
も適用できます。 REF-修飾子はコンパイラがコピーのいずれかを使用するか、より適切であるかに応じてセマンティクスを移動できるように、通常と右辺値参照セマンティクスの間で選択するために使用され、そしてするために適用される*this
代わりにthis
。
参照構文を使用するref-qualifierにもかかわらず、 this
自体は依然としてポインタであることに注意してください。また、ref修飾子は*this
型を実際に変更しないことに注意してください。彼らのように見て、その効果を説明して理解するほうが簡単です。
struct RefQualifiers {
std::string s;
RefQualifiers(const std::string& ss = "The nameless one.") : s(ss) {}
// Normal version.
void func() & { std::cout << "Accessed on normal instance " << s << std::endl; }
// Rvalue version.
void func() && { std::cout << "Accessed on temporary instance " << s << std::endl; }
const std::string& still_a_pointer() & { return this->s; }
const std::string& still_a_pointer() && { this->s = "Bob"; return this->s; }
};
// ...
RefQualifiers rf("Fred");
rf.func(); // Output: Accessed on normal instance Fred
RefQualifiers{}.func(); // Output: Accessed on temporary instance The nameless one
メンバ関数はref-qualifierの有無にかかわらず多重定義を持つことはできません。プログラマはどちらかを選択する必要があります。ありがたいことに、cv修飾子はref修飾子と組み合わせて使用することができ、 const
正当性規則を守ることができます。
struct RefCV {
void func() & {}
void func() && {}
void func() const& {}
void func() const&& {}
void func() volatile& {}
void func() volatile&& {}
void func() const volatile& {}
void func() const volatile&& {}
};