Kapan saya harus menggunakan pointer `this` secara eksplisit?

100

Kapan saya harus menulis secara eksplisit this->memberdalam metode kelas?

dmckee --- mantan moderator anak kucing
sumber
16
Saya yakin ini adalah penipuan, tetapi tentu saja tidak dapat ditelusuri. Bukan untuk pertama kalinya, saya berharap penunjuk ini disebut diri!
5
Tidak hanya itu, saya berharap itu menjadi referensi.
rlbond
2
Sama. : | Inilah alasannya, omong-omong: research.att.com/~bs/bs_faq2.html#this
GManNickG
11
Cara ini jelas tidak akan berhasil jika orang tersebut tidak mengetahui jawabannya.
TANYAKAN
3
@JohnH .: Hm, sepertinya research.att.com/~bs/sekarang stroustrup.com. Tautan baru: stroustrup.com/bs_faq2.html#this
GManNickG

Jawaban:

120

Biasanya, Anda tidak harus, this->tersirat.

Terkadang, ada ambiguitas nama, yang dapat digunakan untuk membedakan anggota kelas dan variabel lokal. Namun, ini adalah kasus yang sama sekali berbeda this->yang secara eksplisit diperlukan.

Perhatikan kode berikut:

template<class T>
struct A {
   int i;
};

template<class T>
struct B : A<T> {

    int foo() {
        return this->i;
    }

};

int main() {
    B<int> b;
    b.foo();
}

Jika Anda mengabaikannya this->, kompilator tidak akan tahu bagaimana memperlakukannya i, karena mungkin ada atau mungkin tidak ada di semua contoh A. Untuk memberitahunya bahwa imemang anggota dari A<T>, untuk apapun T, this->prefiks diperlukan.

Catatan: Anda masih bisa menghilangkan this->prefiks dengan menggunakan:

template<class T>
struct B : A<T> {

    using A<T>::i; // explicitly refer to a variable in the base class

    int foo() {
        return i; // i is now known to exist
    }

};
Meminta
sumber
8
Penggunaan yang bagus menggunakan deklarasi :)
Faisal Vali
3
Ini adalah kasus yang sangat buruk. Saya pernah digigit olehnya sebelumnya.
Jason Baker
5
Ini mungkin pertanyaan konyol, tapi saya tidak mengerti mengapa itidak ada di A. Bisakah saya mendapatkan contoh?
Cam Jackson
1
@ CamJackson Saya mencoba kode di studio visual. hasilnya sama tidak peduli "ini->" ada atau tidak. Ada ide?
Peng Zhang
8
@ CamJackson: Seseorang dapat mengkhususkan kelas pada tipe:template<> struct A<float> { float x; };
Macke
31

Jika Anda mendeklarasikan variabel lokal dalam metode dengan nama yang sama sebagai anggota yang sudah ada, Anda harus menggunakan this-> var untuk mengakses anggota kelas alih-alih variabel lokal.

#include <iostream>
using namespace std;
class A
{
    public:
        int a;

        void f() {
            a = 4;
            int a = 5;
            cout << a << endl;
            cout << this->a << endl;
        }
};

int main()
{
    A a;
    a.f();
}

cetakan:

5
4

PaV
sumber
1
Saya akan lebih baik menggunakan cout << A :: a << endl; sebagai gantinya. `` ini "tidak penting dalam kasus ini.
siddhant3s
3
Saya lebih suka menghindari nama bentrok dengan konvensi seperti "m_a" atau "a_".
Tom
19

Ada beberapa alasan mengapa Anda mungkin perlu menggunakan thispointer secara eksplisit.

  • Saat Anda ingin meneruskan referensi ke objek Anda ke suatu fungsi.
  • Ketika ada objek yang dideklarasikan secara lokal dengan nama yang sama dengan objek anggota.
  • Saat Anda mencoba mengakses anggota kelas dasar dependen .
  • Beberapa orang lebih memilih notasi tersebut untuk membedakan akses anggota dalam kode mereka.
avakar
sumber
7

Meskipun saya biasanya tidak terlalu menyukainya, saya pernah melihat orang lain menggunakan ini-> hanya untuk mendapatkan bantuan dari Intellisense!

Brad Robinson
sumber
6
  1. Dimana variabel anggota akan disembunyikan oleh variabel lokal
  2. Jika Anda hanya ingin memperjelas bahwa Anda memanggil metode / variabel instance


