본문 바로가기
열혈강의c++

[ 열혈C++ ] OOP : 단계별 프로젝트 08/09단계

by 어린왕자1234 2022. 7. 21.

[ 열혈C++ ] OOP : 단계별 프로젝트 08단계

이번 단계에서는 예제 StablePointPtrArray.cpp에서 정의한 BoundCheckPointPtrArray 클래스를 본 프로젝트에 맞게 변경시켜서 적용하고자 한다. 그리고 일부 클래스에 대해 깊은 복사를 진행하도록 대입 연산자도 오버로딩 하고자 한다.

Account 클래스는 깊은 복사를 진행하도록 복사 생성자가 정의되어 있다. 따라서 대입 연산자도 깊은 복사가 진행되도록 정의하는 것이 좋다. 그리고 AccountHandler 클래스에는 배열이 멤버로 선언되어서 객체의 저장을 주도 하는데, 이를 이번 Chapter에서 정의한 BoundCheckPointPtrArray 배열 클래스로 대체하고자 한다. 물론, 이를 위해서는 약간의 수정이 필요하며, 클래스의 이름도 적당히 변경 할 필요가 있다.

실제 변경이 발생하는 헤더파일과 소스파일은 다음과 같다.
 - Account.h, Account.cpp                                대입 연산자의 정의
 - AccountHandler.h                                         BoundCeckPointPtrArray 클래스의 적용

따라서 이들 파일에 대한 버전정보를 갱신하기 바란다. 또한 배열 클래스의 추가를 위해서 다음의 소스파일과 헤더파일을 추가하기로 하자.
 - AccountArray.h, AccountArray.cpp              배열 클래스의 선언과 정의

혹 위에서 언급한 파일과 BankingCommonDecl.h를 제외한 다른 파일에서 수정이 발생했다면, 무엇인가 잘못되었거나 불필요한 수정이 가해졌을 수도 있으니, 다시 한번 확인하기 바란다.(필자와 구현방법을 달리한다면, AccountHandler.cpp의 수정이 불가피할 수는 있다.)

BankingCommonDecl.h 파일에  헤더파일 <cstdlib> 추가

 

복사 생성자 및 대입 연산자 추가 

Account.h

Account& operator=(const Account& ref);

Account.cpp

Account& Account::operator=(const Account& ref)
{
	delete[]name;
	size_t len = strlen(ref.name);
	name = new char[len + 1];
	strcpy_s(name, len + 1, ref.name);
	accID = ref.accID;
	balance = ref.balance;
	return *this;
}

[별도 지정] 상속 클래스에 복사생성자 및 대입연산자 추가

-  NormalAccount.h 

NormalAccount::NormalAccount(const NormalAccount& cpy) : Account(cpy), interRate(cpy.interRate) {}
NormalAccount& NormalAccount::operator=(const NormalAccount& ref)
{
	Account::operator=(ref);
	interRate = ref.interRate;
	return *this;
}

-  HighCreditAccount.h

HighCreditAccount::HighCreditAccount(const HighCreditAccount& cpy) 
	: NormalAccount(cpy), specialRate(cpy.specialRate) {}

HighCreditAccount& HighCreditAccount::operator=(const HighCreditAccount& ref)
{
	NormalAccount::operator=(ref);
	specialRate = ref.specialRate;
	return *this;
}

 

배열class 추가 및 control class 수정

AccountHandler.h

#include "AccountArray.h"   // 추가


