ฉันควรเขียนthis->member
วิธีการของคลาสอย่างชัดเจนเมื่อใด
research.att.com/~bs/
stroustrup.com
ลิงค์ใหม่: stroustrup.com/bs_faq2.html#this
ฉันควรเขียนthis->member
วิธีการของคลาสอย่างชัดเจนเมื่อใด
research.att.com/~bs/
stroustrup.com
ลิงค์ใหม่: stroustrup.com/bs_faq2.html#this
คำตอบ:
โดยปกติคุณไม่จำเป็นต้อง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
}
};
i
A
ขอตัวอย่างได้ไหม
template<> struct A<float> { float x; };
หากคุณประกาศตัวแปรโลคัลในเมธอดที่มีชื่อเดียวกับสมาชิกที่มีอยู่คุณจะต้องใช้ 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
มีสาเหตุหลายประการที่คุณอาจต้องใช้this
ตัวชี้อย่างชัดเจน
แม้ว่าฉันจะไม่ค่อยชอบเป็นพิเศษ แต่ฉันเคยเห็นคนอื่นใช้สิ่งนี้ -> เพื่อขอความช่วยเหลือจาก intellisense!
มาตรฐานการเข้ารหัสบางอย่างใช้แนวทาง (2) เนื่องจากอ้างว่าทำให้อ่านรหัสได้ง่ายขึ้น
ตัวอย่าง:
สมมติว่า MyClass มีตัวแปรสมาชิกที่เรียกว่า 'count'
void MyClass::DoSomeStuff(void)
{
int count = 0;
.....
count++;
this->count = count;
}
มีบางกรณีที่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
คนในพื้นที่
อีกกรณีหนึ่งคือเมื่อเรียกใช้ตัวดำเนินการ เช่นแทนที่จะเป็น
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));
)
คุณจะต้องใช้ 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
คำตอบนี้ไม่ได้กล่าวถึงการใช้งานที่น่าสนใจจริงๆทั้งหมด
อีกอย่างใช้สำหรับสิ่งนี้ (ตามที่ฉันคิดเมื่อฉันอ่านบทสรุปและคำถามครึ่งหนึ่ง ... หรือใช้กับตัวชี้ไปยังสมาชิก
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;
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)();
}
}
หวังว่าจะช่วยแสดงการใช้งานอื่น ๆ นอกเหนือจากนี้ -> สมาชิก
คุณต้องใช้this
เพื่อแยกความสับสนระหว่างพารามิเตอร์ / ตัวแปรโลคัลและตัวแปรสมาชิก
class Foo
{
protected:
int myX;
public:
Foo(int myX)
{
this->myX = myX;
}
};
จุดประสงค์หลัก (หรือฉันสามารถพูดได้คือจุดประสงค์เดียว) ของthis
ตัวชี้คือชี้ไปที่วัตถุที่ใช้เรียกฟังก์ชันสมาชิก
จากจุดประสงค์นี้เราอาจมีบางกรณีที่ใช้this
ตัวชี้เท่านั้นที่สามารถแก้ปัญหาได้
ตัวอย่างเช่นเราต้องส่งคืนวัตถุที่เรียกใช้ในฟังก์ชันสมาชิกโดยมีอาร์กิวเมนต์เป็นวัตถุคลาสเดียวกัน:
class human {
...
human & human::compare(human & h){
if (condition)
return h; // argument object
else
return *this; // invoking object
}
};
ฉันพบอีกกรณีหนึ่งที่น่าสนใจของการใช้ตัวชี้ "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;
length
เปลี่ยนแปลงได้หรือแม้แต่วางไว้ในโครงสร้างที่ซ้อนกัน แทบจะไม่ใช่ความคิดที่ดีเลย
const
mutable
มิฉะนั้นคุณจะทำให้ชีวิตซับซ้อนขึ้นสำหรับคุณเป็นผู้ดูแลคนอื่น ๆ