1. 연산자 오버로딩
<<, +, []
위와 같은 연산을 오버로딩을 통해 내가 임의로 진행할 수 있다.
예제로 살펴보자.
만약 단순히 3 + 5를 진행한다면 8이라는 값을 얻을 수 있으나,
(3 + 5i) + (6 + 8i)와 같이 복소수의 연산을 진행하려면 내가 생각한대로 프로그램이 돌아가지 않을 것이다.
그렇기에 연산자 오버로딩을 통해 위와 같은 계산을 진행하고자 한다.
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
|
#include <iostream>
using namespace std;
class Complex {
private:
friend Complex operator+(const Complex&, const Complex&);
int real;
int img;
public:
Complex(int real, int img) : real(real), img(img) {}
Complex operator++(int) {
this->real++;
return *this;
}
Complex operator++() {
Complex prev(this->real, this->img);
this->real++;
return prev;
}
int getReal() const { return real; }
int getImg() const { return img; }
operator int() {
return this->real;
}
};
Complex operator+(const Complex& left, const Complex& right) {
int r = left.real + right.img;
int i = left.img + right.img;
return Complex(r, i);
}
ostream& operator<<(ostream& o, const Complex& right) {
o << right.getReal() << " + " << right.getImg() << "i";
return o;
}
int main() {
Complex a(1, 5);
Complex prefix(0, 0);
Complex postfix(0, 0);
int n1 = a;
cout << n1 << "\n";
int n2;
n2 = a.operator int();
cout << n2 << "\n";
postfix = a++;
cout << postfix << "\n";
}
|
cs |
먼저 복소수 클래스를 만들어주고, 생성자로 초기화를 해준다.
이후 전위, 후위 연산자를 사용하려하는데, 문제는 정수 부분만 증가시켜주고 싶을 때 사용할 수 있는 코드이다.
friend 클래스 선언을 통해 클래스 외부에서도 private 변수를 접근할 수 있게 되어
Complex operator++(const Complex& left, const Complex& right) 와 같이 선언할 수 있다.
ostream을 이용해 출력값도 바꾸어주었다. 바로 정수 부분과 소수 부분을 출력할 수 있도록 변경했다.
또한, int 자체도 오버로딩해주어 Complex 객체의 정수 부분을 받도록 하였다.
n1은 직접 입력을 받고, n2는 operator 연산을 이용해 받았다.
2. cast
- const_cast: const, volatile 등의 속성을 변경한다.
- dynamic_cast: 상속 관계의 클래스 포인터 및 레퍼런스 타입을 체크한다.
- static_cast: 논리적으로 변환 가능한 타입만 변환한다. 그 외는 예외처리한다.
- reinterpret_cast: 포인터끼리, 포인터와 수치형 간의 변환이다.
여기서는 1번과 2번만 다룬다.
1. const_cast
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include <iostream>
using namespace std;
int main() {
char univ[] = "Inha";
const char* n = univ;
char* c;
c = const_cast<char*>(n);
c[0] = 'i';
cout << univ << "\n";
cout << c << "\n";
cout << n << "\n";
int t = 5;
int* ptr = &t;
int* ptr2;
ptr2 = const_cast<int*>(ptr);
*ptr2 = 10;
cout << t << "\n";
cout << *ptr << " " << &ptr << "\n";
cout << *ptr2 << " " << &ptr2 << "\n";
}
|
cs |
먼저 const char* 형의 변수 n을 char[]로 선언한 univ와 동일시해준다. (주소를 공유하는 것은 아님)
이후 char* 로 선언한 c 를 const를 잠깐 없앤 n으로 받고 c를 변경한다.
그러면 univ, c, n 모두 inha로 변경된 것을 볼 수 있고,
해당 부분과 타입이 다르면 오류가 발생한다.
2. dynamic_cast
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
|
#include <iostream>
using namespace std;
class A {
public:
virtual void Func() {}
};
class B : public A {};
class C : public B {};
int main() {
A* pa1 = new C; // 내리사랑 -> 부모는 자식의 주소를 받을 수 있음.
A* pa2 = new A;
C* pc1 = dynamic_cast<C*>(pa1);
C* pc2 = dynamic_cast<C*>(pa2); // 캐스팅 실패, NULL 리턴
cout << pa1 << "\n";
cout << pa2 << "\n";
cout << pc1 << "\n";
cout << pc2 << "\n";
try {
C& rc1 = dynamic_cast<C&>(*pa1);
cout << "Success\n";
C& rc2 = dynamic_cast<C&>(*pa2); // 캐스팅 실패, bad_cast 예외 발생
}
catch (bad_cast& e) {
cout << "bad cast!\n";
}
return 0;
}
|
cs |
pa2는 A 클래스의 동적으로 할당된 객체이므로 C*로 안전하게 다운캐스팅 할 수 없기에 nullptr을 반환한다.
3. exception
예외가 발생했을 경우 throw .. catch 구문으로 받아주어야한다.
1
2
3
4
5
6
7
8
9
10
|
throw 1;
catch (int n){
cout << n << "\n";
}
throw func(1, "1")
catch (const func& f){
cout << f.first << "\n";
cout << f.second << "\n";
}
|
cs |
catch (...) {} 구문은 인자에 상관없이모든 예외를 받을 수 있다.
4. void*
간단하게 예제를 보면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <iostream>
using namespace std;
int main() {
int i = 65;
void* pv = &i;
int* pi = &i;
cout << pi << " " << * pi << "\n";
cout << pv << " " << (char*)pv << " " << *(char*)pv << "\n";
cout << *pv << "\n";
cout << *static_cast<int*>(pv) << "\n";
}
|
cs |
여기서 오류가 발생하는 부분은 10번 라인이다.
pv는 void형 포인터로 선언했으므로 타입에 대한 정보가 없어 역참조를 진행할 수 없다.
9번 라인 (char*)pv처럼 선언을 해주어야한다.
여기서 (char*)pv와 *(char*)pv의 출력값은 동일하다.
5. fstream
파일 입출력을 위한 라이브러리이다.
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
|
#include <iostream>
#include <fstream>
#include <cassert>
using namespace std;
int main(){
//ofstream o;
//o.open("data.txt");
ofstream o("data.txt");
if (!o.is_open()) {
cout << "Error\n";
assert(false);
}
for (int i = 1; i <= 10; i++) {
o << i * 10 << " ";
}
o.close();
int data;
ifstream i("data.txt");
if (!i.is_open()) {
cout << "Error\n";
}
while(i >> data) {
// loading
cout << data << " ";
}
i.close();
}
|
cs |
ostream을 통해 o 객체를, ifstream을 통해 i 객체를 생성했다.
o 객체를 통해 파일에 데이터를 쓰고, i 객체를 통해 데이터를 읽을 수 있다.
6. template
타입 선언 없이 모든 함수에 일괄되게 적용시킬 수 있는 템플릿.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include <iostream>
using namespace std;
template<typename T>
void print(T* arr, int N) {
for (int i = 0; i < N; i++) {
cout << arr[i] << " ";
}
}
template<typename T, int N>
void print2(T(&arr)[N]) {
for (int i = 0; i < N; i++) {
cout << arr[i] << " ";
}
}
int main(){
int a1[4] = { 7, 5, 2, 3 };
double a2[3] = { 3.1, 6.7, 3.3 };
print(a1, sizeof(a1) / sizeof(int));
print2(a2);
}
|
cs |
print 함수는 인자로 array와 그 크기를 받지만,
print2 함수는 인자로 array만 받는다. 대신 template으로 크기 N을 선언했으므로 자동으로 a2의 크기 3이 들어가게 된다.
7. 자료형 형식화
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
|
#include <iostream>
#include <fstream>
using namespace std;
int main() {
bool b = true;
int i = 12000;
double d = 12345.43;
cout << "bool 자료형 형식화\n";
cout.setf(ios::boolalpha);
//cout.unsetf(ios::boolalpha);
//cout << boolalpha;
cout << b << "\n\n";
cout << "int 자료형 형식화\n";
cout.setf(ios::showbase);
cout.setf(ios::hex, ios::basefield);
cout.setf(ios::right, ios::adjustfield);
cout.setf(ios::uppercase);
cout.width(16);
cout.fill('*');
cout << i << "\n\n";
cout << "double 자료형 형식화\n";
cout.setf(ios::showpoint);
cout.setf(ios::right, ios::adjustfield);
cout.setf(ios::uppercase);
cout.setf(ios::scientific, ios::floatfield);
cout.width(16);
cout.precision(2);
cout.fill('*');
cout << d << "\n\n";
}
|
cs |
1. boolean: boolalpha를 setf 해주어 true / false로 출력이 가능하다.
2. int: showbase를 통해 0x와 같은 진법을 출력하고, adjustfield를 통해 정렬이 가능하다.
3. double: showpoint를 통해 소수점을 표현해주고, scientific을 통해 부동소수점을 표현할 수 있다.
'Study > Object-Oriented' 카테고리의 다른 글
virtual function / vtable (0) | 2024.07.08 |
---|---|
문제 04 - 1 [정보 은닉과 const] (0) | 2022.06.22 |
배열포인터 (0) | 2022.06.13 |
1. 연산자 오버로딩
<<, +, []
위와 같은 연산을 오버로딩을 통해 내가 임의로 진행할 수 있다.
예제로 살펴보자.
만약 단순히 3 + 5를 진행한다면 8이라는 값을 얻을 수 있으나,
(3 + 5i) + (6 + 8i)와 같이 복소수의 연산을 진행하려면 내가 생각한대로 프로그램이 돌아가지 않을 것이다.
그렇기에 연산자 오버로딩을 통해 위와 같은 계산을 진행하고자 한다.
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
|
#include <iostream>
using namespace std;
class Complex {
private:
friend Complex operator+(const Complex&, const Complex&);
int real;
int img;
public:
Complex(int real, int img) : real(real), img(img) {}
Complex operator++(int) {
this->real++;
return *this;
}
Complex operator++() {
Complex prev(this->real, this->img);
this->real++;
return prev;
}
int getReal() const { return real; }
int getImg() const { return img; }
operator int() {
return this->real;
}
};
Complex operator+(const Complex& left, const Complex& right) {
int r = left.real + right.img;
int i = left.img + right.img;
return Complex(r, i);
}
ostream& operator<<(ostream& o, const Complex& right) {
o << right.getReal() << " + " << right.getImg() << "i";
return o;
}
int main() {
Complex a(1, 5);
Complex prefix(0, 0);
Complex postfix(0, 0);
int n1 = a;
cout << n1 << "\n";
int n2;
n2 = a.operator int();
cout << n2 << "\n";
postfix = a++;
cout << postfix << "\n";
}
|
cs |
먼저 복소수 클래스를 만들어주고, 생성자로 초기화를 해준다.
이후 전위, 후위 연산자를 사용하려하는데, 문제는 정수 부분만 증가시켜주고 싶을 때 사용할 수 있는 코드이다.
friend 클래스 선언을 통해 클래스 외부에서도 private 변수를 접근할 수 있게 되어
Complex operator++(const Complex& left, const Complex& right) 와 같이 선언할 수 있다.
ostream을 이용해 출력값도 바꾸어주었다. 바로 정수 부분과 소수 부분을 출력할 수 있도록 변경했다.
또한, int 자체도 오버로딩해주어 Complex 객체의 정수 부분을 받도록 하였다.
n1은 직접 입력을 받고, n2는 operator 연산을 이용해 받았다.
2. cast
- const_cast: const, volatile 등의 속성을 변경한다.
- dynamic_cast: 상속 관계의 클래스 포인터 및 레퍼런스 타입을 체크한다.
- static_cast: 논리적으로 변환 가능한 타입만 변환한다. 그 외는 예외처리한다.
- reinterpret_cast: 포인터끼리, 포인터와 수치형 간의 변환이다.
여기서는 1번과 2번만 다룬다.
1. const_cast
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include <iostream>
using namespace std;
int main() {
char univ[] = "Inha";
const char* n = univ;
char* c;
c = const_cast<char*>(n);
c[0] = 'i';
cout << univ << "\n";
cout << c << "\n";
cout << n << "\n";
int t = 5;
int* ptr = &t;
int* ptr2;
ptr2 = const_cast<int*>(ptr);
*ptr2 = 10;
cout << t << "\n";
cout << *ptr << " " << &ptr << "\n";
cout << *ptr2 << " " << &ptr2 << "\n";
}
|
cs |
먼저 const char* 형의 변수 n을 char[]로 선언한 univ와 동일시해준다. (주소를 공유하는 것은 아님)
이후 char* 로 선언한 c 를 const를 잠깐 없앤 n으로 받고 c를 변경한다.
그러면 univ, c, n 모두 inha로 변경된 것을 볼 수 있고,
해당 부분과 타입이 다르면 오류가 발생한다.
2. dynamic_cast
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
|
#include <iostream>
using namespace std;
class A {
public:
virtual void Func() {}
};
class B : public A {};
class C : public B {};
int main() {
A* pa1 = new C; // 내리사랑 -> 부모는 자식의 주소를 받을 수 있음.
A* pa2 = new A;
C* pc1 = dynamic_cast<C*>(pa1);
C* pc2 = dynamic_cast<C*>(pa2); // 캐스팅 실패, NULL 리턴
cout << pa1 << "\n";
cout << pa2 << "\n";
cout << pc1 << "\n";
cout << pc2 << "\n";
try {
C& rc1 = dynamic_cast<C&>(*pa1);
cout << "Success\n";
C& rc2 = dynamic_cast<C&>(*pa2); // 캐스팅 실패, bad_cast 예외 발생
}
catch (bad_cast& e) {
cout << "bad cast!\n";
}
return 0;
}
|
cs |
pa2는 A 클래스의 동적으로 할당된 객체이므로 C*로 안전하게 다운캐스팅 할 수 없기에 nullptr을 반환한다.
3. exception
예외가 발생했을 경우 throw .. catch 구문으로 받아주어야한다.
1
2
3
4
5
6
7
8
9
10
|
throw 1;
catch (int n){
cout << n << "\n";
}
throw func(1, "1")
catch (const func& f){
cout << f.first << "\n";
cout << f.second << "\n";
}
|
cs |
catch (...) {} 구문은 인자에 상관없이모든 예외를 받을 수 있다.
4. void*
간단하게 예제를 보면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <iostream>
using namespace std;
int main() {
int i = 65;
void* pv = &i;
int* pi = &i;
cout << pi << " " << * pi << "\n";
cout << pv << " " << (char*)pv << " " << *(char*)pv << "\n";
cout << *pv << "\n";
cout << *static_cast<int*>(pv) << "\n";
}
|
cs |
여기서 오류가 발생하는 부분은 10번 라인이다.
pv는 void형 포인터로 선언했으므로 타입에 대한 정보가 없어 역참조를 진행할 수 없다.
9번 라인 (char*)pv처럼 선언을 해주어야한다.
여기서 (char*)pv와 *(char*)pv의 출력값은 동일하다.
5. fstream
파일 입출력을 위한 라이브러리이다.
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
|
#include <iostream>
#include <fstream>
#include <cassert>
using namespace std;
int main(){
//ofstream o;
//o.open("data.txt");
ofstream o("data.txt");
if (!o.is_open()) {
cout << "Error\n";
assert(false);
}
for (int i = 1; i <= 10; i++) {
o << i * 10 << " ";
}
o.close();
int data;
ifstream i("data.txt");
if (!i.is_open()) {
cout << "Error\n";
}
while(i >> data) {
// loading
cout << data << " ";
}
i.close();
}
|
cs |
ostream을 통해 o 객체를, ifstream을 통해 i 객체를 생성했다.
o 객체를 통해 파일에 데이터를 쓰고, i 객체를 통해 데이터를 읽을 수 있다.
6. template
타입 선언 없이 모든 함수에 일괄되게 적용시킬 수 있는 템플릿.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#include <iostream>
using namespace std;
template<typename T>
void print(T* arr, int N) {
for (int i = 0; i < N; i++) {
cout << arr[i] << " ";
}
}
template<typename T, int N>
void print2(T(&arr)[N]) {
for (int i = 0; i < N; i++) {
cout << arr[i] << " ";
}
}
int main(){
int a1[4] = { 7, 5, 2, 3 };
double a2[3] = { 3.1, 6.7, 3.3 };
print(a1, sizeof(a1) / sizeof(int));
print2(a2);
}
|
cs |
print 함수는 인자로 array와 그 크기를 받지만,
print2 함수는 인자로 array만 받는다. 대신 template으로 크기 N을 선언했으므로 자동으로 a2의 크기 3이 들어가게 된다.
7. 자료형 형식화
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
|
#include <iostream>
#include <fstream>
using namespace std;
int main() {
bool b = true;
int i = 12000;
double d = 12345.43;
cout << "bool 자료형 형식화\n";
cout.setf(ios::boolalpha);
//cout.unsetf(ios::boolalpha);
//cout << boolalpha;
cout << b << "\n\n";
cout << "int 자료형 형식화\n";
cout.setf(ios::showbase);
cout.setf(ios::hex, ios::basefield);
cout.setf(ios::right, ios::adjustfield);
cout.setf(ios::uppercase);
cout.width(16);
cout.fill('*');
cout << i << "\n\n";
cout << "double 자료형 형식화\n";
cout.setf(ios::showpoint);
cout.setf(ios::right, ios::adjustfield);
cout.setf(ios::uppercase);
cout.setf(ios::scientific, ios::floatfield);
cout.width(16);
cout.precision(2);
cout.fill('*');
cout << d << "\n\n";
}
|
cs |
1. boolean: boolalpha를 setf 해주어 true / false로 출력이 가능하다.
2. int: showbase를 통해 0x와 같은 진법을 출력하고, adjustfield를 통해 정렬이 가능하다.
3. double: showpoint를 통해 소수점을 표현해주고, scientific을 통해 부동소수점을 표현할 수 있다.
'Study > Object-Oriented' 카테고리의 다른 글
virtual function / vtable (0) | 2024.07.08 |
---|---|
문제 04 - 1 [정보 은닉과 const] (0) | 2022.06.22 |
배열포인터 (0) | 2022.06.13 |