◆연산자 오버로딩
- 연산자를 오버로딩하는것을 뜻한다.
- int의 덧셈과 string의 덧셈은 구체적으로 다르나 이를 피연산자로 구분하는 오버로딩을 사용함으로 쓰기 용이하게 해준다.
●연산자 함수의 형식은 다음과 같다.
operatorop(argument-list) //op는 오버로딩할 연산자를 나타내는 기호이다.
operator+()
- op는 오버로딩할 연산자를 나타내는 기호이다.
- 단, op는 적법한 C++ 연산자여야 한다.
- @와 같은 없는 연산자를 넣으면 안된다.
◆Price 라는 클래스를 가지고, a와 b가 모두 Price 이라면
Price c = a + b; //연산자 표기
Price c = a.operator+(b); //함수 표기
◆연산자 오버로딩 선언시 형식
class Time
{
public:
Time operator+(const Time & t) const;
}
- 매개변수가 const 참조인 이유는 복사를 줄이고 원본값을 바꾸지 않기 위해서
- 오버로딩에 const가 있는 이유는 모르겠다.
◆연산자 오버로딩 순서
Price p4 = p1 + p2 + p3;
Price p4 = p1.operator+(p2 + p3);
Price p4 = p1.operator+(p2.operator+(p3));
◆오버로딩 제약
- 오버로딩된 연산자들이 반드시 멤버 함수일 필요는 없으나, 피연산자 중 하나는 사용자 정의 데이터 형이여야 한다.
- 오버로딩된 연산자를, 오리지널 연산자에 적용되는 문법 규칙을 위반하는 방식으로 사용할 수 없다.
- 연산자 기호를 새로 만들 수 없다.
◆다음과 같은 연산자들은 오버로딩할 수 없다.
연산자 | 설명 |
sizeof | sizeof 연산자 |
. | 멤버 연산자 |
.* | 멤버 지시 포인터 연산자 |
:: | 사용 범위 결정 연산자 |
?: | 조건 연산자 |
typeid | RTTI 연산자 |
const_cast | 데이터형 변환 연산자 |
dynamic_cast | 데이터형 변환 연산자 |
reinterpret_cast | 데이터형 변환 연산자 |
static_cast | 데이터형 변환 연산자 |
◆오버로딩하는 데 멤버 함수만 사용할 수 있다.
연산자 | 설명 |
= | 대입 연산자 |
() | 함수 호출 연산자 |
[] | 배열 인덱스 연산자 |
-> | 클래스 멤버 접근 포인터 연산자 |
◆프렌드
- 멤버함수가 아니지만 private에 접근해야 되는 일이 있다. 이를 위해 프렌드를 사용한다.
- 프렌드 함수, 프렌드 클래스, 프렌드 멤버 함수가 있다.
◆프렌드 함수가 필요한 이유
- 이항 연산자의 경우 서로 다른 두 객체를 연산할때, 매개변수의 선언 순서에 따라 오류가 날 수 있다. (올바르지 못한 위치에 존재할 수 있다.)
◆프렌드 함수 생성하기
1.클래스 선언에 선언을 넣기
friend Time operator*(double m, const Time & c);
- operator*() 함수는 클래스 선언 안에 선언되지만 멤버 함수가 아니다.
- operator*() 함수는 외부 함수지만, 클래스와 동등한 접근 권한을 가진다.
2. 클래스 밖에서 함수 정의 작성
Time operator*(double m, const Time & t){
Time result;
long totalminutes = t.hours * mult *60 + t.minutes * mult;
result.hours = totalminutes / 60;
result.minutes = totalminutes % 60;
return result;
}
A = 2.75 * B;
A = operator*(2.75, B);
- 정의 시, friend는 작성하지 않아도 된다.
- 또한 사용자 정의 타입의 소유가 아니기 때문에, 멤버 변수는 범위 지정 연산자를 통해 사용하여야 한다.
2-1. 곱셈에서 어떤 값이 먼저 나오도록 정의를 고치는 용도의 프렌드 함수
Time operator*(double m, const Time & t){
result t * m;
}
A = 2.75 * B;
A = operator*(2.75, B);
A = B * 2.75; //오버로딩 연산자와 같이 double값이 뒤에있어 정상적으로 작동한다.
◆프렌드 : <<연산자의 오버로딩
- Time 클래스에서 cout을 사용하려면, 프렌드 함수를 사용해야 한다.
- 왜냐하면 cout << time의 첫번째 피연산자가 cout이기 때문에 이를 바꿔야 하기 때문이다.
●<<연산자 오버로딩 코드
void operater<<(ostream & os, const Time & t)
{
os << t.houts << "시간, " << t.minutes << "분";
}
- 이제 cout << trip을 사용하면 "3시간, 32분" 과 같은 형식으로 출력된다.
- 여기서 ostream은 객체로서 사용할뿐 ostream클래스를 건들진 않으므로 Time<<만의 프렌드 함수라고 할 수 있다.
- cout은 객체의 복사본이 아니라 그 자체를 사용해야 하기 때문에 참조로 전달하였다.
★◆오버로딩 <<의 두 번째 버전
- 위의 코드의 경우 cout << trip << "game"; 구문을 실행할 수 없다. cout << trip 이 연산을 마치면 << "game"만 남기 때문인데. 이를 해결하기 위해서 operator<<() 함수가 ostream 객체에 대한 참조를 리턴하도록 수정하면 된다.
ostream operater<<(ostream & os, const Time & t)
{
os << t.houts << "시간, " << t.minutes << "분";
return os;
}
◆오버로딩 연산자 : 멤버 함수와 멤버가 아닌 함수
- 오버로딩 연산자 함수의 멤버가 아닌 버전은, 연산자가 요구하는 수만큼의 형식 매개변수를 요구한다.
- 같은 연산자 함수의 멤버 버전은, 하나의 피연산자가 암시적으로 호출한 객체로 전달되기 때문에, 매개변수를 하나 적게 요구한다.
T1 = T2 + T3;
T1 = T2.operator+(T3); //멤버 함수(this로 T2 사용)
T1 = operator+(T2,T3); //멤버가 아닌 함수
◆생성자의 매개변수가 하나 혹은 나머지가 디폴트 값을 가질때
- 대입 연산자를 통해 암시적 형변화 초기화를 할 수 있다.
Cat(int age);
Dot(int age, char name="no_name");
Cat red;
red = 12; //Cat(int)를 사용하여 12를 Cat으로 변환
Dog yellow = 1; //Dog(int)를 사용하여 12를 Cat으로 변환, name="no_name"
●이를 막기 위해 explicit Cat(int age); 를 써서 암시적 데이터 변환을 막을 수 있다.
explicit Cat(int age);
Cat red;
red = 12; //불가능
red = (Cat)12; //가능
◆하나의 매개변수를 사용하는 생성자는, 그 매개변수의 데이터형을 클래스형으로 변환하는 것을 정의한다. 그 생성자가 키워드 explicit로 제한된 경우에는, 명시적 데이터형 변환만 할 수 있다. 그렇지 않은 경우에는 암시적 데이터형 변환도 할 수 있다.
◆암시적 형변화가 일어나는 경우(기초 자료형 -> 객체)
- 객체를 기초 자료형 값으로 초기화할 때
- 객체에 기초 자료형 값을 대입할 때
- 객체형 매개변수를 기대하는 함수에 기초 자료형 값을 전달할 때
- 객체형 값을 리턴하도록 선언된 함수가 기초 자료형 값을 리턴하려고 시도할 때
- 앞의 네 상황에서 기초 자료형 대신 모호하지 않게 기초 자료형으로 변환할 수 있는 내장 데이터형을 사용할 때
- ※ int의 경우 long으로도 double으로도 형변환이 가능해 둘다 선언되있다면 컴파일러가 동작을 거부한다.
◆변환 함수(conversion function)
- 클래스를 기초 자료형으로 바꾸어준다.
- 사용자 정의 강제 데이터형 변환이다.
- 일반적인 강제 데이터형 변환(type cast)처럼 사용하면 된다.
●변환함수 작성 코드
operator typeNmae();
●변환함수 사용시 주의사항
- 변환 함수는 클래스의 메서드여야 한다.
- 변환 함수는 리턴형을 가지면 안 된다.
- 변환 함수는 매개변수를 가지면 안 된다.
class Time{
...
explicit operator int() const;
explicit operator double() const;
}
- ▷▷C++11에서 사용자는 explicit을 변환 연산자로 선언할 수 있다.
- 암시적 형변환이 막혔다.
◆요약
- 하나의 매개변수를 사용하는 클래스 생성자는, 그 매개변수의 데이터형을 클래스형으로 변환하는 명령어 역할을 한다. 예를 들어, 하나의 int형 매개변수를 사용하는 Cat 클래스 생성자는, int형 값을 Cat 객체로 자동으로 변환한다. 그러나 생성자 선언에 explicit 키워드를 사용하면, 암시적 변환은 없어지고 명시적 변환만 가능하게 된다.
- 변환 함수라고 부르는 특별한 클래스 멤버 연산자 함수는, 클래스 객체를 다른 어떤 데이터형으로 변환하는 명령어 역할을 한다. 이 변환 함수는 리턴형을 선언하지 않고 매개변수도 사용하지 않는다. 객체를 typeName형으로 변환하려면, 그 변환 함수를 operator typeName() 형식의 클래스 멤버로 선언한다. 객체를 어떤 데이터형의 변수에 대입하면, 그 변수의 데이터형에 해당하는 변환 함수가 동으로 호출된다.
◆변환과 프렌드
- 프렌드 정의의 중요점은 암시적으로 전달하는 객체의 자리에 기본 자료형이 오게된다면 컴파일러가 이를 이해 하지 못하기에 중요하다.
◆구현의 선택
- operator+(const Stonewt &, const Stonewt &)를 프렌드 함수로 정의하고, Stonewt (double) 생성자를 사용하여 double형 매개변수를 Stonewt형 매개변수로 변환하는 것이다.
operator+(const Stonewt &, const Stonewt &);
- 기초 자료형 매개변수를 명시적으로 사용하는 함수들로 덧셈 연산자를 한 번 더 오버로딩하는 것이다.(프렌드 오버로딩)
Stonewt operator+(double x);
friend Stonewt operator+(double x, Stonewt & s);
total = (Stonewt)jennySt + (double)kennyD; // Stonewt operator+(double x)
total = (double)kennyD + (Stonewt)ennySt; // friend Stonewt operator+(double x, Stonewt & s)
'C++ > C++' 카테고리의 다른 글
Overloading (0) | 2023.07.28 |
---|---|
12. 클래스와 동적 메모리 대입, +추가 필요 (0) | 2023.07.25 |
10. 객체와 클래스 (0) | 2023.07.17 |
9. 메모리 모델과 이름 공간 (0) | 2023.07.14 |
8. 함수의 활용 ★ (0) | 2023.07.12 |