프로그래밍/C++

C++] dynamic_cast에 대해서...

Hwan2 2020. 6. 14. 20:10
반응형

모든 언어에는 형변환이 있습니다. C++에선 다양한 형번환 객체들을 제공합니다.


1. static_cast = https://hwan-shell.tistory.com/211


2. dynamic_cast


3. const_cast = https://hwan-shell.tistory.com/215


4. reinterpret_cast = https://hwan-shell.tistory.com/219


가 있습니다.


그 중 dynamic_cast에 대해 알아보도록 하겠습니다.



1. dynamic_cast란?

class의 상속관계에서의 형변환을 프로그래머가 올바르게 하도록 도와주는 긴능을 제공하는 녀석입니다.
dynamic_cast는 RTTI(Run Time Type Information)을 지원합니다.
RTTI는 런타임에서 클래스의 type_info를 보고 해당 클래스가 올바른 type의 형태인지 아닌지 판단하게 해 줍니다.

dynamic_cast를 사용하는 1가지 조건이 있습니다.

바로 virtual function을 사용해야 합니다.
그 이유는 RTTI의 type_info 때문입니다.
virtual function을 사용하게 되면 해당 클래스에는 v-table이 생성이 됩니다.
이 v-table에는 override된 자식 클래스의 함수를 가르킬 수 있는 주소값이 들어있는데
이 v-table에 클래스의 type_info 정보가 들어가게 됩니다.

때문에 dynamic_cast를 사용하게 되면 좀 더 안전한 형변환을 할 수 있습니다.
하지만 단점은 RTTI는 자원을 좀 먹기 때문에 퍼포먼스 측면에서 static_cast보다 좀 떨어지는 것이 사실입니다.
(a little....)

그럼 dynamic_cast를 왜 써야 할까?? 도대체 쓸 일이 있을까??

형변환?? 그런걸 할 이유가 있나?? 언제 필요하나?? 에 대해 궁금할 수 있습니다.


때문에 dynamic_cast사용법과 더불어 사용하는 예시를 하나 소개해보겠습니다.


2. 상속 upcast, downcast 사용 예

저는 Animal이라는 부모 클래스를 만들고 자식 클래스로 Cat 과 Dog를 만들고 싶습니다.
그리고 Cat 객체와 Dog객체 각 100개씩 총 200개를 백터에 넣어 관리하고 싶습니다.

하지만 저는 vector<Cat>, vector<Dog> 이렇게 2개의 백터로 관리하고 싶지 않고
하나의 백터로 관리하고 싶습니다.

이럴땐 어떻게 하면 될까요??

#include <iostream>
#include <string>
#include <vector>

class Animal {
public:
    virtual void sound() = 0;
    void info(){
        std::cout << "동물은 숨을 쉽니다.\n";
    }
};

class Dog : public Animal{
private:
    std::string name;
public:
    Dog(std::string s) : name(s) {};
    void sound() { std::cout << "멍멍\n"; }
    void name_print() { std::cout << name << std::endl; }
    void only_dog() { std::cout << "이건 개 클래스\n"; }
};

class Cat : public Animal {
private:
    std::string name;
public:
    Cat(std::string s) : name(s) {};
    void sound() { std::cout << "냐옹\n"; }
    void name_print() { std::cout << name << std::endl; }
    void data() { std::cout << this << std::endl; }
    void only_cat() { std::cout << "이건 고양이 클래스\n"; }
};

int main() {

    std::vector<Animal*> v;
    v.emplace_back(new Cat("나비"));
    v.emplace_back(new Dog("멍멍이"));

    delete v[0];
    delete v[1];
    return 0;
}


이런식으로 Animal이라는 부모객체를 선언 후 upcast를 통해 자식 클래스를 관리할 수 있습니다.


그럼 빼낼 땐 어떻게 해야 할까요?? downcast를 해주면 됩니다.


int main() {

    vector<Animal*> v;
    v.emplace_back(new Cat("나비"));
    v.emplace_back(new Dog("멍멍이"));

    Cat* cat = static_cast<Cat*>(v[0]);
    Dog* dog = (Dog*)v[1];

    cat->name_print();
    cat->sound();
    dog->name_print();
    dog->sound();

    delete cat;
    delete dog;
    return 0;
}


downcast가 잘 된것을 확인할 수 있습니다.

하지만 문제가 있습니다.


백터에 들어가있는 클래스는 Cat클래스 인데 Dog클래스로 받으면??

심각한 오류를 범할 수 있습니다.


int main() {

    vector<Animal*> v;
    v.emplace_back(new Cat("나비"));
    v.emplace_back(new Dog("멍멍이"));

    Dog* cat = static_cast<Dog*>(v[0]);
    Cat* dog = (Cat*)v[1];

    cat->name_print();
    cat->sound();
    cat->only_dog();
    dog->name_print();
    dog->sound();
    dog->only_cat();

    delete cat;
    delete dog;
    return 0;
}


Dog와 Cat의 위치를 바꿔줬는데 실행이 잘 되는걸 확인할 수 있습니다.

하지만 실행 결과는 혼종이 탄생되었습니다.


이런 오류때문에 이런것들을 방지하고자 dynamic_cast를 사용합니다.




3. dynamic_cast 사용법


#include <iostream>
#include <string>
#include <vector>

class Animal {
public:
    virtual void sound() = 0;
    void info(){
        std::cout << "동물은 숨을 쉽니다.\n";
    }
};

class Dog : public Animal{
private:
    std::string name;
public:
    Dog(std::string s) : name(s) {};
    void sound() { std::cout << "멍멍\n"; }
    void name_print() { std::cout << name << std::endl; }
    void only_dog() { std::cout << "이건 개 클래스\n"; }
};

class Cat : public Animal {
private:
    std::string name;
public:
    Cat(std::string s) : name(s) {};
    void sound() { std::cout << "냐옹\n"; }
    void name_print() { std::cout << name << std::endl; }
    void data() { std::cout << this << std::endl; }
    void only_cat() { std::cout << "이건 고양이 클래스\n"; }
};

int main() {

    std::vector<Animal*> v;
    v.emplace_back(new Cat("나비"));
    v.emplace_back(new Dog("멍멍이"));

    Cat* cat; Dog* dog;
    for (size_t idx = 0; idx < v.size(); idx++) {
        if (cat = dynamic_cast<Cat*>(v[idx])) {
            cat->name_print();
            cat->sound();
            cat->only_cat();
        }
        else {
            dog = dynamic_cast<Dog*>(v[idx]);
            dog->name_print();
            dog->sound();
            dog->only_dog();
        }
    }
    delete cat;
    delete dog;
    return 0;
}


이런식으로 if문을 통해 Runtime에 클래스들의 타입에 맞게 downcast된 것을 확인할 수 있습니다.


안정적인 코드를 위해서 dynamic_cast를 사용하는 것이 좋습니다.




반응형