Я понимаю значение 'этого', но я не вижу варианта использования его.
Для следующего примера я должен преподавать компилятор, если параметр совпадает с членской переменной, и мне нужен этот указатель.
#include <iostream>
using namespace std;
class AAA {
int x;
public:
int hello(int x) { this->x = x;}
int hello2(int y) {x = y;} // same as this->x = y
int getx() {return x;}
};
int main()
{
AAA a;
a.hello(10); // x <- 10
cout << a.getx();
a.hello2(20); // x <- 20
cout << a.getx();
}
Каков был бы вариант использования для 'этого' указателя кроме этого (изобретенного) примера?
Спасибо за все ответы. Даже при том, что я делаю ответ orangeoctopu, как принято один, это просто, потому что он получил большую часть голосования. Я должен сказать, что все ответы довольно полезны, и дают мне лучше понимание.
Иногда вы хотите вернуть себя из оператора, например operator =
MyClass& operator=(const MyClass &rhs) {
// assign rhs into myself
return *this;
}
В C++ он используется не очень часто. Однако очень часто используется, например, в Qt, где вы создаете виджет, который имеет текущий объект в качестве родителя. Например, окно создает кнопку в качестве своего дочернего объекта:
QButton *button = new QButton(this);
Указатель this полезен, если методу класса необходимо передать экземпляр (this) другой функции.
Указатель this - это указатель на сам объект. Рассмотрим, например, следующий метод:
class AAA {
int x;
public:
int hello(int x) { some_method(this, x);}
};
Вы можете удалить динамически созданный объект, вызвав delete this
из одной из его функций-членов.
Кроме получения указателя на собственный объект для передачи (или возврата) другим функциям, и решения, что идентификатор является членом, даже если он скрыт локальной переменной, есть действительно надуманное использование этого в шаблонном программировании. Это использование заключается в преобразовании не зависимого имени в зависимое. Шаблоны проверяются в два захода, сначала перед фактической подстановкой типов, а затем снова после подстановки типов.
Если вы объявляете шаблонный класс, производный от одного из параметров его типа, вам необходимо квалифицировать доступ к членам базового класса так, чтобы компилятор обошел проверку на первом этапе и оставил проверку на второй этап:
template <typename T>
struct test : T {
void f() {
// print(); // 1st pass Error, print is undefined
this->print(); // 1st pass Ok, print is dependent on T
}
};
struct printer {
void print() { std::cout << "print"; }
};
struct painter {
void paint() { std::cout << "paint"; }
};
int main() {
test<printer> t; // Instantiation, 2nd pass verifies that test<printer>::print is callable
t.f();
//test<painter> ouch; // 2nd pass error, test<painter>::print does not exist
}
Важный момент: поскольку test
наследуется от T
, все ссылки на this
зависят от аргумента шаблона T
, и поэтому компилятор предполагает, что он корректен, и оставляет проверку на второй этап. Есть и другие решения, например, фактически квалифицировать тип, реализующий метод, как в:
template <typename T>
struct test2 : T {
void f() {
T::print(); // 1st pass Ok, print is dependent on T
}
};
Но это может иметь нежелательный побочный эффект: компилятор будет статически диспетчеризировать вызов printer::print
независимо от того, является ли printer
виртуальным методом или нет. Таким образом, поскольку printer::print
объявлен виртуальным, если класс, производный от test
, реализует print
, то будет вызван этот конечный переопределитель, в то время как если бы тот же класс был производным от test2
, то код вызвал бы printer::print
.
// assumes printer::print is virtual
struct most_derived1 : test<printer> {
void print() { std::cout << "most derived"; }
};
struct most_derived2 : test2<printer> {
void print() { std::cout << "most derived"; }
};
int main() {
most_derived1 d1;
d1.f(); // "most derived"
most_derived2 d2;
d2.f(); // "print"
}
void somefunc(AAA* a_p)
{
......
}
class AAA {
int x;
public:
int hello(int x) { this->x = x;}
int hello2(int y) {x = y;} // same as this.x = y
int getx() {return x;}
void DoSomething() { somefunc(this); }
};
this
неявно, когда вы используете функцию-член или переменную без указания. Кроме этого, существует много, много ситуаций, в которых вы захотите передать текущий объект другой функции или в качестве возвращаемого значения.
Так что, да, это весьма полезно.
Иногда вам нужно обратиться к самому объекту "this", а иногда вам может понадобиться разобрать случаи, когда локальная переменная или параметр функции затеняет член класса:
class Foo {
int i;
Foo* f() {
return this; // return the 'this' pointer
}
void g(){
j(this); // pass the 'this' pointer to some function j
}
void h(int i) {
this->i = i; // need to distinguish between class member 'i' and function parameter 'i'
}
};
Два первых случая (f()
и g()
) являются наиболее значимыми. Третьего можно избежать, просто переименовав переменную-член класса, но никак нельзя обойтись без использования this
в первых двух случаях.
Другой возможный вариант использования этого
:
#include <iostream>
using namespace std;
class A
{
public:
void foo()
{
cout << "foo() of A\n";
}
};
class B : A
{
public:
void foo()
{
((A *)this)->foo(); // Same as A::foo();
cout << "foo() of B\n";
}
};
int main()
{
B b;
b.foo();
return 0;
}
g++ this.cpp -o this ./this foo() of A foo() of B
Еще одно использование this
- предотвращение сбоев, если метод вызывается для метода, вызываемого по NULL-указателю (аналогично шаблону объекта NULL):
class Foo
{
public:
void Fn()
{
if (!this)
return;
...
}
};
...
void UseFoo(Foo* something)
{
something->Fn(); // will not crash if Foo == NULL
}
Полезно это или нет, зависит от контекста, но я видел это время от времени и сам тоже использовал.
При передаче ссылки на объект в одном из его методов. Например:
struct Event
{
EventProducer* source;
};
class SomeContrivedClass : public EventProducer
{
public:
void CreateEvent()
{
Event event;
event.source = this;
EventManager.ProcessEvent(event);
}
};
Это полезно, если вам нужно передать указатель на текущий объект другой функции или вернуть его. Последний используется для объединения функций вместе:
Obj* Obj::addProperty(std::string str) {
// do stuff
return this;
}
obj->addProperty("foo")->addProperty("bar")->addProperty("baz");