Коли я повинен явно писати 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; };
Якщо ви оголосите локальну змінну в методі з тим самим іменем, що і існуючий член, вам доведеться використовувати цей-> 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
явно використовувати покажчик.
Хоча зазвичай мені це не подобається, я бачив, як інші використовують це-> просто, щоб отримати допомогу від інтелігенції!
Деякі стандарти кодування використовують підхід (2), оскільки вони стверджують, що це полегшує читання коду.
Приклад:
Припустимо, що MyClass має змінну члена під назвою 'count'
void MyClass::DoSomeStuff(void)
{
int count = 0;
.....
count++;
this->count = count;
}
Інший випадок - це виклик операторів. Наприклад, замість
bool Type::operator!=(const Type& rhs)
{
return !operator==(rhs);
}
ви можете сказати
bool Type::operator!=(const Type& rhs)
{
return !(*this == rhs);
}
Що може бути більш читабельним. Інший приклад - копіювання та обмін:
Type& Type::operator=(const Type& rhs)
{
Type temp(rhs);
temp.swap(*this);
}
Я не знаю, чому це не написано, swap(temp)
але це, здається, часто.
const
функцію члена на тимчасовому ( Type(rhs).swap(*this);
законно і правильно) , але тимчасово не може зв'язуватися з неконстантним опорним параметром (компілятор шлюбу swap(Type(rhs));
, а також this->swap(Type(rhs));
)
Мало випадків, коли this
потрібно використовувати, і є інші, коли використання this
вказівника є одним із способів вирішення проблеми.
1) Доступні альтернативи : Для вирішення двозначності між локальними змінними та членами класу, як показано @ASk .
2) Немає альтернативи: повернення покажчика або посилання на this
функцію-члена. Це часто робиться (і повинно бути зроблено) при перевантаженні operator+
, operator-
, operator=
і т.д .:
class Foo
{
Foo& operator=(const Foo& rhs)
{
return * this;
}
};
Це дозволяє ідіому, відому як " ланцюжок методів ", коли ви виконуєте кілька операцій над об’єктом в одному рядку коду. Як от:
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) Доступні альтернативи: В рамках стилю кодування потрібно документувати, які змінні є змінними-членами на відміну від локальних змінних. Я віддаю перевагу іншій схемі іменування, коли варіації членів ніколи не можуть мати те саме ім’я, як місцеві жителі. В даний час я використовую mName
для членів та name
для місцевих жителів.
Це потрібно використовувати лише тоді, якщо у вас є символ з тим самим іменем у двох потенційних просторах імен. Візьмемо для прикладу:
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
}
};
Я знайшов ще один цікавий випадок явного використання вказівника "цей" в книзі "Ефективний C ++".
Наприклад, скажімо, у вас функція const, як
unsigned String::length() const
Ви не хочете обчислювати довжину рядка для кожного дзвінка, отже, ви хочете кешувати його, роблячи щось подібне
unsigned String::length() const
{
if(!lengthInitialized)
{
length = strlen(data);
lengthInitialized = 1;
}
}
Але це не складеться - ви змінюєте об'єкт у функції const.
Трюк, щоб вирішити це, вимагає відкинути це до неконструктивного цього :
String* const nonConstThis = (String* const) this;
Тоді ви зможете зробити вище
nonConstThis->lengthInitialized = 1;
length
змінний або навіть помістити його в вкладені структури. Відганяти незграбність майже ніколи не є хорошою ідеєю.
const
функцій члена, він повинен бути mutable
. Інакше ви ускладнюєте життя іншим обслуговуючим особам.