일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 언어의 온도
- 백트레킹
- Segment Tree
- 캘리그라피
- upper_bound
- 다음 지도 api
- BFS
- 생활코딩
- 영어회화 100일의 기적
- 위상정렬
- yolo
- 성화봉송주자
- 비트마스크
- lower_bound
- 안드로이드 스튜디오
- 평창동계올림픽
- 이분탐색
- 성화봉송
- BOJ 2098
- 삼성 코딩테스트
- 그리디 알고리즘
- DP
- multiset
- 다음 API
- boj
- MST
- 다이나믹 프로그래밍
- 인간이 그리는 무늬
- 외판원 순회
- 창훈쓰다
- Today
- Total
Hoon222y
[1220-2] 연산자 오버로딩 / 임시 객체 / 출력 연산자 오버로딩 본문
연산자 오버로딩 : 클래스에 대한 연산자를 정의하여 t3 = t2+t1; 처럼 객체끼리 연산할 수 있도록 만드는 문법이다.
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 | #include <iostream> using namespace std; class Int32{ int v; public: Int32(int val = 0) : v(val){} void print() const{ cout << v << endl; } int get() const{ return v; } Int32 operator+(const Int32& o){ Int32 temp(v + o.v); return temp; } }; Int32 add(const Int32& i, const Int32& j){ Int32 temp(i.get() + j.get()); return temp; } int main(){ Int32 i(10); // == Int32 i = 10; Int32 j(20); Int32 k = i + j; //이걸 연산자 오버로딩한다. //i.operator+(j); 자기자신과 동일한 타입을 받는다. //Int32 k = add(i,j); k.print(); Int32 x = i.operator+(j);//이런식으로 명시적으로 호출 또한 가능하다. x.print(); } | cs |
코드를 보면서 이야기 하자. 원래라면 25번째 줄처럼 연산을 하기 위해서는 + 기호를 사용하는 것이 편하다. 하지만 +라는 기호가 각각의 클래스마다 어떻게 더해질 지 모르기 때문에 사용자가 해당 연산자에 대하여 재정의 하는것이다.
i와 j를 더하는 방법은 1. 16~18줄 처럼 클래스 내부에 멤버 변수 값을 가져오는 함수를 정의하고, 임시 temp 변수를 만들고 해당 변수를 return 하는 방법과 2. + 연산자에 대해 재정의 하는 방법이 있다. 연산자 오버로딩의 경우 리턴타입 함수 이름([매개변수,..]) 로 구성이 되어있다.
이 때 살펴볼 점은 25번 째 줄처럼 +연산자 오버로딩을 사용하여 정의할 수도 있고, 29번째 줄처럼 명시적으로 호출이 가능하다는점이다. 연산자 오버로딩은 멤버 함수 뿐만 아니라 비멤버 함수로도 구현이 가능하다. 따라서
1 2 3 4 5 6 7 8 | //비멤버 버전의 연산자 오버로딩을 좀 더 쉽게 구현하기 위해 friend friend Int32 operator+(const Int32& i, const Int32& j); //class내부에 선언 후 Int32 operator+(const Int32& i, const Int32& j){ Int32 temp(i.v + j.v); return temp; } |
해당 코드처럼 class 내부에 friend 키워드를 붙이고 멤버 변수를 직접적으로 사용할 수 도 있다. 비멤버 버전의 연산자 함수가 필요한 경우는 여러가지가 있지만 대표적으로는 교환법칙을 지원하기 위해서다.
참고적으로 연산자 함수도 오버로딩이 가능하다.
1 2 3 4 5 | Int32 x = i + 10; // i.operator+(10) x.print(); Int32 y = 10 + i; y.print(); | cs |
해당 경우처럼 i+ 10 , 10 + i에 대한 교환법칙을 성립시키기 위해서
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | Int32 operator+(const Int32& o){ //Int32 temp(v + o.v); //5번 코드 보고 이후에 주석처리 하였음 //return temp; return Int32(v + o.v); } //연산자 함수도 오버로딩이 가능하다. Int32 operator+(int i){ //Int32 temp(v + i); //return temp; return Int32(v + i); } friend Int32 operator+(int i, const Int32& j); }; Int32 operator+(int i, const Int32& j){ //Int32 temp(i + j.v); //return temp; return Int32(i + j.v); } | cs |
해당처럼 각각의 경우에 대해 각각의 연산자 오버로딩을 가능하게 한다.
지역 객체 : 이름이 있으며, 스텍이 파괴될 때 삭제된다.
임시 객체 : 이름이 없으며, 명령이 끝날 때 삭제된다.
1 2 3 4 5 6 7 8 9 10 11 | int main(){ Int32 i(10); //지역 객체: 이름이 있음, 스텍이 파괴될 때 삭제됨 Int32(20); //임시 객체: 이름이 없음, 명령이 끝날 때 삭제됨 getchar(); int i1 = 10; int j1 = 20; //임시 객체는 컴파일러의 필요에 의해 생성되는 개념이다. int k = i1 + j1; //여기서 i,j가 임시객체라고 볼 수 있다. //int temp = i+j; int k = temp; delete temp;랑 같은 것이다. } | cs |
해당 코드와 같이 9번째 줄에서 볼 수 있듯 연산이 진행되면 temp 임시변수가 발생할 수 있다. 따라서 어떤 함수 내에서 자신의 타입을 만들자 마자 반환하게 된다면 임시객체를 생성해서 반환하는 것이 좋다. 지역 객체를 생성하면 임시 객체가 생성되고, 값을 복사한 뒤에 파괴되기 때문에 성능상에 오버헤드가 발생하기 때문이다.따라서
1 2 3 4 5 6 7 8 | Int32 operator+(const Int32& o){ //Int32 temp(v + o.v); //return temp; //위에 주석처리한것처럼 해도 되는데 아래처럼 선언하면 return Int32(v + o.v); //라고 하면 임시객체를 만들자마자 리턴한다. } //RVO(Return value optimazation) 이라고 한다. //최신의 컴파일러는 최적화를 통하여 임시 객체를 사용하지 않고도 RVO를 수행하는데 //이를 NRVO(named Return value optimazation)라고 한다. | cs |
처럼 바로 임시객체를 생성한 후 반환한다. 이를 RVO라고 하고, 최신 컴파일러의 경우 임시 객체를 생성하지 않고도 RVO를 수행하는데 이를 NRVO라고 한다.
출력 연산자 오버로딩 : <<
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 | class Person{ char name[32]; public: Person(const char* name) { strcpy(this->name, name); } friend ostream& operator<<(ostream& cout, const Person& o); }; //namespace std{ // class ostream{}; // ostream cout; //} 이므로 아래에서 ostream& cout 이다. //여기서 name을 사용하기 위해서 방법 2가지 //1. 친구를 한다. 2. class 내부에서 get name함수를 만들어서 가져온다. //출력 연산자를 구현하는 방법 //1. 아래 함수를 구현한다. //2. 그리고 cout 객체를 반환하도록 해야한다. 자기자신을 리턴하여 다양한 타입에 대해서 연쇄적 출력을 위하여 ostream& operator<<(ostream& c, const Person& o){ c << o.name <<endl; return c; } int main(){ Person p("hoon"); cout << p; //원래 void operator<<(ostream& cout, const Person& o)일 떄는 //cout << p 만 되고 cout<< p << endl;가 안된다. //하기 위해서는 return 타임을 osream&로 해서연속으로 호출이 되게 한다. cout << p << endl; //cout.operator<<(p)랑 같은 것이다. 원래는 위에것만 사용 가능하지만 // ostream& 을 해서 문제를 해결하였다. //cout 안에는 operator << 이있긴 하지만 그건 Person 타입이 아니다. //맴버 함수가 없기때문에 비맴버 함수를 찾는다. -> operator<<(cout,p) } | cs |
출력 연산자 오버로딩의 경우 주의할 점은
1. 연속적인 호출을 하기 위해서 return 타입이 ostream& 이라는 점(이를 통해서 <<endl; 이 가능해짐 )
2. 안에 매개변수로 들어가는 것이 (ostream& c, 클래스& 변수명)
3. 클래스 안에 들어가는 변수에 접근하고 싶다면 friend 키워드 선언 이다.
'코딩 > 교육' 카테고리의 다른 글
[1220-4] 대입 연산자 오버로딩 / 비교 연산자 오버로딩 (0) | 2018.12.20 |
---|---|
[1220-3] 첨자 연산자 오버로딩/ 변환 연산자 오버로딩 (explicit) / 변환 생성자 (0) | 2018.12.20 |
[1220-1] friend 키워드 (0) | 2018.12.20 |
[1219-5] 상수 멤버 함수 / mutable (0) | 2018.12.20 |
[1219-4] 유일한 객체를 선언하는 방법 (1) | 2018.12.19 |