VisualStudio/C++

[C++] 형변환 (static_cast, dynamic_cast, const_cast, reinterpret_cast)

usingsystem 2023. 7. 24. 17:00
728x90

static_cast

타입을 변경할 때 상식정인 캐스팅만 허용된다.

 

Ex)

1) int <-> float 등

2) Parent* -> Sun* (다운캐스팅) 안전성은 보장하지 않는다. 아래 코드에서 보면 sun2 또 한 parent를 상속받기 때문에 parent로 캐스팅되고이 parent를  static_cast를 사용하여 sun1으로 캐스팅이 되지만 서로 설계한 매개변수가 다르기 때문에 잘 못 된 메모리를 접근할 수 있다.

class Parent
{
};
class Sun1 : public Parent
{
};
class Sun2 : public Parent
{
};

int main()
{
	int hp = 100;
	int maxHp = 200;
	float ratio = static_cast<float>(hp / maxHp);
	cout << ratio << endl;

	{
		Parent* parent = new Parent();
		Sun1* sun1 = static_cast<Sun1*>(parent);
	}
	{
		Parent* parent = new Sun2();
		Sun1* sun1 = static_cast<Sun1*>(parent);
	}
	return 0;
}

dynamic_cast

static_cast에서 다운캐스팅할 때 안전성은 보장하지 않는다. 이런 상속 관계에서 안전한 형변환을 위해 사용되는 방법이 dynamic_cast이다. RTTI(Runtime Type Information)를 활용하는 방법으로 RTTI란 실시간으로 동작하는 환경에서 타입을 확인할 수 있는 방법으로 즉 다형성을 활용하는 방식이다. 다형성을 활용하는 방식의 예를 들어본다면 어떤 클래스에서 Virtural로 정의된 소멸자가 존재하고 이를 상속받은 클래스는 virtual함수가 하나라도 있다면 객체의 메모리에 가상함수 테이블 주소가 기입되고 dynamic_cast로 캐스팅 할 때 이 객체의 메모리에 가상함수의 주소를 확인하여 가능한지를 판단하고 만약 잘못된 타입으로 캐스팅 됐다면 nullptr을 반환한다.

 

결국 반드시 Virtual함수가 하나라도 존재해야 하며 가상테이블을 확인하기 때문에 virtual_cast보다는 느리다.

class Parent
{
public:
	virtual ~Parent() {}
};
class Sun1 : public Parent
{
};
class Sun2 : public Parent
{
};

int main()
{
	Parent* parent = new Parent();
	Sun1* sun1 = dynamic_cast<Sun1*>(parent);
	return 0;
}

const_cast

const를 붙이거나 떼기위해서.

원래 PrintText의 매개변수 타입은 const로 해야 하지만 const_cast를 통해 const를 제거할 수 있다.

void PrintText(char* str) {

}
int main()
{
	PrintText(const_cast<char*>("asdf"));
	return 0;
}

reinterpret_cast

가장 위험하고 강력한 형태의 캐스팅으로 캐스팅하고자 하는 둘 간에 상관관계가 없어도 형변환을 할 수 있다. 

class Test {

};

int main()
{
	Test* t = new Test();
	__int64 address = reinterpret_cast<__int64>(t);
	cout << address << endl;
}

활용

malloc으로 힙에서 동적할당받은 메모리를 캐스팅할 때 사용할 수 있다.

	void* p = malloc(1000);
	Test* t2 = reinterpret_cast<Test*>(p);

static_pointer_cast

std::shared_ptr와 같은 스마트 포인터를 다른 타입으로 안전하게 캐스팅할 수 있게 해주는 함수입니다. 주로 기본 클래스 포인터를 파생 클래스 포인터로 캐스팅할 때 사용됩니다.

static_pointer_cast는 스마트 포인터 간의 캐스팅을 안전하고 명확하게 할 수 있게 해줍니다. 이 함수는 정적 캐스팅이므로, 캐스팅하려는 타입 간의 관계가 컴파일 타임에 명확히 정의되어 있어야 합니다. 그렇지 않으면 컴파일러가 오류를 발생시킵니다.

#include <iostream>
#include <memory>

class Base {
public:
    virtual ~Base() = default;
};

class Derived : public Base {
public:
    void hello() {
        std::cout << "Hello from Derived!" << std::endl;
    }
};

int main() {
    // Base 타입의 std::shared_ptr 생성
    std::shared_ptr<Base> basePtr = std::make_shared<Derived>();

    // std::shared_ptr<Base>를 std::shared_ptr<Derived>로 캐스팅
    std::shared_ptr<Derived> derivedPtr = std::static_pointer_cast<Derived>(basePtr);

    // Derived 클래스의 메서드 호출
    derivedPtr->hello();

    return 0;
}
728x90