Khi nào tôi nên sử dụng rõ ràng con trỏ `this`?


97

Khi nào tôi nên viết rõ ràng this->membertrong một phương thức của một lớp?


16
Tôi chắc rằng đây là một bản dupe, nhưng tất nhiên là không thể tìm được. Không phải lần đầu tiên, tôi ước con trỏ này được gọi là chính mình!

5
Không chỉ vậy, tôi ước nó là một tài liệu tham khảo.
rlbond

2
Tương tự. : | Nhân tiện, đây là lý do tại sao: research.att.com/~bs/bs_faq2.html#this
GManNickG

11
Phương pháp này rõ ràng là không hiệu quả nếu người đó không biết câu trả lời.
HỎI

3
@JohnH: Hm, có vẻ như research.att.com/~bs/bây giờ stroustrup.com. Liên kết mới: stroustrup.com/bs_faq2.html#this
GManNickG

Câu trả lời:


118

Thông thường, bạn không cần phải làm như vậy this->.

Đôi khi, có sự mơ hồ về tên, nơi nó có thể được sử dụng để phân biệt các thành viên lớp và các biến cục bộ. Tuy nhiên, đây là một trường hợp hoàn toàn khác khi this->được yêu cầu rõ ràng.

Hãy xem xét đoạn mã sau:

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

Nếu bạn bỏ qua this->, trình biên dịch không biết cách xử lý i, vì nó có thể tồn tại hoặc không tồn tại trong tất cả các bản khởi tạo của A. Để cho biết nó ithực sự là thành viên của A<T>, đối với bất kỳ T, this->tiền tố là bắt buộc.

Lưu ý: vẫn có thể bỏ qua this->tiền tố bằng cách sử dụng:

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
Thoải mái sử dụng của việc sử dụng khai :)
Faisal Vali

3
Đây là một trường hợp đặc biệt khó chịu. Tôi đã bị nó cắn trước đây.
Jason Baker

5
Đây có thể là một câu hỏi ngớ ngẩn, nhưng tôi không hiểu tại sao icó thể không tồn tại A. Tôi có thể lấy một ví dụ?
Cam Jackson

1
@CamJackson Tôi đã thử mã trên studio trực quan. kết quả giống nhau cho dù "this->" tồn tại hay không. Bất kỳ ý tưởng?
Peng Zhang

8
@CamJackson: Người ta có thể chuyên biệt hóa các lớp theo loại:template<> struct A<float> { float x; };
Macke

31

Nếu bạn khai báo một biến cục bộ trong một phương thức có cùng tên với một thành viên hiện có, bạn sẽ phải sử dụng this-> var để truy cập thành viên lớp thay vì biến cục bộ.

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

bản in:

5
4


1
Tôi nên sử dụng cout << A :: a << endl; thay thế. '' này" là không quan trọng trong trường hợp này.
siddhant3s

3
Tôi chỉ muốn tránh xung đột tên với các quy ước như "m_a" hoặc "a_".
Tom,

19

Có một số lý do tại sao bạn có thể cần phải sử dụng thiscon trỏ một cách rõ ràng.

  • Khi bạn muốn chuyển một tham chiếu đến đối tượng của mình cho một số hàm.
  • Khi có một đối tượng được khai báo cục bộ trùng tên với đối tượng thành viên.
  • Khi bạn đang cố gắng truy cập các thành viên của các lớp cơ sở phụ thuộc .
  • Một số người thích ký hiệu để phân biệt trực quan quyền truy cập của thành viên trong mã của họ.

7

Mặc dù tôi thường không đặc biệt thích nó, nhưng tôi đã thấy những người khác sử dụng điều này-> chỉ đơn giản là để nhận trợ giúp từ intellisense!


6
  1. Trường hợp biến thành viên sẽ bị ẩn bởi một biến cục bộ
  2. Nếu bạn chỉ muốn làm rõ ràng rằng bạn đang gọi một phương thức / biến thể hiện


Một số tiêu chuẩn mã hóa sử dụng cách tiếp cận (2) vì họ cho rằng nó làm cho mã dễ đọc hơn.

Ví dụ:
Giả sử MyClass có một biến thành viên được gọi là 'count'

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

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

5

Một trường hợp khác là khi gọi các toán tử. Vd: thay vì

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

bạn có thể nói

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

Cái nào có thể dễ đọc hơn. Một ví dụ khác là copy-and-swap:

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

Tôi không biết tại sao nó không được viết swap(temp)nhưng điều này dường như là phổ biến.


Trong trường hợp cuối cùng của bạn, lưu ý rằng bạn có thể gọi một phi constchức năng thành viên trên tạm thời ( Type(rhs).swap(*this);là hợp pháp và chính xác) nhưng một lon tạm thời không liên kết với một tham số tham chiếu không const (Rejects trình biên dịch swap(Type(rhs));cũng như this->swap(Type(rhs));)
Ben Voigt

5

