일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 인간이 그리는 무늬
- 성화봉송주자
- 다음 API
- 성화봉송
- BFS
- 안드로이드 스튜디오
- 창훈쓰다
- MST
- 삼성 코딩테스트
- 비트마스크
- yolo
- BOJ 2098
- boj
- 언어의 온도
- Segment Tree
- 다이나믹 프로그래밍
- 이분탐색
- 백트레킹
- 평창동계올림픽
- 위상정렬
- upper_bound
- 외판원 순회
- lower_bound
- 생활코딩
- 다음 지도 api
- 영어회화 100일의 기적
- DP
- multiset
- 캘리그라피
- 그리디 알고리즘
- Today
- Total
Hoon222y
[1220-3] 첨자 연산자 오버로딩/ 변환 연산자 오버로딩 (explicit) / 변환 생성자 본문
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | //첨자 연산자 오버로딩 #include <iostream> using namespace std; class IntArray{ int* buf; int size; public: IntArray(int sz = 10): size(sz){ buf = new int[size]; } int length() const{ return size; } //아래의 코드가 동작될 수 있도록 함수를 변경하라.(리턴 타입을 int에서 int&로 바꿈) //첨자 연산자를 반환할 때는 참조 타입을 반환해야 하는데 그 이우는 값의 대입(=) 때문이다. int& operator[](int idx){ //이 함수는 그냥 main문을 통해서 arr[i] 출력할 떄인데 return buf[idx]; //아래꺼는 print_arr를 밖에서 사용할 떄 } //상수 객체는 상수 멤버 함수만 호출 가능하므로 위의 첨자 연산자를 사용할 수 없다. //이제 상수 객체용 첨자 연산자 함수를 구현한다. //구현해 보아라. 방법은 두가지 이다. 참조를 리턴하는것이 아니라 //1. const int& 로 하던지 2.그냥 int 로 바꾼다. int& 자체가 상수이기 떄문에 상수에서 상수로 대입이 안됨. //여기서 주의할 점은 참조를 반환하여 값을 수정할 수 있고록 해서는 안된다는 것이다. // 따라서 살수(값)를 반환하도록 해야한다. int operator[](int idx) const { return buf[idx]; } }; void print_arr(const IntArray& arr){ for (int i = 0; i < arr.length(); i++){ cout << arr[i] << " "; }cout << endl; } int main(){ IntArray arr(10); //int arr[10]; for (int i = 0; i < arr.length(); i++){ //arr[i] = 0; //이건 실행되지 않는다. 이것을 해결하는것이 첨자 연산자 오버로딩이다. //arr.operator[](i) = 0; //이렇게 하면 10=0 처럼 상수에 상수 대입이라 에러 //arr.operator[](i) = 0; arr[i] = 0; //이렇게 하기 위해서 함수의 리턴타입을 int& operator[]로 선언한다. } print_arr(arr); } | cs |
변환 연산자 오버로딩 : 변수에 값을 할당할 때 타입이 다른 경우가 있다. 이때 변환을 해주는 연산자를 사용하게 되는데
operator 타입() 을 통하여 사용한다. 이때 주의할 점은 절대 return 타입을 명시하면 안된다는 점이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class Complex{ double r, i; public: Complex(double real, double image) : r(real), i(image){} // 변환 연산자 함수 오버로딩 방법 // operator 타입() // 주의! 단 절대 리턴 타입을 명시하면 안된다. operator double(){ return r; } }; int main(){ double d = 3.14; int i = (int)d; Complex c(1, 1); //1+1i double real = (double)c; //double(c) -> c.operator double(); double real2 = double(c); cout << real << endl; cout << real2 <<endl; } | cs |
여기서 double(c)나 (double)c 나 같이 동작한다. 하지만 8번째 줄처럼 구현하면 문제가 발생할 수 있다. 바로 암시적으로 변환이 되는 경우가 발생하기 때문이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | class Complex{ double r, i; public: Complex(double real, double image) : r(real), i(image){} // 변환 연산자 함수 오버로딩 방법 // operator 타입() // 주의! 단 절대 리턴 타입을 명시하면 안된다. operator double(){ return r; } }; int main(){ double d = 3.14; int i = (int)d; Complex c(1, 1); //1+1i double real = (double)c; //double(c) -> c.operator double(); double real2 = double(c); cout << real << endl; cout << real2 <<endl; } #endif #if 0 #include <iostream> using namespace std; class Complex{ double r, i; public: Complex(double real, double image) : r(real), i(image){} /* //operator double(){ return r; } //이걸 지우고 //변환 연산자 대신 변환을 위한 인터페이스를 제공한다. //변환 연산자 사용하지 말고 왠만하면 변환 함수를 쓰는것이 더 낫다. //하지만 double asReal(){ return r; } //이건 사용자가 회피한거지 근본적 해결책이 아님 */ //explicit 키워드는 현재 변환 연산자를 명시적 변환에만 사용하고 //암시적 변환에서는 사용하지 않겠다는 키워드이다. explicit operator double(){ return r; } }; void print_double(double d){ cout << d << endl; } int main(){ Complex c(1, 1); //print_double(c); //이게 원래 double을 인자로 받는데 실행이 된다. 왜일까 ? //operator double(){ return r; }이걸 통해서 된 것 작동 //변환 연산자 암시적 변환에 사용! //해당코드는 만약에 explicit으로 선언될 경우 암시적 변환이 안되기 떄문에 에러 발생. //cout << c.asReal() << endl; print_double(double(c)); //explicit을 사용하여 암시적 변환에 사용하지 않고 명시적 변환에서만 사용 한다고 print_double((double)c); //둘다 가능. } | cs |
52번째 줄에서 print_double 의 경우 인자로 double을 받는데, Complex 객체를 입력 받아도 작동이 된다. 이것은 operator double(){ return r; } 의 수행으로 인하여 컴파일러 내에서 암시적으로 변환이 된 것이다. 이러한 문제를 피하기 위해서 변환 연산자가 아닌 변환 함수를 사용하묜 된다. 하지만 이것은 사용자가 피한것이지 근본적인 해결책이 되지는 않는다. 따라서 이 때 사용할 수 있는것이 바로 explicit 키워드이다.
explicit 키워드 : 현재 변환 연산자를 명시적 변환에서만 사용하고, 암시적 변환에 사용하지 않겠다는 뜻이다. 이 때 사용하기 위해서는 46번째 줄에서 매개변수가 double 로 들어가는데 Complex 클래스 내에서 operator double 변환 연산자를 통하여 return r이 되도록 하는것이다. 이때 double(c) 나 (double)c 둘다 똑같이 작동한다.
변환 생성자
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | //변환 생성자 //인자 1개를 사용하여 생성자를 호출할 수 있는 생성자는 모두 변환생성자가 될 수 있다. #include <iostream> using namespace std; //결론 ! 변환생성자와 연산자를 사용할 때는 가급적 explicit을 사용하자 class Int32{ public: int val; // 아래와 같은 생성자가 암묵적으로 사용되지 않도록 하려면 생성자의 앞에 explicit 키워드를 사용하면 된다. explicit Int32(int v = 0) :val(v){ cout << "int32(" << v << ")" << endl; } }; void print_int32(Int32 i){ cout << i.val << endl; } int main(){ Int32 i(10); print_int32(i); int a = 20; //print_int32(a); //이게 되는것을 의도한것은 아닌데 된다. //컴파일러가 생성자를 print_int32(Int32(a)) 이렇게 해서 넘겨준 것이다. 그래서 동작함. //결과값을 수행해보면 이때 생성자가 발생하는 것을 볼 수 있다. //생성자에 explicit 쓰면 이러한 암시적 변환을 막을 수 있다. Int32 i2(10); // Int32 j2 = 10; //Int32 j = Int32(10; -> Int32j(Int32(10)) //explicit 으로 막아서 작동을 안하게 된다. } | cs |
해당 코드들을 보면 22번 째 줄에서 print_init32(a)가 암시적 형변환으로 실행이 된다. 인자로 들어간 10이 10번째 줄에서 변환생성자가 되어 인자로 Int32 타입으로 형변환이 되는것이다. 이러한 암시적 형변환을 막기 위해서 explicit을 선언한다. 28번째 줄 또한 10이 변환 생성자로 인하여 Int32 타입으로 자동 형변환 되어 사용되는 것이다. 이 모든것을 막기위해 explicit을 선언한다. 변환 생성자와 연산자를 사용할 경우에는 가급적 explicit을 사용한다. !!
'코딩 > 교육' 카테고리의 다른 글
[1220-5] 상속 / 오버라이딩 / 바인딩 / 업케스팅 (0) | 2018.12.20 |
---|---|
[1220-4] 대입 연산자 오버로딩 / 비교 연산자 오버로딩 (0) | 2018.12.20 |
[1220-2] 연산자 오버로딩 / 임시 객체 / 출력 연산자 오버로딩 (0) | 2018.12.20 |
[1220-1] friend 키워드 (0) | 2018.12.20 |
[1219-5] 상수 멤버 함수 / mutable (0) | 2018.12.20 |