Beberapa standar pengkodean menggunakan pendekatan (2) karena mereka mengklaim itu membuat kode lebih mudah dibaca.

Contoh:
Asumsikan MyClass memiliki variabel anggota yang disebut 'count'

void MyClass::DoSomeStuff(void)
{
   int count = 0;

   .....
   count++;
   this->count = count;
}
zebrabox
sumber
6

Ada beberapa kasus di mana penggunaan this harus digunakan, dan ada kasus lain di mana menggunakan thispenunjuk adalah salah satu cara untuk menyelesaikan masalah.

1) Alternatif Tersedia : Untuk menyelesaikan ambiguitas antara variabel lokal dan anggota kelas, seperti yang diilustrasikan oleh @ASk .

2) Tidak Ada Alternatif: Untuk mengembalikan pointer atau referensi ke thisdari fungsi anggota. Hal ini sering dilakukan (dan harus dilakukan) ketika overloading operator+, operator-, operator=, dll:

class Foo
{
  Foo& operator=(const Foo& rhs)
  {
    return * this;
  }
};

Melakukan ini mengizinkan idiom yang dikenal sebagai " method chaining ", di mana Anda melakukan beberapa operasi pada suatu objek dalam satu baris kode. Seperti:

Student st;
st.SetAge (21).SetGender (male).SetClass ("C++ 101");

Beberapa menganggap ini konseptual, yang lain menganggapnya sebagai kekejian. Hitung saya di kelompok terakhir.

3) Tidak Ada Alternatif: Untuk menyelesaikan nama dalam tipe dependen. Ini muncul saat menggunakan template, seperti dalam contoh ini:

#include <iostream>


template <typename Val>
class ValHolder
{
private:
  Val mVal;
public:
  ValHolder (const Val& val)
  :
    mVal (val)
  {
  }
  Val& GetVal() { return mVal; }
};

template <typename Val>
class ValProcessor
:
  public ValHolder <Val>
{
public:
  ValProcessor (const Val& val)
  :
    ValHolder <Val> (val)
  {
  }

  Val ComputeValue()
  {
//    int ret = 2 * GetVal();  // ERROR:  No member 'GetVal'
    int ret = 4 * this->GetVal();  // OK -- this tells compiler to examine dependant type (ValHolder)
    return ret;
  }
};

int main()
{
  ValProcessor <int> proc (42);
  const int val = proc.ComputeValue();
  std::cout << val << "\n";
}

4) Alternatif yang Tersedia: Sebagai bagian dari gaya pengkodean, untuk mendokumentasikan variabel mana yang merupakan variabel anggota dan bukan variabel lokal. Saya lebih suka skema penamaan yang berbeda di mana anggota varibales tidak pernah bisa memiliki nama yang sama dengan penduduk setempat. Saat ini saya menggunakan mNameuntuk anggota dan namependuduk setempat.

John Dibling
sumber
5

Satu kasus lainnya adalah saat memanggil operator. Misalnya, bukan

bool Type::operator!=(const Type& rhs)
{
    return !operator==(rhs);
}

bisa dibilang

bool Type::operator!=(const Type& rhs)
{
    return !(*this == rhs);
}

Yang mungkin lebih mudah dibaca. Contoh lainnya adalah copy-and-swap:

Type& Type::operator=(const Type& rhs)
{
    Type temp(rhs);
    temp.swap(*this);
}

Saya tidak tahu mengapa itu tidak tertulis swap(temp)tetapi ini tampaknya umum.

rlbond.dll
sumber
Dalam kasus terakhir Anda, perhatikan bahwa Anda dapat memanggil non constfungsi anggota pada sementara ( Type(rhs).swap(*this);legal dan benar) tapi kaleng sementara tidak mengikat parameter referensi non-const (menolak compiler swap(Type(rhs));serta this->swap(Type(rhs));)
Ben Voigt
4

Anda hanya perlu menggunakan this-> jika Anda memiliki simbol dengan nama yang sama di dua ruang nama potensial. Ambil contoh:

class A {
public:
   void setMyVar(int);
   void doStuff();

private:
   int myVar;
}

void A::setMyVar(int myVar)
{
  this->myVar = myVar;  // <- Interesting point in the code
}

void A::doStuff()
{
  int myVar = ::calculateSomething();
  this->myVar = myVar; // <- Interesting point in the code
}

