เมื่อใดที่ฉันควรใช้ตัวชี้ "this" อย่างชัดเจน


101

ฉันควรเขียนthis->memberวิธีการของคลาสอย่างชัดเจนเมื่อใด


17
ฉันแน่ใจว่านี่คือการหลอกลวง แต่แน่นอนว่าไม่สามารถเปิดเผยได้ ไม่ใช่ครั้งแรกฉันหวังว่าตัวชี้นี้จะเรียกว่าตัวเอง!

5
ไม่เพียงแค่นั้นฉันหวังว่ามันจะเป็นข้อมูลอ้างอิง
rlbond

2
เหมือนกัน. : | นี่คือเหตุผลโดยวิธีการ: research.att.com/~bs/bs_faq2.html#this
GManNickG

11
เห็นได้ชัดว่าวิธีนี้ไม่ได้ผลหากบุคคลนั้นไม่ทราบคำตอบ
ASk

3
@JohnH .: หืมดูเหมือนอยู่ในขณะนี้research.att.com/~bs/ stroustrup.comลิงค์ใหม่: stroustrup.com/bs_faq2.html#this
GManNickG

คำตอบ:


121

โดยปกติคุณไม่จำเป็นต้องthis->เป็นนัย

บางครั้งอาจมีความคลุมเครือของชื่อซึ่งสามารถใช้เพื่อทำให้สมาชิกคลาสและตัวแปรโลคัลไม่ชัดเจน อย่างไรก็ตามนี่เป็นกรณีที่แตกต่างอย่างสิ้นเชิงซึ่งthis->จำเป็นอย่างชัดเจน

พิจารณารหัสต่อไปนี้:

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();
}

หากคุณละเว้นthis->คอมไพเลอร์จะไม่ทราบวิธีปฏิบัติiเนื่องจากคอมไพเลอร์อาจมีหรือไม่มีอยู่ในอินสแตนซ์ทั้งหมดของA. เพื่อที่จะบอกว่าiเป็นที่แน่นอนเป็นสมาชิกของA<T>, สำหรับการใด ๆTที่this->คำนำหน้าเป็นสิ่งจำเป็น

หมายเหตุ: เป็นไปได้ที่จะยังคงละเว้นthis->คำนำหน้าโดยใช้:

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
    }

};

8
ใช้ประกาศใช้อย่างดี :)
Faisal Vali

4
นี่เป็นกรณีที่น่ารังเกียจอย่างยิ่ง ฉันเคยโดนมันกัดมาก่อน
Jason Baker

5
นี้อาจจะเป็นคำถามโง่ แต่ผมไม่เข้าใจว่าทำไมอาจจะไม่อยู่ในi Aขอตัวอย่างได้ไหม
Cam Jackson

1
@CamJackson ฉันลองใช้รหัสบน Visual Studio ผลลัพธ์จะเหมือนกันไม่ว่า "this->" จะมีอยู่จริงหรือไม่ก็ตาม ความคิดใด ๆ ?
Peng Zhang

8
@CamJackson: หนึ่งสามารถเชี่ยวชาญชั้นเรียนประเภท:template<> struct A<float> { float x; };
Macke

31

หากคุณประกาศตัวแปรโลคัลในเมธอดที่มีชื่อเดียวกับสมาชิกที่มีอยู่คุณจะต้องใช้ this-> var เพื่อเข้าถึงสมาชิกคลาสแทนตัวแปรโลคัล

#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();
}

พิมพ์:

5
4


1
ฉันจะใช้ cout ดีกว่า << A :: a << endl; แทน. `` สิ่งนี้ "ไม่สำคัญในกรณีนี้
siddhant3s

3
ฉันอยากจะหลีกเลี่ยงการปะทะกันของชื่อที่มีแบบแผนเช่น "m_a" หรือ "a_"
ทอม

19

มีสาเหตุหลายประการที่คุณอาจต้องใช้thisตัวชี้อย่างชัดเจน

  • เมื่อคุณต้องการส่งต่อการอ้างอิงไปยังวัตถุของคุณไปยังฟังก์ชันบางอย่าง
  • เมื่อมีอ็อบเจ็กต์ที่ประกาศในเครื่องที่มีชื่อเดียวกันกับอ็อบเจ็กต์สมาชิก
  • เมื่อคุณพยายามเข้าถึงสมาชิกของคลาสพื้นฐานที่พึ่งพาคลาสฐานขึ้น
  • บางคนชอบให้สัญกรณ์เพื่อทำให้การเข้าถึงของสมาชิกไม่ชัดเจนในรหัสของพวกเขา

