#include <iostream>
// 문자열 관련 라이브러리
#include <string>
// C++ 표준 라이브러리
using namespace std;
// 테스트를 위해 Student1이라는 이름의 클래스를 정의한다
class Student1
{
private:
int age;
public:
Student1(int value)
{
age = value;
}
void SetInfo()
{
cout << age << " 살 입니다" << endl;
}
};
// 1. 클래스의 멤버 변수와 매개 변수의 이름이 같을 때 사용된다.
// 테스트를 위해 student2 클래스 정의
class Student2
{
private:
// 멤버 변수는 변수 이름만 보더라도 멤버 변수인 것을 알 수 있도록 m_를 붙여준다.
// int m_Age;
int age;
public:
// 멤버 변수 age와 매개 변수 age가 같을 때
// this->age는 멤버 변수를 의미하고, age는 매개 변수를 의미한다
// 하지만 매개 변수를 동일한 이름으로 하지 않는다면 굳이 this포인터를 쓸 필요는 없다.
// this 포인터는 명시적으로 멤버 변수임을 가리킬 수 있다.
Student2(int age)
{
//age = age; // (o)
this->age = age;
}
};
// 2. 객체 자신의 주소를 반환할 때 사용된다,
// 테스트를 위해 Student3 클래스 정의
class Student3
{
private:
int age;
public:
Student3(int age)
{
this->age = age;
}
void SetInfo()
{
// 자기 자신의 주소를 반환
cout << "this : " << this << endl;
}
};
// *this를 확인해보기 위해 Lion 클래스를 정의
class Lion
{
private:
int num = 10;
public:
// 자기 자신을 출력하는 멤버 함수를 정의
void PrintThis()
{
cout << "this : " << this << endl;
}
// 자기 자신의 참조자를 반환하는 멤버 함수를 정의
Lion& ReturnThisReference()
{
return *this;
}
};
// 테스트를 위해 Tiger 클래스 정의
class Tiger
{
private:
string m_Name;
public:
// : 콜론 뒤가 초기화 리스트로 초기화를 하는 부분이다
Tiger(string name) : m_Name(name)
{
}
void SetName()
{
cout << "Name : " << m_Name << endl;
}
Tiger& ReturnThis1()
{
return *this; // 가리키는 값이 자기 자신이라서 참조가 가능하다
}
Tiger ReturnThis2()
{
return *this; // 복사 생성자에 의해 주소값 다르다
}
Tiger& ReturnThis3()
{
//return this; // 주소값에 대한 참조는 안되기 때문이다
}
Tiger* ReturnThis4()
{
return this; // this가 자기 자신을 가리키는 포인터이기 때문에 포인터 리턴이 가능하다
}
Tiger ReturnThis5()
{
//return this; // 포인터를 일반 변수로 리턴하면 안된다
}
};
int main()
{
// This포인터
// 클래스의 멤버 변수를 호출할 때 C++은 어떻게 호출할 객체(인스턴스, instance)를 찾을까
// = this라는 숨겨진 포인터를 사용한다는 것이다.
// this포인터는 클래스의 실제 객체에 대한 주소를 가리키는 포인터이다
// 클래스의 멤버 함수를 호출할 때 이 this라는 포인터를 이용해서 객체를 찾을 수 있다,
// 클래스가 메모리에 올라가면 객체(인스턴스)가 된다.
// 테스트를 위해 student1이라는 이름의 클래스를 정의한다
Student1 student1(22);
cout << "student1 : ";
student1.SetInfo();
cout << endl;
// 메모리에 객체가 생성될 때 멤버 변수는 메모리에 올라가지만 멤버 함수는 올라가지 않는다.
// 여러개의 같은 객체가 메모리에 올라갈 때 멤버 변수는 다양하게 생성되지만
// 멤버 함수는 하나만 생성되고 구분을 위해 this포인터를 사용한다.
cout << "Sizeof(student1) : " << sizeof(student1) << " bytes" << endl;
cout << endl;
// student1.SetInfo() : 함수는 매개 변수 없이 호출되는 것 처럼 보인다
// 하지만 student1.SetInfo(student1); 처럼 쓰인다.
// 이 주소는 우리 눈에는 안보이지만 존재한다.
// 첫 번째 매개 변수로 전달된 객체의 주소는 이름이 this인 포인터 변수에 저장된다.
// student1 : student1객체의 주소
// this : 객체의 주소
// 이렇게 컴파일러에 의해서 자동으로 추가 된다.
// 우리가 실제로 매개 변수로 넣어주는 것 보다 하나가 더 추가되는 것이다.
// 이렇게 하면 호출된 멤버 함수는 자신을 호출한 객체가 무엇인지 정확하게 알 수 있다
// this포인터는
// 1. 클래스의 멤버 변수와 매개 변수의 이름이 같을 때 사용된다.
// 테스트를 위해 student2 클래스 정의
// 2. 객체 자신의 주소를 반환할 때 사용된다,
// 테스트를 위해 Student3 클래스 정의
Student3 student3(2);
cout << "student3의 주소값 : " << &student3 << endl;
cout << endl;
// this를 이용해서 출력한 값과 &student3의 값이 같은 것을 확인할 수 있다.
// this는 자기 자신을 가리키는 포인터이기 때문이다.
cout << "student3의 주소값 : ";
student3.SetInfo();
cout << endl;
// this포인터는 메모리에 생성된 객체 자신을 가리키는 포인터이다
// 그러면 왜 this 포인터를 만든 것일까
// class멤버변수들은 객체가 생성되어질 때 마다 메모리에 할당된다.
// 함수와 같이 크기가 큰 것들은 메모리에 굳이 반복해서 할당할 필요없다.
// 함수를 메모리에 한 번만 할당해 놓고 각 객체의 this포인터를 넘긴다
// 어떤 객체가 함수를 호출했는지 알 수 있게 하는 것이다.
// *this는 무엇일까
// this포인터가 가리키는 주소의 실제 값을 의미한다
// 멤버 변수와 멤버 함수이다
int intValue1 = 30;
int* intPtr1 = &intValue1;
cout << "*intPtr1 : " << *intPtr1 << endl;
cout << endl;
// *intPtr1은 출력하면 30이 나온다
// 이것은 *intPtr1 가리키는 값을 참조하기 때문이다 포인터의 참조이다
// int& intRef1 = *intPtr1; 은
// int& intRef1 = intValue1; 으로 생각할 수 있다
// 참조자는 포인터가 아니다 별명이다
// int* (&intRef2) = intPtr1;은
// intPtr1이 가리키는 주소값을 받는 상황임에도 당연히 포인터로 받아야 하지만
// intRef2라는 참조자로 받는 것도 가능하다
// 단 intRef2는 참조자이지만 포인터로 전달받기 때문에 별도의 포인터 주소값을 가지게 된다.
int& intRef1 = *intPtr1;
int* (&intRef2) = intPtr1;
// 참조자는 포인터가 아니다
// *intRef1 : 참조자는 포인터가 아니니 역참조 연산자로 포인터가 가리키는 값을 알 수 없다.
// &intRef1 : 참조자는 포인터가 아니니 참조자가 가리키는 주소값을 알기 위해서는 주소 연산자를 사용한다.
//cout << "intRef1 value : " << *intRef1 << ", intRef1 address : " << &intRef1 << endl; // x
cout << "intRef1 value : " << intRef1 << ", intRef1 address : " << &intRef1 << endl;
cout << endl;
// this는 자기 자신의 객체를 가리키는 포인터 변수이다
// *this를 확인해보기 위해 Lion 클래스를 정의
Lion lion1;
// 자기 자신을 출력
lion1.PrintThis();
cout << endl;
// 참조자 변수 선언하고 참조자 리턴 받기
Lion& lionRef = lion1.ReturnThisReference();
// 참조자의 자기 자신을 출력
lionRef.PrintThis();
cout << endl;
// this는 자기 자신을 가리키는 키워드 이고
// 포인터 변수에 역참조 연산자 (*)를 붙여주면 포인터 변수가 가리키는 실제 값이다
// 객체일 때는 포인터 변수가 가리키는 객체의 멤버변수, 멤버함수를 가리킨다.
// *this : 자기 자신이 가리키는 값(멤버변수, 멤버함수)이다
// 포인터의 참조 개념이다
// this는 자기 자신을 가리키는 포인터이고
// *this는 자기 자신의 객체 자체이다
cout << "lion1의 주소값 : " << &lion1 << endl;
cout << "lionRef의 주소값 : " << &lionRef << endl;
cout << endl;
// 테스트를 위해 Tiger 클래스 정의
Tiger tiger1("Korea");
Tiger* ReturnThis1 = &tiger1.ReturnThis1();
Tiger ReturnThis2 = tiger1.ReturnThis2();
Tiger* ReturnThis4 = tiger1.ReturnThis4();
cout << "ReturnThis1 : " << ReturnThis1 << endl;
cout << "ReturnThis2 : " << &ReturnThis2 << endl;
cout << "ReturnThis4 : " << ReturnThis4 << endl;
ReturnThis1->SetName();
ReturnThis2.SetName();
ReturnThis4->SetName();
}