Khi nào tôi nên viết rõ ràng this->member
trong một phương thức của một lớp?
research.att.com/~bs/
bây giờ stroustrup.com
. Liên kết mới: stroustrup.com/bs_faq2.html#this
Khi nào tôi nên viết rõ ràng this->member
trong một phương thức của một lớp?
research.att.com/~bs/
bây giờ stroustrup.com
. Liên kết mới: stroustrup.com/bs_faq2.html#this
Câu trả lời:
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ó i
thự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
}
};
i
có thể không tồn tại A
. Tôi có thể lấy một ví dụ?
template<> struct A<float> { float x; };
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
Có một số lý do tại sao bạn có thể cần phải sử dụng this
con trỏ một cách rõ ràng.
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!
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;
}
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.
const
chứ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));
)
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 this
con 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 this
từ 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 mName
cho các thành viên và name
cho người dân địa phương.
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->".
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 this
nó thậm chí còn không được đề cập đến bởi câu trả lời này.
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.
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)();
}
}
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->.
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;
}
};
Mục đích chính (hoặc tôi có thể nói, mục đích duy nhất) của this
con 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 this
con 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
}
};
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;
length
có 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.
const
chứ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.