※ 제가 개인적으로 공부하는 것이라 요약하거나 책에서 빠진 내용이 있을 수 있습니다 ※
Section 6. STL에 필요한 주요 연산자 오버로딩
함수 호출 연산자 오버로딩은 객체를 함수처럼 동작하게 하는 연산자입니다. C++에서 Print(10)이라는 함수 호출 문장은 다음 세 가지로 해석할 수 있습니다.
1. 함수 호출 : Print가 함수 이름
2. 함수 포인터 : Print가 함수 포인터
3. 함수 객체 : Print가 함수 객체
struct FuncObject
{
public:
void operator() (int arg) const
{
cout << "정수: " << arg << endl;
}
};
void Print1(int arg)
{
cout << "정수 : " << arg << endl;
}
int main()
{
void (*Print2)(int) = Print1;
FuncObject Print3;
Print1(10); // 1. 함수를 이용한 출력
Print2(10); // 2. 함수 포인터 이용 출력
Print3(10); // 3. 함수 객체를 사용한 정수 출력 (Print3.operator(10))
}
예제 1-17 정수를 저장하는 간단한 Array 클래스
class Array
{
int *arr;
int size;
int capacity;
public:
Array(int cap=100) : arr(0), size(0), capacity(cap)
{
arr = new int[capacity];
}
~Array()
{
delete [] arr;
}
void Add(int data)
{
if(size < capacity)
arr[size++] = data;
}
int Size() const
{
return size;
}
int operator[] (int idx) const
{
return arr[idx];
}
};
int main()
{
Array ar(10);
ar.Add(10);
ar.Add(20);
ar.Add(30);
for(int i=0; i<ar.Size(); i++)
cout << ar[i] << endl; // ar.operator[] (i)와 같다
return 0;
}
[] 연산자 오버로딩은 ar[i] = 10과 같은 쓰기 연산도 가능해야 하므로 operator[]() 함수는 const함수와 비 const 함수 모두를 제공해야 한다.
...
int operator[] (int idx) const
{
return arr[idx];
}
int& operator[](int idx)
{
return arr[idx];
}
};
메모리 접근, 클래스 멤버 접근 연산자 오버로딩 (*, -> 연산자)
*, -> 연산자는 스마트 포인터나 반복자 (iterator) 등의 특수한 객체에 사용.
PointPtr 클래스의 소멸자를 이용해 동적으로 할당된 Point 객체(heap 객체)를 자동으로 제거
class Point
{
int x;
int y;
public:
Point(int _x = 0, int _y = 0) : x(_x), y(_y) { }
void Print() const {cout << x << ',' << y << endl; }
};
class PointPtr
{
Point* ptr;
public:
PointPtr(Point *p) : ptr(p) { }
~PointPtr()
{
delete ptr;
}
Point* operator->() const
{
return ptr;
}
Point& operator*() const
{
return *ptr;
}
};
int main()
{
Point* p1 = new Point(2, 3); // 일반 포인터
PointPtr p2 = new Point(5, 5); // 스마트 포인터
p1->Print(); // p1->Print() 호출
p2->Print(); // p2.operator->()->Print() 호출
(*p1).Print(); // (*p1).Print() 호출
(*p2).Print(); // p2.operator*().Print() 호출
delete p1;
// p2의 소멸자에서 자동 해제
return 0;
}
Section 7. 타입 변환 연산자 오버로딩
타입 변환은 두 가지
1. 생성자를 이용한 타입 변환
2. 타입 변환 연산자 오버로딩을 이용한 타입 변환
생성자를 이용한 타입 변환
Point 객체에 정수 대입? 가능 -> 생성자를 이용한 타입 변환 중 암시적 생성자 호출
Point pt;
pt = 10; // Point(10, 0) 암시적 생성자 호출
이렇게 실수로 정수를 대입해도 컴파일이 성공해서 의도치 않은 버그로 연결될 수 있어서 의도하지 않는다면 생성자는 명시적 호출만 가능하도록 explicit 키워드를 지정.
...
explicit Point(int _x =0, int _y = 0) : x(_x), y(_y) { }
...
};
int main()
{
Point pt;
// pt = 10; -> 오류, 암시적 생성자 호출 불가능
pt = Point(10); // 명시적 생성자 호출만 가능
}
타입 변환 연산자 오버로딩을 이용한 타입 변환
예시 : Point 클래스의 int 타입 변환 연산자
class Point
{
int x;
int y;
...
operator int() const
{
return x;
}
};
int main()
{
int n = 10;
Point pt(2, 3);
n = pt;
cout << n << endl; // 2 출력
}
1장 연습문제
1.4 다음이 컴파일될 수 있게 최소한의 String 클래스를 작성하세요.
// Q.
String s("Hello!");
const char* sz = s;
// A.
#include <iostream>
#include <cstring>
using namespace std;
class String
{
char buf[100];
public:
String(const char* sz)
{
strcpy(buf, sz);
}
// 핵심은 여기 -> String 타입을 const char* 타입 변환을 위해
// operator const char* () const 구현
operator const char* () const
{
return buf;
}
};
int main()
{
...
}
1.5 다음이 컴파일될 수 있게 최소한의 String 클래스를 작성하세요.
// Q.
const char* sz = "Hello!";
String s("Hi~!");
s = sz;
// A.
// 방법 1
// String 클래스의 = 연산은 얕은 복사(shallow copy)로 암묵적인 생성자를 이용한 방법
#include <iostream>
#include <cstring>
using namespace std;
class String
{
char buf[100];
public:
String(const char* sz)
{
strcpy(buf, sz);
}
};
// A
// 방법 2
// 깊은 복사
#include <iostream>
#include <cstring>
using namespace std;
class String
{
char buf[100];
public:
String(const char* sz)
{
buf = new char[strlen(sz) + 1];
}
~String()
{
delete [] buf;
}
const String& operator=(const String& arg)
{
delete[] buf;
buf = new char[strlen(arg.buf) + 1];
strcpy(buf, arg.buf);
return *this;
}
};
'C++ > C++ STL' 카테고리의 다른 글
C++ STL (뇌를 자극하는) - 4장. 함수 템플릿 (Section 2 ~ 3) (0) | 2025.07.28 |
---|---|
C++ STL (뇌를 자극하는) - 4장. 함수 템플릿 (Section 1) (2) | 2025.07.27 |
C++ STL (뇌를 자극하는) - 3장. 함수 객체란 (1) | 2025.07.25 |
C++ STL (뇌를 자극하는) - 2장. 함수 포인터 (2) | 2025.07.24 |
C++ STL (뇌를 자극하는) - 1장. 연산자 오버로딩 (0) | 2025.07.08 |