Có một số trường hợp this phải sử dụng việc sử dụng, và có những trường hợp khác sử dụng thiscon trỏ là một cách để giải quyết vấn đề.

1) Có sẵn các giải pháp thay thế : Để giải quyết sự không rõ ràng giữa các biến cục bộ và các thành viên lớp, như được minh họa bởi @ASk .

2) Không thay thế: Để trả về một con trỏ hoặc tham chiếu đến thistừ một hàm thành viên. Này thường được thực hiện (và nên được thực hiện) khi quá tải operator+, operator-, operator=, vv:

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

Làm điều này cho phép một thành ngữ được gọi là " chuỗi phương thức ", nơi bạn thực hiện một số thao tác trên một đối tượng trong một dòng mã. Nhu la:

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

Một số coi đây là khuyết điểm, những người khác coi đó là một điều ghê tởm. Hãy đếm tôi trong nhóm sau.

3) Không có thay thế: Để giải quyết các tên trong các loại phụ thuộc. Điều này xuất hiện khi sử dụng các mẫu, như trong ví dụ sau:

#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) Có sẵn các giải pháp thay thế: Là một phần của phong cách mã hóa, để ghi lại biến nào là biến thành viên thay vì biến cục bộ. Tôi thích cách đặt tên khác mà các thành viên không bao giờ có thể có cùng tên với người dân địa phương. Hiện tại tôi đang sử dụng mNamecho các thành viên và namecho người dân địa phương.


4

Bạn chỉ phải sử dụng this-> nếu bạn có một ký hiệu trùng tên trong hai không gian tên tiềm năng. Lấy ví dụ:

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
}

Tại các điểm thú vị trong mã, tham chiếu đến myVar sẽ tham chiếu đến myVar (tham số hoặc biến) cục bộ. Để truy cập thành viên lớp còn được gọi là myVar, bạn cần sử dụng rõ ràng "this->".


Đây là một trong những cách sử dụng this->tầm thường cần tránh (chỉ cần đặt tên khác cho biến cục bộ). Tất cả những công dụng thực sự thú vị của thisnó thậm chí còn không được đề cập đến bởi câu trả lời này.
cmaster - Khôi phục monica

4

Các cách sử dụng khác cho điều này (như tôi nghĩ khi tôi đọc phần tóm tắt và một nửa câu hỏi ....), Bỏ qua sự phân biệt đặt tên (xấu) trong các câu trả lời khác, là nếu bạn muốn ép kiểu đối tượng hiện tại, hãy ràng buộc nó trong một đối tượng hàm. hoặc sử dụng nó với một con trỏ đến thành viên.

Phôi

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

Ràng buộc

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-thành viên

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

Hy vọng nó sẽ giúp hiển thị các công dụng khác của cái này ngoài thành viên này->.


3

Bạn cần sử dụng thisđể phân biệt giữa một tham số / biến cục bộ và biến thành viên.

class Foo
{
protected:
  int myX;

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

2
Không, bạn không cần nó, bạn có thể sử dụng nó. Bạn cũng có thể sử dụng một tên khác cho đối số hàm, điều này có lợi thế là không có hai thực thể trùng tên.
cmaster - Khôi phục monica

3

Mục đích chính (hoặc tôi có thể nói, mục đích duy nhất) của thiscon trỏ là nó trỏ đến đối tượng được sử dụng để gọi một hàm thành viên.

Dựa trên mục đích này, chúng ta có thể có một số trường hợp chỉ sử dụng thiscon trỏ mới có thể giải quyết được vấn đề.

Ví dụ, chúng ta phải trả về đối tượng đang gọi trong một hàm thành viên với đối số là một đối tượng cùng lớp:

class human {

... 

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

2

Tôi đã tìm thấy một trường hợp thú vị khác về cách sử dụng con trỏ "this" rõ ràng trong cuốn sách C ++ hiệu quả.

Ví dụ: giả sử bạn có một hàm const như

  unsigned String::length() const

Bạn không muốn tính toán độ dài của Chuỗi cho mỗi cuộc gọi, do đó bạn muốn lưu vào bộ nhớ cache của nó bằng cách làm như

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

Nhưng điều này sẽ không biên dịch - bạn đang thay đổi đối tượng trong một hàm const.

Thủ thuật để giải quyết vấn đề này yêu cầu truyền dữ liệu này thành một không const sau :

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

Sau đó, bạn sẽ có thể làm ở trên

  nonConstThis->lengthInitialized = 1;

3
Hoặc bạn có thể làm cho nó lengthcó thể thay đổi, hoặc thậm chí đặt nó trong một cấu trúc lồng nhau. Loại bỏ hằng số hầu như không bao giờ là một ý tưởng hay.
Richard J. Ross III

3
Xin đừng. Nếu thành viên được thay đổi từ các constchức năng của thành viên, thì nó phải như vậy mutable. Nếu không, bạn đang làm cho cuộc sống của bạn trở nên phức tạp hơn với một người bảo trì khác.
David Rodríguez - dribeas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.