내돈내고 내가 공부한것을 올리며, 중요한 단원은 저 자신도 곱씹어 볼겸 가겹게 포스팅 하겠습니다.
1) 1장. 연산자 오버로딩
이번 단원에서는 연산자 오버로딩에 대하여 컴파일러가 어떻게 해석하는지를 주로 배웠다. 이전에 따배씨쁠쁠에서 한번 싹 배운내용긴 하지만 복습겸 정독하였고, 또한 함수 호출 연산자 오버로딩을 집중하여 공부하였다.
함수 호출 연산자 오버로딩( () 연산자 )
함수 호출 연산자는 객체를 함수처럼 동작하게 하는 연산자 입니다.
객체를 함수처럼 동작하게 하기 위해서는 호출연산자 "()" 를 정의해줘야 한다.
따배씨쁠쁠에서 배웠던 functor(함수 객체) 를 말한다.
#include <iostream>
using namespace std;
struct FuncObject {
public:
void operator() (int arg) const {
cout << "정수 >> " << arg << endl;
}
};
void Print1(int arg) {
cout << "정수 : " << arg << endl;
}
int main()
{
void (*Print2)(int);
Print2 = Print1; // Print1의 주소전달
FuncObject Print3;
Print1(10); // '함수'를 사용한 정수 출력
Print2(10); // '함수 포인터'를 사용한 정수 출력
Print3(10); // '함수 객체(Functor)'를 사용한 정수 출력, Print3.operator(10)과 같음
return 0;
}
실행 결과로는 다음과 같다.
실행 결과를 보면 3번째 출력은 >> 로 출력된것을 볼 수 있다. 이는 operator() 함수를 사용했다는 증거이다.
즉 FuncObject의 객체인 Print3가 오버로딩 으로 정의된 연산자() 를 사용하는 것 이다.
Print3.operator(10)으로 해석된다.
배열 인덱스 연산자 오버로딩( [] 연산자 )
베열 인덱스 연산자를 오버로딩하면 객체에서도 이를 활용할 수 있다.
#include <iostream>
using namespace std;
class Point {
int m_x;
int m_y;
public:
Point(int x = 0, int y = 0) : m_x(x), m_y(y) {}
void Print() const { cout << m_x << ',' << m_y << endl; }
int operator[] (int idx) const {
if (idx == 0)
return m_x;
else if (idx == 1)
return m_y;
else
throw "예외처리";
}
};
int main()
{
Point pt(1, 2);
try {
cout << pt[0] << ',' << pt[1] << ',' << pt[2] << endl;
}
catch (const char* a)
{
cout << a << endl;
}
return 0;
}
출력 결과는 다음과 같다.
위의 코드에서 pt[0]은 pt.operator[] (0) 을 호출하면서 멤버 변수인 m_x를 반환받는다.
pt[1]도 같은 방식이며, pt[2]는 예외처리 된다.
[] 연산자 오버로딩은 주로 컨테이너 객체에서 사용한다고 저자가 말하고있다.
컨터이너의 관리의 용이성을 위해서 겠지? 라고 생각하고 바로 다음줄을 읽어보니, 저자또한 객체가 관리하는 내부 원소에 접근을 편하게 하기위해 사용한다고 하였다.
- const 객체는 const 멤버함수만 호출할 수 있음을 기억해두자.
메모리 접근, 클래스 멤버 접근 연산자 오버로딩
오버로딩 설명에 앞서 저자가 스마트포인터에 대한 예와 설명을 해주고 있는데, 이는 따배씨쁠쁠에서 다 배운 내용이니 넘어가겠다.
->연산자 오버로딩에 대하여 기록하겠다.
#include <iostream>
using namespace std;
class Point {
int m_x;
int m_y;
public:
Point(int x = 0, int y = 0) : m_x(x), m_y(y) {}
void Print() const {
cout << m_x << ',' << m_y << endl;
}
};
class PointPtr {
Point* m_ptr;
public:
PointPtr(Point* p) : m_ptr(p) {}
~PointPtr() {
delete m_ptr;
}
Point* operator->() const {
return m_ptr;
}
};
int main()
{
PointPtr p1 = new Point(2, 3);
PointPtr p2 = new Point(5, 5);
p1->Print(); // p1.operator->()->Print()
p2->Print(); // p2.operator->()->Print()
// 메모리 자동 소멸
return 0;
}
p1->Print()는 p1.operator->() 함수를 호출하여 return된 m_ptr을 Point*형으로 반환받고, 이 포인터를 사용하여 멤버함수인 Print()함수를 호출합니다.
포인터를 사용한 호출이니까 p1.operator->()->Print()가 됩니다.
이와 같은 방식으로 *(역참조연산자) 또한 오버로딩이 가능하다.
Point& operator* () const {
return *m_ptr;
}
m_ptr의 역참조한 값을 참조형을 통해 반환하고 있다.
당연한 것 이 그냥 Point로 반환하면 값이 복사되기 때문에 이후의 연산은 복사본에 연산을 진행하게 되어 원본에 변화가 없다.
타입 변환 연산자 오버로딩
사용자가 직접 정의하여 사용할 수 있는 타입 변환은 생성자를 이용한 방식과 연산자 오버로딩을 이용한 방식으로 나뉜다.
- 명시적 호출만 가능하도록 하고싶다면 explicit 키워드를 사용하자.
#include <iostream>
using namespace std;
class A {
};
class B {
public:
operator A() {
cout << "operator A" << endl;
return A();
}
operator int() {
cout << "operator int" << endl;
return 10;
}
operator double() {
cout << "operator double()" << endl;
return 5.5;
}
};
int main()
{
A a;
int n;
double d;
B b;
a = b; // b.oprator A() 암시적 호출
n = b; // b.oprator int() 암시적 호출
d = b; // b.oprator double() 암시적 호출
return 0;
}
결과는 다음과 같다.
위의 코드에서 a = b를 보면 컴파일러는 B타입의 객체를 A 타입으로 변환하기 위해 타입 변환 연산자 b.operator A()를 호출한다. 또한 타입변환 연산자는 반환 타입을 지정하지않는다.
1장은 약 50페이지 정도였는데, 그중 페이지수로만 치면 7페이지 정도의 양만 기록해 보았다.
내가 곱씹을 내용들 위주로 기록한 것이며, 더많은 내용을 원한다면 이책을 직접 구매하여 읽어보길 추천한다.
특히 나처럼 STL 사용에 익숙해지고 싶은 사람이라면 말이다.
2) 나의 현황
오늘부터 새로운 책을 읽기 시작했다. 총 12쳅터이니 25일 안에 완독(2월 말까지) 하도록 해보자.
공부는 하나하나 격파하는 재미가 있어야 지치지않고, 기운풀리지않는다.
그러기 위해서는 항상 일정을 타이트하게 잡아야 함을 살면서 많이 느꼈다. 사실 지금보다 더 타이트하게 공부하고자 하면(학교 중간, 기말시즌모드)로 하면 12쳅터 책 쯤이야 7일이면 다 읽겠지만... 방학이니... 미친듯 달리진 말자...
이글의 모든 사진과 내용의 출처는 공동환 님께 있습니다.
'CS > C++' 카테고리의 다른 글
뇌를 자극하는 C++ STL : 3장. 함수 객체 (0) | 2022.01.18 |
---|---|
뇌를 자극하는 C++ STL : 2장. 함수 포인터 (0) | 2022.01.18 |
C++ 공부 섹션20 : 홍정모의 따배씨쁠쁠 <완강> (0) | 2022.01.18 |
C++ 공부 섹션19 Modern C++ : 홍정모의 따배씨쁠쁠 (0) | 2022.01.17 |
C++ 공부 섹션18 입출력 : 홍정모의 따배씨쁠쁠 (0) | 2022.01.17 |
댓글