7

แม้ว่าฉันจะไม่ค่อยชอบเป็นพิเศษ แต่ฉันเคยเห็นคนอื่นใช้สิ่งนี้ -> เพื่อขอความช่วยเหลือจาก intellisense!


6
  1. โดยตัวแปรสมาชิกจะถูกซ่อนโดยตัวแปรโลคัล
  2. หากคุณต้องการให้ชัดเจนอย่างชัดเจนว่าคุณกำลังเรียกใช้อินสแตนซ์เมธอด / ตัวแปร


มาตรฐานการเข้ารหัสบางอย่างใช้แนวทาง (2) เนื่องจากอ้างว่าทำให้อ่านรหัสได้ง่ายขึ้น

ตัวอย่าง:
สมมติว่า MyClass มีตัวแปรสมาชิกที่เรียกว่า 'count'

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

   .....
   count++;
   this->count = count;
}

6

มีบางกรณีที่this ต้องใช้การใช้งานและมีบางกรณีที่การใช้thisตัวชี้เป็นวิธีหนึ่งในการแก้ปัญหา

1) ทางเลือกที่มีจำหน่าย : เพื่อความคลุมเครือแก้ปัญหาระหว่างตัวแปรท้องถิ่นและสมาชิกชั้นแสดงตาม @ASk

2) ไม่มีทางเลือกอื่น:เพื่อส่งกลับตัวชี้หรือการอ้างอิงthisจากฟังก์ชันสมาชิก นี้จะกระทำบ่อย (และควรจะทำ) เมื่อมากoperator+, operator-, operator=ฯลฯ :

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

การทำเช่นนี้จะทำให้เกิดสำนวนที่เรียกว่า " method chaining " ซึ่งคุณสามารถดำเนินการหลายอย่างกับวัตถุในโค้ดบรรทัดเดียว เช่น:

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

บางคนคิดว่าสัญญานี้บางคนคิดว่าเป็นสิ่งที่น่ารังเกียจ นับฉันเป็นกลุ่มหลัง

3) ไม่มีทางเลือกอื่น:เพื่อแก้ไขชื่อในประเภทที่อ้างอิง สิ่งนี้เกิดขึ้นเมื่อใช้เทมเพลตดังในตัวอย่างนี้:

#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) Alternatives Available:ในฐานะที่เป็นส่วนหนึ่งของรูปแบบการเข้ารหัสเพื่อบันทึกว่าตัวแปรใดเป็นตัวแปรสมาชิกซึ่งตรงข้ามกับตัวแปรท้องถิ่น ฉันชอบรูปแบบการตั้งชื่อที่แตกต่างกันโดยที่ตัวแปรสมาชิกไม่สามารถมีชื่อเดียวกับคนในพื้นที่ได้ ตอนนี้ฉันใช้mNameสำหรับสมาชิกและnameคนในพื้นที่


5

อีกกรณีหนึ่งคือเมื่อเรียกใช้ตัวดำเนินการ เช่นแทนที่จะเป็น

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

คุณสามารถพูดได้

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

ซึ่งอาจจะอ่านได้มากขึ้น อีกตัวอย่างหนึ่งคือ copy-and-swap:

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

ฉันไม่รู้ว่าทำไมถึงไม่เขียนswap(temp)แต่ดูเหมือนจะเป็นเรื่องธรรมดา


ในกรณีสุดท้ายของคุณโปรดทราบว่าคุณสามารถเรียกใช้constฟังก์ชันที่ไม่ใช่สมาชิกได้ชั่วคราว ( Type(rhs).swap(*this);ถูกต้องตามกฎหมายและถูกต้อง) แต่ชั่วคราวไม่สามารถผูกกับพารามิเตอร์อ้างอิงที่ไม่ใช่ const ได้ (คอมไพเลอร์ปฏิเสธswap(Type(rhs));เช่นเดียวกับthis->swap(Type(rhs));)
Ben Voigt

4

