Когда я должен явно писать 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
необходимо использовать using , а есть другие, где использование 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
для местных жителей.
Еще один случай - вызов операторов. Например, вместо
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-> только в том случае, если у вас есть символ с тем же именем в двух потенциальных пространствах имен. Например:
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-> member.
Вам нужно использовать 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 ++».
Например, скажем, у вас есть константная функция вроде
unsigned String::length() const
Вы не хотите рассчитывать длину строки для каждого вызова, поэтому вы хотите кэшировать ее, выполняя что-то вроде
unsigned String::length() const
{
if(!lengthInitialized)
{
length = strlen(data);
lengthInitialized = 1;
}
}
Но это не скомпилируется - вы меняете объект в константной функции.
Чтобы решить эту проблему, нужно преобразовать this в неконстантный this :
String* const nonConstThis = (String* const) this;
Затем вы сможете сделать это
nonConstThis->lengthInitialized = 1;
length
изменяемым или даже поместить его во вложенную структуру. Отказ от константности почти никогда не бывает хорошей идеей.
const
функций-членов, он должен быть mutable
. В противном случае вы усложняете жизнь себе и другим сопровождающим.