Post

2026-03-03 TIL (6일차)

2026-03-03 TIL (6일차)

C++ 학습

  1. 포인터 vs 역참조 연산자 vs 곱셈 연산자

    구분피연산자 개수특징
    포인터- (해당 없음)자료형 오른쪽에 붙습니다. 주소를 저장함 (예: int* ptr;)
    역참조 연산자1개피연산자로 포인터를 받아서, 해당 메모리 주소에 저장된 값을 읽거나 값을 수정할 때 사용하는 연산자. (예: *ptr = 10;)
    곱셈 연산자2개- (예: a * b;)
  2. 참조자 vs 주소연산자

    구분위치의미예시
    참조자자료형 뒤(int&, Player&)기존 변수에 대한 별명void Func(int& a)
    주소 연산자변수 앞(&num, &obj)변수의 메모리 주소int* ptr = #
  3. Private 생성자
    클래스 내부에서 객체를 생성을 허용하는 생성자
    생성자가 private이면 밖에서는 new를 못 하지만, 클래스 내부의 멤버 함수는 private에 접근할 수 있기 때문에 객체 생성이 가능합니다.
    대표적인 게임 예시: 게임 내내 하나만 존재하면서, 어디서든 접근 가능하고 관리 가능한 매니저 클래스.

    사용하는 이유

    1. “강제성” 부여 (실수 방지)
    2. 메모리와 자원의 단일화
    3. 생성 시점 조절

    적용 예시

    1. 싱글톤 패턴 (하나만 존재해야 하는 객체)
    2. 정적 유틸리티 클래스 (상태 변수는 없고 기능 함수만 있는 클래스)
    3. 팩토리 메서드 (객체 생성이 복잡하거나, 생성되는 방식을 명확히 구분하고 싶을 때)
  4. this 포인터

    표현자료형(Type)의미
    thisT* (포인터)객체(클래스)자신의 메모리 주소 값
    *thisT (객체 그 자체)주소를 찾아가서 만난 “나 자신(객체)”

    주로 사용하는 곳
    ● 참조 반환
    원리: 반환 타입이 참조형이므로, 새로운 임시 객체를 만들지 않고 현재 메모리에 있는 그 객체의 별명을 그대로 던집니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    class Player
    {
    private:
        int hp = 100;
    public:
        Player& DrinkPotion(int amount)
        {
            hp += amount;
            return *this; // 나 자신의 실체(객체)를 반환
        }
        Player& TakeDamage(int damage)
        {
            hp -= damage;
            return *this;
        }
        void ShowHP() { std::cout << "현재 HP: " << hp << std::endl; }
    };
    // 사용 예시
    player.DrinkPotion(20).TakeDamage(10).ShowHP();
    

    ● 이름 충돌 해결
    원리: 멤버 변수의 이름과 함수의 매개변수(Parameter) 이름이 같을 때, this를 사용하여 멤버 변수를 명확히 지칭합니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    class Monster
    {
    private:
        int level;
    public:
        void SetLevel(int level)
        {
            // level = level;      // ❌ 둘 다 매개변수 level로 인식됨
            this->level = level;   // ✅ this->level은 멤버 변수, 오른쪽 level은 매개변수
        }
    };
    

    ● 나 자신을 통째로 다른 함수에 넘겨줄 때
    원리: 어떤 객체가 다른 객체와 상호작용할 때, 자신과 상호작용하기위해 자신의 주소(this)를 넘겨주는 방식입니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
       class Weapon;
    
       class Warrior
       {
       public:
           void Equip(Weapon* weapon)
       {
               // 무기에게 "나(this)"를 장착하라고 명령함
               weapon->SetOwner(this); 
           }
       };
    
       class Weapon
       {
       private:
           Warrior* owner;
       public:
           void SetOwner(Warrior* warrior)
           {
               this->owner = warrior;
               std::cout << "무기의 주인이 결정되었습니다." << std::endl;
           }
       };
    
  5. 클래스 암시적 형 변환

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    class AAA {
    public:
        AAA(int n) : num(n) { } // 이제 "명시적"으로만 호출 가능!
        int num;
    };
    
    int main() {
        AAA obj = 3;      // ✅ 암시적 변환 AAA obj(3);
        AAA obj(3);       // ✅ 가능 (직접 생성자 호출)
        AAA obj2 = AAA(3);// ✅ 가능 (명시적 형 변환)
    }
    

    그러므로 묵시적인 변환을 허용하지 않을려면 explicit을 사용해야한다.
    explicit AAA(int n) : num(n) { }
    주로 디폴트 복사생성자를 막기위해 사용된다.

  6. 깊은복사vs얕은복사

    구분얕은 복사 (Shallow Copy)깊은 복사 (Deep Copy)
    핵심 정의멤버 변수의 만 그대로 복사함포인터가 가리키는 데이터 공간(동적할당)까지 새로 생성
    포인터 처리주소값만 복사 (두 객체가 같은 곳을 가리킴)새 메모리 할당 + 내용 복사 (두 객체가 다른 곳을 가리킴)
    객체 독립성낮음 (한쪽을 수정/삭제하면 다른 쪽이 영향받음)높음 (서로 완전히 독립적인 객체임)
    C++ 기본 동작복사 생성자 미정의 시 컴파일러가 자동 수행사용자가 코드로 구현해야 함
    주요 위험Double Free, 댕글링 포인터 발생 위험없음 (안전함)
    성능(속도)매우 빠름 (주소값만 슥 복사하면 끝)상대적으로 느림 (메모리 할당 + 데이터 복사 과정 필요)
  7. 복사생성자가 호출되는 시점

    ● 기존에 생성된 객체를 이용해서 새로운 객체를 초기화하는 경우

    1
    2
    3
    
    Monster m1("Slime", 100);
    Monster m2 = m1;    // 복사 생성자 호출 
    Monster m3(m1);    // 복사 생성자 호출 
    

    원리: m2와 m3라는 새로운 메모리가 힙이나 스택에 할당됩니다.
    이때 컴파일러는 빈 메모리를 그냥 두지 않고, m1의 데이터를 복사 생성자를 통해 메모리에 써넣습니다.

    ● Call-by-value 방식의 함수호출 과정에서 객체를 인자로 전달하는 경우 함수를 호출 할 때 매개변수가 참조형(&) 이 아닐 때 발생

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    void SpawnParticle(Monster m) // 여기서 매개변수 m이 생성됨
    { 
     // ... 
    }
    
    int main()
    {
        Monster boss("Dragon", 5000);
        SpawnParticle(boss); // boss를 전달하는 순간 복사 생성자 호출!
    }
    

    원리:함수 SpawnParticle이 실행되면, 그 함수만을 위한 전용 메모리 공간(스택 프레임)이 생깁니다.
    그 공간 안에 m이라는 새로운 지역 변수가 만들어져야 하므로, 원본인 boss를 본떠서 m을 만드느라 복사 생성자가 호출됩니다.

    ● 객체를 반환하되, 참조형으로 반환하지 않는 경우 함수가 끝나고 결과물을 던져줄 때, 그 결과물을 안전하게 밖으로 빼내기 위해 발생합니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    Monster CreateRandomMonster()
    {
        Monster temp("Zombie", 50);
        return temp; // temp를 반환할 때 복사 생성자 호출!
    }
    
    int main()
    {
        Monster m = CreateRandomMonster();
    }
    

    원리: 함수가 종료되면 함수 안의 temp는 메모리에서 사라져야 합니다.
    그런데 이 값을 밖으로 전달해야 하니, 사라지기 직전에 임시 객체하나 더 만들어서 원본 데이터를 복사합니다.
    이때 복사 생성자가 사용됩니다.