class AccountHandler
{
private:
	BoundCeckPointPtrArray accArr;   // 배열 class 객체 멤버변수로 변경

AccountArray.h 생성

#ifndef __ACCOUNTARRAY_H__
#define __ACCOUNTARRAY_H__

#include "Account.h"
typedef Account* ACC_PTR;
class BoundCeckPointPtrArray
{
	ACC_PTR* arr;
	int arrlen;
	BoundCeckPointPtrArray(const BoundCeckPointPtrArray& cpy);
	BoundCeckPointPtrArray& operator=(const BoundCeckPointPtrArray& cpy);
public:
	BoundCeckPointPtrArray(int len=100) ;
	ACC_PTR& operator[](int idx);
	ACC_PTR operator[](int idx) const;
	int GetArrLen() const; 
	~BoundCeckPointPtrArray();
};
#endif

AccountArray.cpp 생성

#include "BankingCommonDecl.h"
#include "AccountArray.h"

BoundCeckPointPtrArray::BoundCeckPointPtrArray(int len) : arrlen(len)
{
	arr = new ACC_PTR[len];
}
ACC_PTR& BoundCeckPointPtrArray::operator[](int idx)
{
	if (idx < 0 || idx >= arrlen)
	{
		cout << "Array index out of bound exception" << endl;
		exit(1);
	}
	return arr[idx];
}
ACC_PTR BoundCeckPointPtrArray::operator[](int idx) const
{
	if (idx < 0 || idx >= arrlen)
	{
		cout << "Array index out of bound exception" << endl;
		exit(1);
	}
	return arr[idx];
}
int BoundCeckPointPtrArray::GetArrLen() const 
{ 
	return arrlen; 
}
BoundCeckPointPtrArray::~BoundCeckPointPtrArray()
{
	delete[]arr;
}

Account.h
0.00MB
AccountArray.h
0.00MB
AccountHandler.h
0.00MB
BankingCommonDecl.h
0.00MB
HighCreditAccount.h
0.00MB
NormalAccount.h
0.00MB
Account.cpp
0.00MB
AccountArray.cpp
0.00MB
AccountHandler.cpp
0.00MB
Main.cpp
0.00MB


[ 열혈C++ ] OOP : 단계별 프로젝트 09단계

우리가 정의한 Account 클래스는 생성자에서 문자열을 동적 할당하기 때문에, 소멸자 그리고 깊은 복사를 위한 복사생성자와 대입 연산자가 정의되어 있다. 그런데 이번에 적용할 String 클래스는 메모리 공간을 동적 할당하고, 깊은 복사를 진행하는 형태로 복사생성자와 대입 연산자가 정의되어 있기 때문에, 이를 이용하면 Account 클래스의 구현이 한결 간단해진다.
조금 더 자세히 설명하면, Account 클래스의 생성자 내에서의 동적 할당이 불필요해지며, 이로 인해서 직접 정의한 소멸자와 복사 생성자 그리고 대입 연산자가 모두 불필요해진다. 바로 이러한 사실을 확인하고 다음의 결론을 스스로 내리는 것이 이번 프로젝트의 핵심이라 할 수 있다.

"적절한 클래스의 등장은 다른 클래스의 정의를 간결하게 해준다"

참고로 String 클래스를 등장시켰다고 해서 char형 포인터 기반의 문자열 표현을 억지로 제한할 필요는 없다.
그러나 본 프로젝트의 목적 중 하나는 직접 정의한 클래스를 적용하는데 있으니, 가급적 String 객체를 이용해서 문자열을 표현하기로 하자.

마지막으로 실제 변경이 발생하는 헤더파일과 소스파일은 다음과 같다.
Account.h,  Account.cpp
NormalAccount.h
HighCreditAccount.h
AccountHandler.cpp
그리고 String 클래스의 추가를 위해서 다음의 소스파일과 헤더파일을 추가하였다.
String.h, String.cpp   String 클래스의 선언과 정의

 

Account.h

 

Account.cpp

 

NormalAccount.h

HighCreditAccount.h

 

AccountHandler.cpp

 

String.h

#ifndef __STRING_H__
#define __STRING_H__

#include "BankingCommonDecl.h"
class String
{
	char* str;
	rsize_t len;
public:
	String();                             // default 생성자
	String(const char* s);                // 생성자
	String(const String& s);              // 복사생성자
	~String();                            // 소멸자
	String& operator=(const String& s);   // 대입연산자
	String& operator+=(const String& s);  // 단축연산자
	String operator+(const String& s);    // 덧셈연산자
	bool operator==(const String& s);     // 비교연산자
	friend ostream& operator<<(ostream& ostrm, const String& s);  // <<연산자
	friend istream& operator>>(istream& istrm, String& s);        // >>연산자
};
#endif

 

String.cpp

#include "String.h"

String::String()
	:str(NULL), len(0) {}
String::String(const char* s)
{
	len = strlen(s) + 1;
	str = new char[len];
	strcpy_s(str, len, s);
}
String::String(const String& s)
{
	len = s.len;
	str = new char[len];
	strcpy_s(str, len, s.str);
}
String::~String()
{
	if (str != NULL)
		delete[]str;
}
String& String::operator=(const String& s)
{
	if (str != NULL)
		delete[]str;
	len = s.len;
	str = new char[len];
	strcpy_s(str, len, s.str);
	return *this;
}
String& String::operator+=(const String& s)
{
	len += (s.len - 1);
	char* tmp = new char[len];
	strcpy_s(tmp, len, str);
	strcat_s(tmp, len, s.str);
	if (str != NULL)
		delete[]str;
	str = tmp;
	return *this;
}
String String::operator+(const String& s)
{
	rsize_t cat_len = len + s.len - 1;
	char* tmp = new char[cat_len];
	strcpy_s(tmp, cat_len, str);
	strcat_s(tmp, cat_len, s.str);

	String newString(tmp);
	delete[]tmp;
	return newString;
}
bool String::operator==(const String& s)
{
	return strcmp(str, s.str) ? false : true;
}
ostream& operator<<(ostream& ostrm, const String& s)
{
	ostrm << s.str;
	return ostrm;
}
istream& operator>>(istream& istrm, String& s)
{
	char str[100];
	istrm >> str;
	s = String(str);   // 임시객체 사용
	return istrm;
}

 

완성파일

9단계.Zip
0.01MB