일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- virtual function
- std::endl
- c++ basic practice
- dynamic_cast
- C++
- suffix return type
- placement new
- pointer to member data
- std::vector
- virtual function table
- c++ multi chatting room
- virtual inheritance
- conversion constructor
- new&delete
- increment operator
- this call
- vector capacity
- constructor
- return by reference
- virtual destructor
- 더 지니어스 양면포커
- member function pointer
- std::ostream
- base from member
- vector size
- diamond inheritance
- std::cout
- discord bot
- delete function
- operator overloading
- Today
- Total
I'm FanJae.
[C++ 기본 연습 문제] Chapter 11. 연산자 오버로딩 II 본문
1. Chatper 11. 연산자 오버로딩 II
1-1. 깊은 복사를 하는 대입 연산자의 정의
① Chapter 07에서는 예제 HASComposite.cpp를 통해서 다음의 두 클래스를 정의하였다.
class Gun
{
....
}
class Police
{
private:
int handcuffs; //소유한 수갑의 수
Gun *pistol; //소유하고 있는 권총
public:
....
}
- 이에 Police 클래스를 대상으로 깊은 복사가 이뤄지도록 대입 연산자와 복사 생성자를 동시에 정의하고 이의 확인을 위해 main 함수도 적절히 정의해보자.
#include <iostream>
#include <cstring>
class Gun
{
private:
int bullet; // 장전된 총알의 수
public:
Gun(int bnum) : bullet(bnum)
{ }
void Shut()
{
std::cout << "BBANG!" << std::endl;
bullet--;
}
};
class Police
{
private:
int handcuffs; // 소유한 수갑의 수
Gun* pistol; // 소유하고 있는 권총
public:
Police(int bnum, int bcuff)
: handcuffs(bcuff)
{
if (bnum > 0)
pistol = new Gun(bnum);
else
pistol = NULL;
}
void PutHandcuff()
{
std::cout << handcuffs << std::endl;
std::cout << "SNAP!" << std::endl;
handcuffs--;
}
void Shut()
{
if (pistol == NULL)
std::cout << "Hut BBANG!" << std::endl;
else
pistol->Shut();
}
~Police()
{
if (pistol != NULL)
delete pistol;
}
Police(const Police& ref) : handcuffs(ref.handcuffs) // 복사 생성자
{
std::cout << "Call the copy constructor" << std::endl;
if (ref.pistol != NULL)
{
pistol = new Gun(*ref.pistol);
}
else
{
pistol = NULL;
}
}
Police& operator=(const Police& ref) // 대입 생성자
{
std::cout << "Call the assignment operator" << std::endl;
this->handcuffs = ref.handcuffs;
if (pistol != NULL)
{
delete pistol;
}
if (ref.pistol != NULL)
{
pistol = new Gun(*ref.pistol);
}
else
{
pistol = NULL;
}
return *this;
}
};
int main(void)
{
Police pman1(10, 10);
Police pman2 = pman1;
Police pman3(10, 5);
pman3 = pman2;
pman3.PutHandcuff();
pman3.Shut();
}
② Chapter 07의 문제 07-2의 두번 째 문제에서는 다음의 두 클래스 정의를 요구하였다.
class Book
{
private:
char *title; //책의 제목
char *isbn; //국제표준도서번호
int price; //책의 정가
....
};
class EBook :public Book
{
private:
char *DRMKey; //보안관련 키
....
};
- 이 때 정의한 두 클래스를 대상으로 Book 클래스도, EBook 클래스도 깊은 복사가 진행이 되도록,
- 복사생성자와 대입 연산자를 정의하고, 이의 확인을 위한 main 함수도 적절히 정의해보자.
- 참고로 이 문제의 해결을 위해서는 여러분이 생각해봐야 할 요소들이 몇 가지 존재한다.
- 특히 앞서 말한 다음 사실을 완전히 이해한 다음에 이 문제를 해결하기 바란다.
- "C++에서, AAA형 참조자는 AAA 객체 또는 AAA를 직접 혹은 간접적으로 상속하는 모든 객체를 참조할 수 있다."
#include <iostream>
class Book
{
private:
char* title; //책의 제목
char* isbn; //국제표준도서번호
int price; //책의 정가
public:
Book(const char* _title, const char* _isbn, int _price)
: price(_price)
{
size_t len = strlen(_title) + 1;
title = new char[len];
strcpy_s(title, len, _title);
size_t len1 = strlen(_isbn) + 1;
isbn = new char[len1];
strcpy_s(isbn, len1, _isbn);
}
void ShowBookInfo() const
{
std::cout << "Title : " << title << std::endl;
std::cout << "ISBN : " << isbn << std::endl;
std::cout << "Price : " << price << std::endl;
}
~Book()
{
delete[]title;
delete[]isbn;
}
Book(const Book& ref) : price(ref.price)
{
std::cout << "Call by copy constructor" << std::endl;
size_t len = strlen(ref.title) + 1;
title = new char[len];
strcpy_s(title, len, ref.title);
size_t len2 = strlen(ref.isbn) + 1;
isbn = new char[len2];
strcpy_s(isbn, len2, ref.isbn);
}
Book& operator=(const Book& ref)
{
std::cout << "Call by assignment constructor" << std::endl;
this->price = ref.price;
if (title != NULL)
{
delete[] title;
}
if (isbn != NULL)
{
delete[] isbn;
}
size_t len = strlen(ref.title) + 1;
title = new char[len];
strcpy_s(title, len, ref.title);
size_t len2 = strlen(ref.isbn) + 1;
isbn = new char[len2];
strcpy_s(isbn, len2, ref.isbn);
return *this;
}
};
class Ebook :public Book
{
private:
char* DRMKey; //보안관련 키
public:
Ebook(const char* a, const char* b, int c, const char* _drmkey) :Book(a, b, c)
{
size_t len = strlen(_drmkey) + 1;
DRMKey = new char[len];
strcpy_s(DRMKey, len, _drmkey);
}
void ShowEBookInfo() const
{
ShowBookInfo();
std::cout << "DRMKey : " << DRMKey << std::endl;
}
~Ebook() {}
};
int main(void)
{
Book book("좋은 C++", "555-12345-890-0", 20000);
Book book2 = book;
book2.ShowBookInfo();
Book book3("열혈 C++", "155-12345-890-0", 40000);
Book book4("좋은 C++", "555-12345-890-0", 20000);
book4 = book3;
book4.ShowBookInfo();
}
1-2. C++ 기반의 데이터 입출력
① 예제 StablePointPtrArray.cpp의 65, 66행을 다음과 같이 구성할 수 있도록,
- Point 클래스를 대상으로 연산자 오버로딩을 진행해보자.
for(int i=0; i<arr.GetArrLen(); i++)
cout<<arr[i];
물론, 실행결과에는 변함이 없도록 오버로딩 해야 한다.
#include <iostream>
#include <cstdlib>
using namespace std;
class Point
{
private:
int xpos, ypos;
public:
Point(int x = 0, int y = 0) : xpos(x), ypos(y) { }
friend ostream& operator<<(ostream& os, const Point& pos);
friend ostream& operator<<(ostream& os, const Point* pos);
};
ostream& operator<<(ostream& os, const Point& pos)
{
os << '[' << pos.xpos << ", " << pos.ypos << ']' << endl;
return os;
}
ostream& operator<<(ostream& os, const Point* pos)
{
os << '[' << pos->xpos << ", " << pos->ypos << ']' << endl;
return os;
}
typedef Point* POINT_PTR;
class BoundCheckPointPtrArray
{
private:
POINT_PTR* arr;
int arrlen;
BoundCheckPointPtrArray(const BoundCheckPointPtrArray& arr) { }
BoundCheckPointPtrArray& operator=(const BoundCheckPointPtrArray& arr) { }
public:
BoundCheckPointPtrArray(int len) :arrlen(len)
{
arr = new POINT_PTR[len];
}
POINT_PTR& operator[] (int idx)
{
if (idx < 0 || idx >= arrlen)
{
cout << "Array index out of bound exception" << endl;
exit(1);
}
return arr[idx];
}
POINT_PTR operator[] (int idx) const
{
if (idx < 0 || idx >= arrlen)
{
cout << "Array index out of bound exception" << endl;
exit(1);
}
return arr[idx];
}
int GetArrLen() const
{
return arrlen;
}
~BoundCheckPointPtrArray()
{
delete[]arr;
}
};
int main(void)
{
BoundCheckPointPtrArray arr(3);
arr[0] = new Point(3, 4);
arr[1] = new Point(5, 6);
arr[2] = new Point(7, 8);
for (int i = 0; i < arr.GetArrLen(); i++)
cout << arr[i];
delete arr[0];
delete arr[1];
delete arr[2];
return 0;
}
② 이번에는 재미삼아서 2차원 배열접근에 대한 연산자 오버로딩을 진행하고자 한다.
- 실제로 이렇게까지 연산자를 직접 오버로딩 하는 경우는 거의 없다.
- 다만, 필자는 호기심을 유발 및 충족시킨다는 측면에서 이 문제를 제시하는 것이다.
- 그러니 여러분도 이 문제에 단순한 호기심과 즐거움을 느꼈으면 좋곘다. 그럼 문제를 제시하겠다.
- 다음의 이름으로 클래스를 정의하자.
class BoundCheck2DIntArray{...}
이 클래스는 BoundCheckIntArray 클래스의 2차원 배열 버전이다.
따라서 다음과 같이 객체를 생성하면,
BoundCheck2DIntArray arr2d(3, 4);
세로와 가로의 길이가 각각 3과 4인, int형 2차원 배열처럼 동작하는 arr2d 객체가 생성되어,
다음의 형태로 데이터를 저장 및 참조할 수 있어야 한다.
for (int n = 0; n < 3; n++)
for (int m = 0; m < 4; m++)
arr2d[n][m] = n + m;
for (int n = 0; n < 3; n++)
{
for (int m = 0; m < 4; m++)
cout << arr2d[n][m] << ' ';
cout << endl;
}
- 참고로 두개의 []연산자를 동시에 오버로딩 하는 것은 허용되지 않기 때문에,
- 위의 다음 문장은, arr2d[n][m] 두번의 []연산자 호출을 동반하게끔 구현해야 한다.
즉, 첫번째 []연산에 의해서 위의 문장은 다음과 같이 해석되어야 하며, (arr2d.operator[](n))[m];
그리고 arr2d.operator[](n)연산의 반환 값을 이용해서 두 번째[] 연산은 다음과 같이 해석되어야한다.
((반환 값).operator[])(m);
#include <iostream>
#include <cstdlib>
using namespace std;
class BoundCheckIntArray
{
private:
int* arr;
int arrlen;
BoundCheckIntArray(const BoundCheckIntArray& arr) { }
BoundCheckIntArray& operator=(const BoundCheckIntArray& arr) { }
public:
BoundCheckIntArray(int len) :arrlen(len)
{
arr = new int[len];
}
int& operator[] (int idx)
{
if (idx < 0 || idx >= arrlen)
{
cout << "Array index out of bound exception" << endl;
exit(1);
}
return arr[idx];
}
int operator[] (int idx) const
{
if (idx < 0 || idx >= arrlen)
{
cout << "Array index out of bound exception" << endl;
exit(1);
}
return arr[idx];
}
int GetArrLen() const
{
return arrlen;
}
~BoundCheckIntArray()
{
delete[] arr;
}
};
typedef BoundCheckIntArray* BoundCheckIntArrayPtr;
class BoundCheck2DIntArray
{
private:
BoundCheckIntArray** arr;
int arrlen;
BoundCheck2DIntArray(const BoundCheck2DIntArray& arr) { }
BoundCheck2DIntArray& operator=(const BoundCheck2DIntArray& arr) { }
public:
BoundCheck2DIntArray(int col, int row) : arrlen(col)
{
arr = new BoundCheckIntArrayPtr[col];
for (int i = 0; i < col; i++)
arr[i] = new BoundCheckIntArray(row);
}
BoundCheckIntArray& operator[] (int idx)
{
if (idx < 0 || idx >= arrlen)
{
cout << "Array index out of bound exception" << endl;
exit(1);
}
return *(arr[idx]);
}
~BoundCheck2DIntArray() {
for (int i = 0; i < arrlen; i++)
delete arr[i];
delete[] arr;
}
};
int main(void)
{
BoundCheck2DIntArray arr2d(3, 4);
for (int n = 0; n < 3; n++)
for (int m = 0; m < 4; m++)
arr2d[n][m] = n + m;
for (int n = 0; n < 3; n++)
{
for (int m = 0; m < 4; m++)
cout << arr2d[n][m] << ' ';
cout << endl;
}
}
'C++ > Basic Practice' 카테고리의 다른 글
[C++ 기본 연습 문제] Chapter 13. 템플릿(Template) 1 (1) | 2024.09.15 |
---|---|
[C++ 기본 연습 문제] Chapter 10. 연산자 오버로딩 I (0) | 2024.09.08 |
[C++ 기본 연습 문제] Chapter 08. 상속과 다형성 (0) | 2024.09.07 |
[C++ 기본 연습 문제] Chapter 07. 상속(Inheritance)의 이해 (1) | 2024.09.06 |
[C++ 기본 연습 문제] Chapter 05. 복사 생성자(Copy Constructor) (0) | 2024.09.05 |