본문 바로가기
프로그래밍/C++

C++] static_cast란??

by Hwan2 2020. 6. 14.
728x90
반응형

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


1. static_cast


2. dynamic_cast = https://hwan-shell.tistory.com/213


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


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


가 있습니다.


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


1. static_cast란?

C++에서 제공하는 기능중 하나로 프로그래머가 형변환을 할 때 오류를 체크해 줍니다.

이 오류는 코드상에서 체크해줍니다. 마치 없는 변수나 함수를 사용자가 사용할 때 처럼 말이죠.

그럼 예시를 보겠습니다.


1) 일반 변수의 형변환들.

#include <iostream>

int main() {
    double dou = 10.1234;
    int integer_1 = dou;    //묵시적 형변환
    int integer_2 = (int)dou;   //명시적 형변환
    int integer_3 = static_cast<int>(dou); //static_cast 형변환
    printf("%d\n%d\n%d\n", integer_1, integer_2, integer_3);

    return 0;
}


일반적인 변수에서는 모두 부담없이 형변환이 됩니다.

단지 double -> int형으로 넘어 갔기 때문에 뒤에있는 소수점들은 버려진 모습입니다.


2)포인터에서의 형변환들

#include <iostream>

int main() {
    double* dou = new double(10.1234);
    int* integer_1 = dou;    //묵시적 형변환
    int* integer_2 = (int*)dou;   //명시적 형변환
    int* integer_3 = static_cast<int*>(dou); //static_cast 형변환
    
    printf("%d\n%d\n%d\n", integer_1, integer_2, integer_3);

    return 0;
}


int integer_2 부분은 명시적 변환으로 되지만 묵시적변환과 static_cast변환은 안되는 걸 확인할 수 있습니다.

데이터 타입이 맞지 않아서 코드상에서 오류를 출력해 주는 것입니다.


저렇게 명시적 형변환으로 코드를 만들게 된다면 말도 안되는 결과값이 호출되면서 프로그램이 정상작동 하지 않을 것입니다.

저렇게 문제가 있지만 컴파일되어 실행되는 것이 가장 무섭지요..... 버그 찾기도 힘들고....



3) 상속관계에서의 형변환들

#include <iostream>

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() {

    Cat* cat_1 = new Cat("나비");
    Dog* dog_1 = new Dog("멍멍이");
    
    cat_1 = dog_1;  //묵시적 형변환
    cat_1 = (Cat*)dog_1;    //명시적 형변환
    cat_1 = static_cast<Cat*>(dog_1);   //static_cast형변환

    return 0;
}




여기서도 보면 명시적 형변환만 되는걸 확인할 수 있습니다.

그럼 명시적 변환이 됐을 때 코드가 어떻게 작동하는지 보겠습니다.


int main() {

    Cat* cat_1 = new Cat("나비");
    Dog* dog_1 = new Dog("멍멍이");
    
    cat_1 = (Cat*)dog_1;    //명시적 형변환

    cat_1->sound();
    cat_1->only_cat();
    return 0;
}


끔찍한 혼종이 태어났습니다. 클래스가 이상하게 동작하는걸 확인할 수 있습니다.

하지만 static_cast로도 똑같은 오류를 범할 수 있습니다.



4)static_cast 오류

#include <iostream>

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() {

    Animal* ani = new Cat("나비");
    Dog* dog = static_cast<Dog*>(ani);

    dog->sound();
    dog->only_dog();
    return 0;
}



간단히 설명해보자면.

부모 클래스의 Animal의 포인터로 Cat을 인자로 받았습니다.

즉, upcast가 된 것입니다.


그 후 Animal의 포인터 변수 ani를 downcast를 통해 Dog 클래스로 바꿔주고 있습니다.

그 후 끔찍한 혼종이 탄생하게 됩니다.


이러한 오류를 범하게 되는 이유는 2가지 입니다.

1. 상속관계이므로 포인터 관계상 서로 같은 타입으로 인식하게 된다.

2. 컴파일 타임에 RTTI(Run Time Type Information)을 검사하지 않는다.


하지만 static_cast는 이러한 오류검사를 하지 않기 때문에 좀 더 퍼포먼스가 나오긴 합니다.

때문에 일반적인 자료형 형변환에서는 static_cast가 좋지만

상속관계에 있을 때는 dynamic_cast를 추천하는 것입니다.



2. std::move에서의 static_cast

std::move는 static_cast로 설계되어 있습니다. static_cast는 자료형 뿐만 아니라 rvlaue, lvalue를 통한 
변수 이동이 가능해 집니다.


#include <iostream>

using namespace std;

class A {
private:
    string s;
    char* data;

public:
    A() {}
    A(string s) : s(s), data(new char[10000])
    {
        cout << " constructor" << endl;
    }

    A(const A& ref) {
        this->s = ref.s;
        this->data = new char[10000];

        cout << s << " copy constructor" << endl;
    }

    A(A&& ref) {
        this->s = ref.s;
        this->data = ref.data;
        ref.data = nullptr;
        cout << s << " move constructor" << endl;
    }

    ~A() {
        delete[]data;
        cout << " ~constructor" << endl;
    }

    void print() {
        printf("data가 가르키고 있는 주소 값 : %x\n", data);
    }
};


int main(void) {

    A a("a");
 //   A b = a;
    A b = static_cast<A&&>(a);

    a.print();
    b.print();

    return 0;
}

1)A b = a 결과 (얕은 복사)


2) A b = static_cast<A&&>(a) 결과 (깊은 복사)



이렇게 static_cast를 이용해 깊은 복사를 할 수 있습니다.

하지만 이미 정의되어 있는 std::move 연산자를 사용하는 것이 더 편할것입니다.


move와 rvalue, lvalue를 좀 더 자세히 보실려면 아래 링크를 클릭해 주세요.

https://hwan-shell.tistory.com/132?category=703822


반응형

댓글


스킨편집 -> html 편집에서