Pada poin-poin menarik dalam kode, mengacu pada myVar akan mengacu pada myVar (parameter atau variabel) lokal. Untuk mengakses anggota kelas yang juga disebut myVar, Anda perlu menggunakan "this->" secara eksplisit.

Joe Schneider
sumber
Ini adalah salah satu penggunaan this->yang sepele untuk dihindari (berikan nama variabel lokal yang berbeda). Semua kegunaan yang sangat menarik thisbahkan tidak disebutkan oleh jawaban ini.
cmaster
4

Kegunaan lain untuk ini (seperti yang saya pikirkan ketika saya membaca ringkasan dan setengah pertanyaan ....), mengabaikan disambiguasi penamaan (buruk) di jawaban lain, adalah jika Anda ingin mentransmisikan objek saat ini, mengikatnya dalam objek fungsi atau gunakan dengan pointer-to-member.

Pemain

void Foo::bar() {
    misc_nonconst_stuff();
    const Foo* const_this = this;
    const_this->bar(); // calls const version

    dynamic_cast<Bar*>(this)->bar(); // calls specific virtual function in case of multi-inheritance
} 

void Foo::bar() const {}

Mengikat

void Foo::baz() {
     for_each(m_stuff.begin(), m_stuff.end(),  bind(&Foo:framboozle, this, _1));        
     for_each(m_stuff.begin(), m_stuff.end(), [this](StuffUnit& s) { framboozle(s); });         
} 

void Foo::framboozle(StuffUnit& su) {}

std::vector<StuffUnit> m_stuff;

ptr-ke-anggota

void Foo::boz() {
    bez(&Foo::bar);
    bez(&Foo::baz);
} 

void Foo::bez(void (Foo::*func_ptr)()) {
    for (int i=0; i<3; ++i) {
        (this->*func_ptr)();
    }
}

Semoga membantu untuk menunjukkan kegunaan lain selain ini-> anggota.

Macke
sumber
3

Anda perlu menggunakan thisuntuk membedakan antara parameter / variabel lokal dan variabel anggota.

class Foo
{
protected:
  int myX;

public:
  Foo(int myX)
  {
    this->myX = myX; 
  }
};
Brian R. Bondy
sumber
2
Tidak, Anda tidak membutuhkannya , Anda dapat menggunakannya . Anda juga dapat menggunakan nama yang berbeda untuk argumen fungsi, yang memiliki keuntungan karena tidak memiliki dua entitas dengan nama yang sama.
cmaster
3

Tujuan utama (atau saya dapat mengatakan, satu-satunya) tujuan dari thispointer adalah menunjuk ke objek yang digunakan untuk memanggil fungsi anggota.

Berdasarkan tujuan ini, kita dapat memiliki beberapa kasus yang hanya menggunakan thispenunjuk yang dapat menyelesaikan masalah.

Misalnya, kita harus mengembalikan objek pemanggilan dalam fungsi anggota dengan argumen objek kelas yang sama:

class human {

... 

human & human::compare(human & h){
    if (condition)
        return h;       // argument object
    else 
        return *this;   // invoking object
    }
};
Trevor
sumber
2

Saya menemukan kasus menarik lainnya dari penggunaan eksplisit pointer "ini" dalam buku Effective C ++.

Misalnya, Anda memiliki fungsi seperti konstanta

  unsigned String::length() const

Anda tidak ingin menghitung panjang String untuk setiap panggilan, karena itu Anda ingin men-cache-nya dengan melakukan sesuatu seperti

  unsigned String::length() const
  {
    if(!lengthInitialized)
    {
      length = strlen(data);
      lengthInitialized = 1;
    }
  }

Tapi ini tidak bisa dikompilasi - Anda mengubah objek dalam fungsi const.

Trik untuk mengatasi ini membutuhkan casting ini ke non-const ini :

  String* const nonConstThis = (String* const) this;

Kemudian, Anda dapat melakukannya di atas

  nonConstThis->lengthInitialized = 1;
Ariel
sumber
3
Atau Anda bisa membuat lengthmutable, atau bahkan meletakkannya di struct bersarang. Menghilangkan keteguhan hampir tidak pernah merupakan ide yang bagus.
Richard J. Ross III
3
Tolong jangan. Jika anggota akan diubah dari constfungsi anggota, itu harus mutable. Jika tidak, Anda membuat hidup lebih rumit untuk Anda sebagai pengelola lainnya.
David Rodríguez - dribeas