전체 코드

1. 타입 변환의 기본 개념

(1) 값 타입 변환 vs 참조 타입 변환

✔️ 값 타입 변환(Value Type Conversion)

  • 비트열을 재구성하여 변환
  • 의미를 유지하기 위해 원본과 다른 데이터 표현 방식으로 변환
  • 예) int → float 변환 시, 정수형 데이터를 부동소수점 표현 방식으로 변환

✔️ 참조 타입 변환(Reference Type Conversion)

  • 비트열은 변경하지 않고, 데이터를 해석하는 관점만 변경
  • 포인터 타입 변환도 참조 타입 변환과 동일한 규칙을 따름
#include <iostream>
using namespace std;

int main() {
    // [1] 값 타입 변환
    int a = 123456789;  
    float b = (float)a; // 값 타입 변환 (비트열 재구성)
    cout << "값 타입 변환 (int → float): " << b << endl;

    // [2] 참조 타입 변환 (비트열 유지)
    float c = (float&)a; 
    cout << "참조 타입 변환 (int → float&): " << c << endl;

    return 0;
}

결과

  • int → float 변환 시, 원본과 다른 표현 방식으로 변환
  • int → float& 변환 시, 비트 패턴을 그대로 유지하며 다른 방식으로 해석

(2) 안전한 변환 vs 불안전한 변환

✔️ 안전한 변환 (Safe Conversion)

  • 의미가 100% 일치하는 경우
  • 같은 타입 내에서 작은 크기 → 큰 크기 변환 (char → short → int → long long)
  • 업캐스팅(Upcasting, 부모 → 자식 변환)

✔️ 불안전한 변환 (Unsafe Conversion)

  • 의미가 완전히 일치하지 않는 경우
  • 타입이 다르거나, 큰 크기 → 작은 크기로 변환 (int → short, float → int)
  • 데이터 손실 가능성이 존재
int main() {
    // 안전한 변환 (업캐스팅)
    int a = 123456789;
    long long b = a;  // int → long long (업캐스팅)
    cout << "안전한 변환: " << b << endl;

    // 불안전한 변환 (다운캐스팅)
    short c = (short)a;  // int → short (데이터 손실 가능)
    cout << "불안전한 변환 (int → short): " << c << endl;

    return 0;
}

결과

  • int → long long 변환은 안전한 변환 (데이터가 그대로 유지)
  • int → short 변환은 데이터 손실 가능성이 존재 (다운캐스팅)

2. 암시적 변환(Implicit) vs 명시적 변환(Explicit)

(1) 암시적 변환 (자동 변환)

컴파일러가 자동으로 타입 변환을 수행하는 경우

int main() {
    int a = 123456789;
    float b = a;  // 암시적 변환 (int → float)
    cout << "암시적 변환: " << b << endl;
    return 0;
}

결과

  • int → float 변환이 자동으로 수행됨

(2) 명시적 변환 (강제 변환)

프로그래머가 강제로 타입 변환을 수행하는 경우
C 스타일 캐스팅 (type) 또는 C++ 스타일 캐스팅 사용 (static_cast, reinterpret_cast 등)

int main() {
    int a = 123456789;
    float b = (float)a;  // C 스타일 캐스팅
    float c = static_cast<float>(a);  // C++ 스타일 캐스팅

    cout << "C 스타일 변환: " << b << endl;
    cout << "C++ 스타일 변환: " << c << endl;
    return 0;
}

결과

  • (float)astatic_cast<float>(a)는 동일한 동작을 수행

3. 클래스 간 타입 변환

(1) 연관 없는 클래스 간 변환

일반적으로 연관 관계가 없는 클래스 간 변환은 허용되지 않음
하지만 타입 변환 생성자(Type Conversion Constructor)와 타입 변환 연산자(operator) 를 활용하면 변환 가능

class Knight {
public:
    int _hp = 100;
};

class Dog {
public:
    int _age = 1;

    // Knight → Dog 변환 생성자
    Dog(const Knight& knight) {
        _age = knight._hp / 10;  // 체력을 나이로 변환
    }

    // Dog → Knight 변환 연산자
    operator Knight() {
        Knight knight;
        knight._hp = _age * 10;
        return knight;
    }
};

int main() {
    Knight knight;
    Dog dog = (Dog)knight;  // 타입 변환 생성자 호출
    cout << "Dog age: " << dog._age << endl;

    Knight knight2 = dog;  // 타입 변환 연산자 호출
    cout << "Knight HP: " << knight2._hp << endl;

    return 0;
}

결과

  • Knight 객체를 Dog 객체로 변환 (타입 변환 생성자 사용)
  • Dog 객체를 Knight 객체로 변환 (타입 변환 연산자 사용)

(2) 상속 관계에서의 타입 변환

✔️ 자식 → 부모 (업캐스팅) → 암시적으로 허용
✔️ 부모 → 자식 (다운캐스팅) → 명시적으로만 가능 (위험성 존재)

class Dog {
public:
    int _age = 1;
};

class BullDog : public Dog {
public:
    bool _isFrench = true;
};

int main() {
    BullDog bulldog;
    
    // [1] 자식 → 부모 (업캐스팅) : 자동 변환
    Dog dog = bulldog;  // OK

    // [2] 부모 → 자식 (다운캐스팅) : 명시적 변환 필요 (위험)
    Dog dog2;
    BullDog* bulldog2 = (BullDog*)&dog2;  // 위험한 변환!
    
    return 0;
}

결과

  • BullDog → Dog 변환은 자동 변환(업캐스팅)
  • Dog → BullDog 변환은 명시적 변환 필요 (다운캐스팅) → 메모리 침범 위험 존재!

4. 포인터 타입 변환

(1) 연관 없는 클래스 간 포인터 변환 (위험)

class Knight {
public:
    int _hp = 100;
};

class Item {
public:
    int _itemType = 1;
};

int main() {
    Knight* knight = new Knight();
    
    // 강제 타입 변환 (위험)
    Item* item = (Item*)knight;
    item->_itemType = 2;  // 잘못된 메모리 접근 가능!

    delete knight;
    return 0;
}

위험성

  • Knight*Item*으로 변환하면 잘못된 메모리 접근 가능

(2) 부모 → 자식 변환 (위험한 다운캐스팅)

class Item {};
class Weapon : public Item {};

int main() {
    Item* item = new Item();
    Weapon* weapon = (Weapon*)item;  // 위험한 다운캐스팅

    delete item;
    return 0;
}

위험성

  • 부모 클래스를 자식 클래스로 변환하면 메모리 침범 가능성 존재!

profile
李家네_공부방

0개의 댓글