C++ 자동변환과 클래스의 자료형 변환(변환 함수)

프로그래밍/C++ 2019.10.17 댓글 Plorence

C++은 표준 자료형의 값이 다른 표준 자료형과 호환이 될 때 암시적 형 변환이 이루어집니다.

반면에 호환되지 않은 자료형은 변환하지 않습니다.

자동으로 자료형이 변환되지 않는 경우에 명시적 형변환을 이용하면 해결할 수 있습니다.

int * ptr = (int*)10;

기본 자료형이나 다른 어떤 클래스와 충분히 관련되어 있어, 서로 간에 변환할 수 있는 클래스를 정의할 수 있습니다.

그러한 경우에, 자동 변환을 할 것인지 강제 변환을 할 것인지 사용자가 C++에게 지시할 수 있습니다.

 

C++에서 하나의 매개변수를 취하는 생성자는 그 매개변수 데이터형의 값을 클래스형으로 변환하는 설계도처럼 동작합니다.

Person(int p_age);

위 생성자는 int형 값을 Person형 값으로 변환하는 구문 역할을 합니다.

프로그램은 해당 생성자를 사용하여 임시 객체를 생성하고, 매개변수 p_age의 값을 초기화 값으로 사용합니다.

그러고 나서 멤버별 대입으로 임시 객체의 내용을 대입하는 객체로 복사됩니다.

#include <iostream>
class Person {
private:
       int age;
public:
       Person(int p_age) {
              age = p_age;
       }
};
int main(void) {
       Person per = 11;
       return 0;
}

이 과정은 명시적 형변환이 필요 없이 자동으로 이루어지므로 암시적(묵시적) 형 변환이라고 합니다.

하나의 매개변수만 사용할 수 있는 생성자만이 이와 같은 변환 생성자로 동작합니다.

 

하지만 이와 같은 방법은 예상치 못한 부작용을 일으킬 수 있습니다.

그래서 자동 데이터형 변환을 막기 위한 키워드 explicit 이라는게 새롭게 추가되었습니다.

#include <iostream>
class Person {
private:
       int age;
public:
       explicit Person(int p_age) {
              age = p_age;
       }
};
int main(void) {
       Person per1 = 11; //불가능
       Person per2 = Person(11);
       Person per3 = (Person)11;
       return 0;
}

explicit 키워드 때문에 per = 1;와 같은방법으로 못하게 되었습니다.

이렇게 예상치 못한 부작용을 막아줍니다.

즉 명시적 데이터형 변환만 가능합니다.

 

그럼 생성자는 언제 호출되는가?

  • Person 객체를 int형 값으로 초기화할 때

  • Person 객체에 int형 값을 대입할 때

  • Person형 매개변수를 기대하는 함수에 int형으로 전달할 때

  • int형값을 리턴하도록 선언된 함수가 int형 값을 리턴할 때

  • int형으로 암시적 형 변환이 가능한 내장 데이터형을 사용할 때

5번은 매개변수가 double형이고 int형이 대입될 경우 int형은 double형으로 암시적 데이터형 변환이 가능하니 double형으로 변환하고 호출됩니다. 

 

변환 연산자

이때까지 어떤 데이터형에서 객체로 변환했지만 이번엔 반대로 객체에서 어떤 데이터형으로 변환하기 위해 있는 것이 변환 함수입니다.

#include <iostream>
class Person {
private:
       int age;
public:
        Person(int p_age) {
              age = p_age;
       }
};
int main(void) {
    Person per = Person(19);
    int age = per; //컴파일 에러
}

컴파일 에러가 발생합니다.

Person 객체에서 int로 변환하는 적절한 변환 함수가 없기 때문입니다.

 

변환 함수 작성 방법

operator typeName();

변환 함수에는 규칙이 있습니다.

  • 변환 함수는 메서드여야 합니다.

  • 변환 함수는 리턴형을 가지면 안 됩니다.

  • 변환 함수는 매개변수를 가지면 안 됩니다.

#include <iostream>
class Person {
private:
       int age;
public:
        Person(int p_age) {
              age = p_age;
       }
        operator int() {
               return age;
        }
};
int main(void) {
       Person per1 = 11;
       int age = per1; //가능
       return 0;
}

객체를 int형으로 변환하는 변환 함수를 선언하고 정의하였습니다.

int age = per1; //가능

그래서 위와 같은 코드는 문제가 없습니다.

 

변환 함수의 모호함

객체에서 변환 함수가 여러 개가 정의되어 있고 그것을 cout 같은 여러 가지 자료형을 지원하는 것에서 변환 함수의 모호함이 발생합니다.

왜냐하면 cout은 int형, double형, float형, long형 등 여러 가지 자료형에 대해 출력이 가능하기 때문입니다.

즉 모호함은 여러 개의 변환 함수가 정의되어 있을 때 나타나는 문제지, 1개의 변환 함수만 정의했다면 모호하다고 컴파일 에러가 발생하지 않습니다.

이럴 때는 명시적 형 변환을 사용해야 합니다.

#include <iostream>
class Person {
private:
       int age;
public:
        Person(int p_age) {
              age = p_age;
       }
        operator int() {
               return age;
        }
        operator double() {
               return double(age);
        }
        operator long() {
               return long(age);
        }
};
int main(void) {
       Person per1 = 11;
       std::cout << per1; //컴파일 에러
       return 0;
}

이렇게 모호해질 때는 명시적 데이터형 변환이 반드시 필요합니다.

만약 모호하지는 않지만 변환 함수가 explicit으로 선언될 경우에는 반드시 명시적 데이터형 변환이 필요합니다.

#include <iostream>
class Person {
private:
       int age;
public:
        Person(int p_age) {
              age = p_age;
       }
        explicit operator int() {
               return age;
        }
        explicit operator double() {
               return double(age);
        }
        explicit operator long() {
               return long(age);
        }
};
int main(void) {
       Person per1 = 11;
       int a = per1; //컴파일 에러
       std::cout << int(per1); //문제없음
       return 0;
}

 

댓글