수색…
비고
this
포인터는 이것을 구현하는 데 필요한 라이브러리가없는 C ++ 용 키워드입니다. 그리고 this
포인터라는 것을 잊지 마라! 그래서 당신은 할 수 없습니다 :
this.someMember();
화살표 기호 ->
사용하여 포인터에서 멤버 함수 또는 멤버 변수에 액세스 할 때 :
this->someMember();
this
포인터에 대한 더 나은 이해를 돕는 다른 유용한 링크 :
http://www.geeksforgeeks.org/this-pointer-in-c/
https://www.tutorialspoint.com/cplusplus/cpp_this_pointer.htm
이 포인터
모든 비 정적 멤버 함수는 숨겨진 매개 변수를 가지며 클래스의 인스턴스에 대한 포인터로 this
라는 이름을 갖습니다. 이 매개 변수는 매개 변수 목록의 처음에 자동으로 삽입되고 컴파일러에서 전적으로 처리됩니다. 클래스의 멤버를 멤버 함수의 내부에 액세스 할 때, 그것은 자동으로 액세스 this
; 이를 통해 컴파일러는 모든 인스턴스에 대해 단일 비 정적 멤버 함수를 사용할 수 있으며 멤버 함수는 다른 멤버 함수를 다형 적으로 호출 할 수 있습니다.
struct ThisPointer {
int i;
ThisPointer(int ii);
virtual void func();
int get_i() const;
void set_i(int ii);
};
ThisPointer::ThisPointer(int ii) : i(ii) {}
// Compiler rewrites as:
ThisPointer::ThisPointer(int ii) : this->i(ii) {}
// Constructor is responsible for turning allocated memory into 'this'.
// As the constructor is responsible for creating the object, 'this' will not be "fully"
// valid until the instance is fully constructed.
/* virtual */ void ThisPointer::func() {
if (some_external_condition) {
set_i(182);
} else {
i = 218;
}
}
// Compiler rewrites as:
/* virtual */ void ThisPointer::func(ThisPointer* this) {
if (some_external_condition) {
this->set_i(182);
} else {
this->i = 218;
}
}
int ThisPointer::get_i() const { return i; }
// Compiler rewrites as:
int ThisPointer::get_i(const ThisPointer* this) { return this->i; }
void ThisPointer::set_i(int ii) { i = ii; }
// Compiler rewrites as:
void ThisPointer::set_i(ThisPointer* this, int ii) { this->i = ii; }
생성자에서, this
안전 (암시 적 또는 명시 적으로) 이미 초기화 된 모든 필드, 또는 부모 클래스의 모든 필드에 액세스 할 수 있습니다; 반대로 아직 초기화되지 않은 필드 나 파생 클래스의 필드에 액세스하는 것은 안전하지 않습니다 (파생 클래스가 아직 생성되지 않아서 해당 필드가 초기화되거나 존재하지 않기 때문에). 통해 가상 멤버 함수를 호출하는 것이 안전하지 this
어떤 파생 된 클래스의 기능이 고려되지 않으므로, 생성자 (인해 아직 건설되지 파생 된 클래스에 따라서 그 생성자는 아직 VTABLE를 업데이트하지 않음).
또한 생성자에서 객체의 유형은 해당 생성자가 생성하는 유형입니다. 객체가 파생 된 유형으로 선언 된 경우에도 마찬가지입니다. 예를 들어, 아래의 예에서, ctd_good
및 ctd_bad
타입 CtorThisBase
내부 CtorThisBase()
, 및 입력 CtorThis
내부 CtorThis()
자신의 정식 형이더라도, CtorThisDerived
. 더 많이 파생 된 클래스가 기본 클래스 주위에 구성되면 인스턴스는 의도 된 유형의 완전히 구성된 인스턴스가 될 때까지 점차 클래스 계층 구조를 통과합니다.
class CtorThisBase {
short s;
public:
CtorThisBase() : s(516) {}
};
class CtorThis : public CtorThisBase {
int i, j, k;
public:
// Good constructor.
CtorThis() : i(s + 42), j(this->i), k(j) {}
// Bad constructor.
CtorThis(int ii) : i(ii), j(this->k), k(b ? 51 : -51) {
virt_func();
}
virtual void virt_func() { i += 2; }
};
class CtorThisDerived : public CtorThis {
bool b;
public:
CtorThisDerived() : b(true) {}
CtorThisDerived(int ii) : CtorThis(ii), b(false) {}
void virt_func() override { k += (2 * i); }
};
// ...
CtorThisDerived ctd_good;
CtorThisDerived ctd_bad(3);
이러한 클래스 및 멤버 함수는 다음과 같습니다.
- 좋은 생성자에서
ctd_good
:-
CtorThisBase
는CtorThis
생성자가 입력 될 때까지 완전히 생성됩니다. 따라서s
는i
를 초기화하는 동안 유효한 상태에 있으므로 액세스 할 수 있습니다. -
i
는j(this->i)
에 도달하기 전에 초기화됩니다. 따라서,i
초기화하는 동안 유효 상태에j
, 이에 액세스 할 수있다. -
j
는k(j)
에 도달하기 전에 초기화됩니다. 따라서j
는k
를 초기화하는 동안 유효한 상태이므로 액세스 할 수 있습니다.
-
- 잘못된 생성자에서
ctd_bad
:-
k
는j(this->k)
에 도달 한 후에 초기화됩니다. 따라서k
는j
를 초기화하는 동안 유효하지 않은 상태에 있으며,이 연산자에 액세스하면 정의되지 않은 동작이 발생합니다. -
CtorThisDerived
하지 때까지 구성되어CtorThis
구성된다. 따라서,k
를 초기화하는 동안b
가 유효하지 않은 상태에 있으며이를 액세스하면 정의되지 않은 동작이 발생합니다. - 객체
ctd_bad
여전히입니다CtorThis
가 떠날 때까지CtorThis()
하고 사용하도록 업데이트되지 않습니다CtorThisDerived
까지의 VTABLE을CtorThisDerived()
. 따라서virt_func()
는 호출 할 것인지 또는CtorThisDerived::virt_func()
를 호출 할 것인지에 관계없이CtorThis::virt_func()
를 호출합니다.
-
이 포인터를 사용하여 멤버 데이터에 액세스
이 문맥 this
포인터를 사용하는 this
꼭 필요한 것은 아니지만 주어진 함수 또는 변수가 클래스의 멤버임을 표시하여 코드를 판독기에 명확하게 만듭니다. 이 상황의 예 :
// Example for this pointer
#include <iostream>
#include <string>
using std::cout;
using std::endl;
class Class
{
public:
Class();
~Class();
int getPrivateNumber () const;
private:
int private_number = 42;
};
Class::Class(){}
Class::~Class(){}
int Class::getPrivateNumber() const
{
return this->private_number;
}
int main()
{
Class class_example;
cout << class_example.getPrivateNumber() << endl;
}
그것을 여기 에서 행동으로 보아라.
이 포인터를 사용하여 구성원 데이터와 매개 변수를 구별합니다.
이것은 구성원 데이터를 매개 변수와 차별화하는 실제 유용한 전략입니다. 다음 예제를 보겠습니다.
// Dog Class Example
#include <iostream>
#include <string>
using std::cout;
using std::endl;
/*
* @class Dog
* @member name
* Dog's name
* @function bark
* Dog Barks!
* @function getName
* To Get Private
* Name Variable
*/
class Dog
{
public:
Dog(std::string name);
~Dog();
void bark() const;
std::string getName() const;
private:
std::string name;
};
Dog::Dog(std::string name)
{
/*
* this->name is the
* name variable from
* the class dog . and
* name is from the
* parameter of the function
*/
this->name = name;
}
Dog::~Dog(){}
void Dog::bark() const
{
cout << "BARK" << endl;
}
std::string Dog::getName() const
{
return this->name;
}
int main()
{
Dog dog("Max");
cout << dog.getName() << endl;
dog.bark();
}
여기서 우리는 다음을 실행하는 생성자를 볼 수 있습니다 :
this->name = name;
여기에서는 Dog 클래스의 private 변수 이름에 매개 변수 이름을 지정하는 것을 볼 수 있습니다 (this-> name).
위 코드의 출력을 보려면 : http://cpp.sh/75r7
이 포인터 CV- 한정어
this
다른 포인터와 같은 cv-qualified 일 수도 있습니다. 그러나, 때문에 this
매개 변수 목록에 표시되지 않는 매개 변수, 특수 구문이 필요합니다; cv-qualifier는 매개 변수 목록 뒤에 있지만 함수 본문 앞에 나열됩니다.
struct ThisCVQ {
void no_qualifier() {} // "this" is: ThisCVQ*
void c_qualifier() const {} // "this" is: const ThisCVQ*
void v_qualifier() volatile {} // "this" is: volatile ThisCVQ*
void cv_qualifier() const volatile {} // "this" is: const volatile ThisCVQ*
};
this
매개 변수이기 this
cv-qualifier (s)를 기반으로 함수를 오버로드 할 수 있습니다 .
struct CVOverload {
int func() { return 3; }
int func() const { return 33; }
int func() volatile { return 333; }
int func() const volatile { return 3333; }
};
하면 this
있다 const
(포함 const volatile
)의 기능 여부 내재적으로 또는 명시 적으로 그것을 통해 멤버 변수에 기록 할 수 없다. 이것에 대한 유일한 예외는 mutable
멤버 변수 이며, const에 관계없이 쓸 수 있습니다. 이 때문에 const
는 물리적 인 상태를 수정하더라도 멤버 함수가 객체의 논리적 상태 (객체가 외부 세계에 나타나는 방식)를 변경하지 않음을 나타 내기 위해 사용됩니다 (객체가 보더에서 보이는 방식 ).
논리적 상태는 객체가 관찰자 외부에 나타나는 방식입니다. 그것은 물리적 인 상태에 직접적으로 묶여 있지 않으며 실제로는 물리적 인 상태로 저장되지 않을 수도 있습니다. 외부 관찰자가 변경 사항을 볼 수없는 한, 객체의 모든 비트를 뒤집더라도 논리 상태는 일정합니다.
비트 상태라고도하는 물리적 상태는 객체가 메모리에 저장되는 방식입니다. 이것은 데이터를 구성하는 원시 1과 0의 객체입니다. 객체는 메모리에있는 표현이 결코 변경되지 않으면 물리적으로 상수입니다.
그 C ++ 기지 참고 const
논리 상태, 물리적 인 상태 다움.
class DoSomethingComplexAndOrExpensive {
mutable ResultType cached_result;
mutable bool state_changed;
ResultType calculate_result();
void modify_somehow(const Param& p);
// ...
public:
DoSomethingComplexAndOrExpensive(Param p) : state_changed(true) {
modify_somehow(p);
}
void change_state(Param p) {
modify_somehow(p);
state_changed = true;
}
// Return some complex and/or expensive-to-calculate result.
// As this has no reason to modify logical state, it is marked as "const".
ResultType get_result() const;
};
ResultType DoSomethingComplexAndOrExpensive::get_result() const {
// cached_result and state_changed can be modified, even with a const "this" pointer.
// Even though the function doesn't modify logical state, it does modify physical state
// by caching the result, so it doesn't need to be recalculated every time the function
// is called. This is indicated by cached_result and state_changed being mutable.
if (state_changed) {
cached_result = calculate_result();
state_changed = false;
}
return cached_result;
}
당신이 기술적으로 사용할 수있는 동안 참고가 const_cast
에 this
은 비 CV-자격을 만들기 위해, 당신은 정말, 정말은 안하고 사용해야 mutable
대신. const_cast
실제로 객체에 사용될 때 정의되지 않은 동작을 호출 할 책임이 const
하면서, mutable
안전하게 사용할 수 있도록 설계되었습니다. 그러나 매우 오래된 코드에서이 문제에 부딪 힐 수도 있습니다.
class CVAccessor {
int arr[5];
public:
const int& get_arr_element(size_t i) const { return arr[i]; }
int& get_arr_element(size_t i) {
return const_cast<int&>(const_cast<const CVAccessor*>(this)->get_arr_element(i));
}
};
이렇게하면 불필요한 코드 중복을 방지 할 수 있습니다.
경우 일반 포인터와 마찬가지로, this
인 volatile
(포함 const volatile
, 그 대신 캐시되는, 메모리에서 액세스 할 때마다로드됩니다). 다른 포인터를 volatile
으로 선언하는 것과 같은 최적화 효과가 있으므로주의해야합니다.
인스턴스가 cv-qualified 인 경우 액세스 할 수있는 멤버 함수는 this
포인터가 적어도 인스턴스 자체만큼 cv-qualified 인 멤버 함수입니다.
- 비 cv 인스턴스는 모든 멤버 함수에 액세스 할 수 있습니다.
-
const
인스턴스는const
및const volatile
함수에 액세스 할 수 있습니다. -
volatile
인스턴스는volatile
및const volatile
함수에 액세스 할 수 있습니다. -
const volatile
인스턴스는const volatile
함수에 액세스 할 수 있습니다.
이것은 const
정확성 의 중요한 교리 중 하나입니다.
struct CVAccess {
void func() {}
void func_c() const {}
void func_v() volatile {}
void func_cv() const volatile {}
};
CVAccess cva;
cva.func(); // Good.
cva.func_c(); // Good.
cva.func_v(); // Good.
cva.func_cv(); // Good.
const CVAccess c_cva;
c_cva.func(); // Error.
c_cva.func_c(); // Good.
c_cva.func_v(); // Error.
c_cva.func_cv(); // Good.
volatile CVAccess v_cva;
v_cva.func(); // Error.
v_cva.func_c(); // Error.
v_cva.func_v(); // Good.
v_cva.func_cv(); // Good.
const volatile CVAccess cv_cva;
cv_cva.func(); // Error.
cv_cva.func_c(); // Error.
cv_cva.func_v(); // Error.
cv_cva.func_cv(); // Good.
이 포인터 참조 한정어
this
cv-qualifier와 마찬가지로 ref-qualifier 를 *this
적용 할 수도 있습니다. ref-qualifier는 normal과 rvalue reference semantics 중 하나를 선택하는데 사용되며, 컴파일러는 어느 것이 더 적절한가에 따라 copy 또는 move 의미를 사용할 수 있으며, 대신에 this
*this
적용됩니다.
참조 구문을 사용하는 ref-qualifier에도 불구 this
자체는 여전히 포인터입니다. ref-qualifier는 *this
의 타입을 실제로 변경하지 않는다. 그들이 한 것처럼 그 효과를 설명하고 이해하는 것이 더 쉽습니다.
struct RefQualifiers {
std::string s;
RefQualifiers(const std::string& ss = "The nameless one.") : s(ss) {}
// Normal version.
void func() & { std::cout << "Accessed on normal instance " << s << std::endl; }
// Rvalue version.
void func() && { std::cout << "Accessed on temporary instance " << s << std::endl; }
const std::string& still_a_pointer() & { return this->s; }
const std::string& still_a_pointer() && { this->s = "Bob"; return this->s; }
};
// ...
RefQualifiers rf("Fred");
rf.func(); // Output: Accessed on normal instance Fred
RefQualifiers{}.func(); // Output: Accessed on temporary instance The nameless one
멤버 함수는 ref-qualifier를 사용하거나 사용하지 않는 오버로드를 가질 수 없습니다. 프로그래머는 둘 중 하나를 선택해야합니다. 고맙게도 cv-qualifier는 ref-qualifier와 함께 사용할 수 있으므로 const
정확성 규칙을 준수 할 수 있습니다.
struct RefCV {
void func() & {}
void func() && {}
void func() const& {}
void func() const&& {}
void func() volatile& {}
void func() volatile&& {}
void func() const volatile& {}
void func() const volatile&& {}
};