C++ ThisPointer

m._.jooong·2023년 4월 19일
0

C++

목록 보기
15/23

#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();

}

0개의 댓글