코딩 테스트 대비

  1. 아스키코드(ASCII)를 활용해 문자 <-> 숫자 변환
    char c;
    int n;

    목표공식Input(예시)결과
    문자 숫자 → 기본 숫자c - ‘0’문자 ‘5’숫자 5
    기본 숫자 → 문자 숫자n + ‘0’숫자 5문자 ‘5’
    알파벳 순서 찾기 (0~25)c - ‘a’문자 ‘c’숫자 2
    숫자를 알파벳으로 복구n + ‘a’숫자 2문자 ‘c’
    대소문자 한 방에 뒤집기c ^ 32문자 ‘A’ (또는 ‘a’)문자 ‘a’ (또는 ‘A’)
    문자의 아스키 값 확인(int)c문자 ‘A’숫자 65
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    // --- [1] 문자 숫자 ('5') -> 진짜 숫자 (5) ---
     char c1 = '5';
     int n1 = c1 - '0'; 
     cout << "1. 문자 '5' -> 숫자: " << n1 << " (타입: " << typeid(n1).name() << ")" << endl;
    
    
     // --- [2] 진짜 숫자 (5) -> 문자 숫자 ('5') ---
     int n2 = 5;
     char c2 = n2 + '0';
     cout << "2. 숫자 5 -> 문자: '" << c2 << "'" << endl;
    
    
     // --- [3] 알파벳 순서 찾기 ('c' -> 2) ---
     char c3 = 'c';
     int n3 = c3 - 'a'; // 'a'는 0, 'b'는 1, 'c'는 2...
     cout << "3. 'c'는 몇 번째 알파벳? : " << n3 << " (0번부터 시작)" << endl;
    
    
     // --- [4] 숫자를 알파벳으로 복구 (2 -> 'c') ---
     int n4 = 2;
     char c4 = n4 + 'a';
     cout << "4. 2번 인덱스 알파벳은? : '" << c4 << "'" << endl;
    
    
     // --- [5] 대소문자 한 방에 뒤집기 ('A' <-> 'a') ---
     char c5 = 'A';
     char flipped = c5 ^ 32; // XOR 연산으로 32차이를 뒤집음
     cout << "5. 'A'를 뒤집으면: '" << flipped << "', 다시 뒤집으면: '" << (char)(flipped ^ 32) << "'" << endl;
    
    
     // --- [6] 문자의 아스키 값 확인 ('A' -> 65) ---
     char c6 = 'A';
     cout << "6. 'A'의 진짜 아스키 번호: " << (int)c6 << endl;
    
    
This post is licensed under CC BY 4.0 by the author.