คุณจะต้องใช้ this-> หากคุณมีสัญลักษณ์ที่มีชื่อเดียวกันในสองเนมสเปซที่เป็นไปได้ ยกตัวอย่าง:

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
}

ที่จุดที่น่าสนใจในโค้ดการอ้างถึง myVar จะอ้างถึง myVar ภายใน (พารามิเตอร์หรือตัวแปร) ในการเข้าถึงสมาชิกชั้นเรียนที่เรียกว่า myVar คุณต้องใช้ "this->" อย่างชัดเจน


นี่คือการใช้this->สิ่งที่ไม่สำคัญเพื่อหลีกเลี่ยง (เพียงตั้งชื่อตัวแปรท้องถิ่นให้แตกต่างกัน) thisคำตอบนี้ไม่ได้กล่าวถึงการใช้งานที่น่าสนใจจริงๆทั้งหมด
cmaster - คืนสถานะโมนิกา

4

อีกอย่างใช้สำหรับสิ่งนี้ (ตามที่ฉันคิดเมื่อฉันอ่านบทสรุปและคำถามครึ่งหนึ่ง ... หรือใช้กับตัวชี้ไปยังสมาชิก

แคสต์

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 {}

ผูกพัน

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-to-member

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)();
    }
}

หวังว่าจะช่วยแสดงการใช้งานอื่น ๆ นอกเหนือจากนี้ -> สมาชิก


3

คุณต้องใช้thisเพื่อแยกความสับสนระหว่างพารามิเตอร์ / ตัวแปรโลคัลและตัวแปรสมาชิก

class Foo
{
protected:
  int myX;

public:
  Foo(int myX)
  {
    this->myX = myX; 
  }
};

2
ไม่คุณไม่ต้องการมันคุณสามารถใช้มันได้ คุณยังสามารถใช้ชื่ออื่นสำหรับอาร์กิวเมนต์ของฟังก์ชันซึ่งมีข้อดีคือไม่มีสองเอนทิตีที่มีชื่อเดียวกัน
cmaster - คืนสถานะโมนิกา

3

จุดประสงค์หลัก (หรือฉันสามารถพูดได้คือจุดประสงค์เดียว) ของthisตัวชี้คือชี้ไปที่วัตถุที่ใช้เรียกฟังก์ชันสมาชิก

จากจุดประสงค์นี้เราอาจมีบางกรณีที่ใช้thisตัวชี้เท่านั้นที่สามารถแก้ปัญหาได้

ตัวอย่างเช่นเราต้องส่งคืนวัตถุที่เรียกใช้ในฟังก์ชันสมาชิกโดยมีอาร์กิวเมนต์เป็นวัตถุคลาสเดียวกัน:

class human {

... 

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

2

ฉันพบอีกกรณีหนึ่งที่น่าสนใจของการใช้ตัวชี้ "this" อย่างชัดเจนในหนังสือ C ++ ที่มีประสิทธิภาพ

ตัวอย่างเช่นสมมติว่าคุณมีฟังก์ชัน const เช่น

  unsigned String::length() const

คุณไม่ต้องการคำนวณความยาวของ String สำหรับการโทรแต่ละครั้งดังนั้นคุณต้องการแคชโดยทำสิ่งที่ต้องการ

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

แต่สิ่งนี้จะไม่รวบรวม - คุณกำลังเปลี่ยนวัตถุในฟังก์ชัน const

เคล็ดลับในการแก้ปัญหานี้ต้องใช้การคัดเลือกสิ่งนี้เป็นสิ่งที่ไม่ใช่เงื่อนไขนี้ :

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

จากนั้นคุณจะสามารถทำได้ในด้านบน

  nonConstThis->lengthInitialized = 1;

3
หรือคุณสามารถทำให้lengthเปลี่ยนแปลงได้หรือแม้แต่วางไว้ในโครงสร้างที่ซ้อนกัน แทบจะไม่ใช่ความคิดที่ดีเลย
Richard J.Ross III

3
กรุณาอย่า ในกรณีที่สมาชิกจะได้รับการเปลี่ยนจากฟังก์ชั่นสมาชิกที่ควรจะเป็นconst mutableมิฉะนั้นคุณจะทำให้ชีวิตซับซ้อนขึ้นสำหรับคุณเป็นผู้ดูแลคนอื่น ๆ
David Rodríguez - น้ำลายไหล
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.