C++. ATM Project ver.4

lsw·2021년 4월 6일
0

Mini Projects

목록 보기
4/11
post-thumbnail

1. 업데이트

newly applied notions be

  • string class
  • use string class to make other classes more clear(lighter)
  • segmentize each file

2. 코드

written on VisulStudio2019

  • string.h
#pragma once
#include<iostream>
using namespace std;

/*String class*/
// 문자열 관련한 잡무(동적할당, 깊은복사, strcat, strcmp 등등)를 수행해 주는 유틸 클래스
class String
{
private:
	int len; // 문자열의 길이
	char* str; // 입력될 문자열

public:
// 생성자 - 동적할당
	String(); // 생성자1(무인자)
	String(const char* Str); // 생성자2(인자 : string's address)
// 복사생성자 - 깊은복사
	String(const String& scopy);
// 대입연산자 - 깊은복사
	String& operator=(const String& scopy);
// 연산자 오버로드(operator overload)
// '+' : 문자열의 연결
	String operator+(const String& str2);
// '+=' : 문자열 연결값으로 초기화 
	String& operator +=(const String& str2);
// '==' : 문자열 일치 여부 확인(일치 시 TRUE, 불일치 시 FALSE)
 	bool operator==(const String& str2); 
// '<<, >>' : 문자열 입, 출력
	friend ostream& operator<<(ostream& cout, const  String& str); // 문자열 출력
	friend istream& operator>>(istream& cin, String& str); // 문자열 입력
/* **주의** 
   입력 연산자 오버로드 함수의 두번째 인자에 const가 선언될 수 없다(상수화 변수)
   이유 : 입력함수는 사용자에게 입력받을 문자열을 저장하는 임시객체를 생성 후 대입연산자
   호출을 통해 기존 str(두번째 인자)에 복사하는 형식으로 정의하려 하기에 값의 변동이 있는
   두번째 인자로서는 const 선언이 불가능하다.
*/
}
  • string.cpp
#include "string.h" 
#include <iostream>
#include <cstring>
using namespace std;

/*String class*/
// 생성자1
/*필수가 아니더라도 가능한 선에선 모두 **const선언**을 진행할 예정*/
String::String() // Default 생성자(무인자), 기본값으로 len, str 각 0, 0(NULL을 의미) 초기화
{
    len = 0;
    str = 0;
}
// 생성자2
String::String(const char* Str)
{
    len = strlen(Str) + 1; // 'null' 포함
    str = new char[len]; // 동적할당
    strcpy_s(str, len, Str); // 입력 문자열 복사
}
// 복사생성자
String::String(const String& scopy)
{
    len = scopy.len + 1; // null포함
    str = new char[len];
    strcpy_s(str, len, scopy.str);
}
// 대입연산자
String& String::operator=(const String& scopy)
{
    len = scopy.len + 1;
    str = new char[len];
    strcpy_s(str, len, scopy.str);
    return *this;
}
/*
위 **생성자, 복사생성자, 대입연산자**의 별도 생성(디폴트 이외)은 멤버변수로 문자열을 갖는 객체
마다 동적할당, 깊은복사를 고려해야하는 수고를 String클래스를 지니는 String.h를 간단히 참조하
는 것으로 덜어내기 위함이다!!
*/
/*연산자 오버로드*/
// '+'
String String::operator+(const String& str2) // 문자열 이어붙인 결과 반환
{
// 디폴트 생성자의 호출에 의해 생성된 변수 str는 배제한다.
    if (str == NULL)
    {
        exit(1); // 디폴트생성, 함수이용시 제한
    }
// 새로운 임시 String 객체를 생성
    int len2 = len + str2.len - 1; 
/*
  좌측 피연산자(함수적용 본체)와 우측피연산자 길이 len, str2.len은 각각 null을 위한 자리
  **1을 추가로** 지니고 있다. 하지만 이 두 문자열을 이어붙여 하나의 문자열을 생성할 계획이니
  두개의 null자리는 불필요하다. 따라서 -1 해주는 모습(교집합을 떠올리면 이해가 쉽다)
*/ 
// 임의 문자열 포인터 동적할당
    char* tempstr = new char[len2];
    strcpy(tempstr, str); // str복사
    strcat(tempstr, str2.str); // 남은공간에 str2.str복사하여 이어붙임
    String temps(tempstr); // 생성자 호출, 임시객체 생성
    return temps; // 임시객체 반환
}
// '+='
String& String::operator +=(const String& str2) // 좌측 피연산자 초기화
{
    if (str == NULL)
    {
        exit(1);
    }
    *this = *this + str2; 
/* 
  좌피연산객체 = 좌피연산객체 +(call '**+'** operator overloaded function) 
  우피연산객체 : 대입연산자를 호출하여 생성된 임시 String 객체를 복사, 좌피연산자를 초기화
*/
    return *this; // 초기화된 객체(좌피연산자) 반환
}
// '=='
bool String::operator==(const String& str2)
{
    if (str == NULL)
    {
        exit(1);
    }
    if (strcmp(str, str2.str) == 0) // strcmp(좌 , 우)== 0 -> same string!
        return true; // also means 1
    else
        return false; // also means 0
}
// '<<, >> operator overload'
// 출력
ostream& operator<<(ostream& cout, const  String& str1)
// ostream & 형으로 반환돼야 << -- << 연속성을 유지할 수 있다.
{
    cout << str1.str << endl; // cout은 단지 ostream object라는 뜻이며 별다른 의미 없다.
    return cout;
}
// 입력
istream& operator>>(istream& cin, String& str1)
{
    char str[100]; // char형 배열 생성(문자열을 입력받을, 즉 주소값을 저장할 공간 생성)
    cin >> str; // 문자열 입력(istream class member function)
    str1 = String(str); // 대입연산자 호출, 기존 객체 초기화
    return cin;
}
  • account.h
#pragma once
#include "string.h"

// 특별이율
namespace TYPE
{
	enum
	{
		NORMAL = 1, HIGH
	};
}

// 가능업무
enum
{
	MAKE = 1, DEPOSIT, WITHDRAW, SHOW, EXIT
};

// 전역변수(일관성)
const int NAME_LEN = 30;
const int ID_LEN = 30;

// virutal class, basic class 
/*Account*/
class Account
{
private:
	// 'char *'type converted into String class type
	String acc_id;
	String acc_name;
	double acc_balance;
// 생성자, 복사생성자, 대입연산자 별동정의가 필요치 않다 -> 간결성 보장
public:
	Account(String acc_id, String acc_name, double acc_seed);
// 매개변수형은 마찬가지 String 이다.

// 기타 재료함수(sub functions)
// 계좌번호 반환
	String Get_id();
// 잔금 반환
	double Get_balance();
// 상황에 따라 함수 접근체 타입(클래스)의 멤버함수를 호출하기 위한 virtual 선언
// 입금
	virtual void Proccess_depos(double money); // 오버로드, virtual
// 출금
  void Proccess_withdraw(double money);
// 정보조회
	void Showinform()

};

// Controller class
/*Accounthandler*/
class Accounthandler
{
private:
	Account* acc[100]; 
	int acc_count;
public:
	// 생성자, 소멸자
	Accounthandler(); // 디폴트 생성자
	~Accounthandler(); // Account 배열객체 소멸용도
	// 기능함수 선언(메뉴, 개설, 입금, 출금, 조회)
	void Menu();
	void Make();
	void Deposit();
	void Withdraw();
	void Show();
};

/*Normal class*/
class Normal : public Account
{
private:
	double acc_rate; // 일반 이율

public:
	Normal(String id, String name, double seed, double rate);
// 입금함수 오버로드 1
	void Proccess_depos(double money);
// 일반이율 반환
	double Getrate();
// 출금함수는 모든 클래스에서 동일기능이기 때문에 추가 정의가 필요치 않다.
};

/*High*/
class High : public Normal
{
private:
	// 고객 등급, 그에 해당하는 특별이율
	char acc_type;
	double acc_srate;

public:
	High(String id, String name, double seed, double rate, char type);
// 입금함수 오버로드 2
	void Proccess_depos(double money);
};
  • account.cpp
#include "account.h" 
#include "string.h"
#include<iostream>
#include<cstring> 
using namespace std;

/*about Account class*/
// 생성자
Account::Account(String Acc_id, String Acc_name, double acc_seed)
    :acc_balance(acc_seed) // 가능한 이니셜라이저를 사용할 수 있다면 사용하는 것이 효율적
{
    acc_id = Acc_id;
    acc_name = Acc_name;
/*
 본문 초기화 시 acc_id, acc_name이 선언된 이후 정의되는데 여기서..
 1. 선언 -> 생성자 호출 -> 동적할당
 2. 정의 -> String class의 대입연산자를 호출 -> 깊은복사
*/
}

// 기타 재료함수
// 계좌번호 반환
String Account::Get_id()
{
    return acc_id;
}
// 잔금반환
double Account::Get_balance()
{
    return acc_balance;
}
// 입금함수
void Account::Proccess_depos(double money)
{
    acc_balance += money; // 오버로디드
}
// 출금함수(전 클래스 동일)
void Account::Proccess_withdraw(double money)
{
    acc_balance -= money;
}
// 조회함수
void Account::Showinform()
{
    cout << "계좌번호 : " << acc_id << endl;
    cout << "고객이름 : " << acc_name << endl;
    cout << "잔액 : " << acc_balance;
}

/*--------------------------------------------------------------------*/
/*about Normal class*/
// 생성자
Normal::Normal(String Acc_id, String Acc_name, double acc_seed, double rate)
    : acc_rate(rate), Account(Acc_id, Acc_name, acc_seed)
{
}

// 입금함수. 일반이율 고려
void Normal::Proccess_depos(double money)
{
// 총 입금액 = 입금액 + 원금*일반이율
    Account::Proccess_depos(money + Account::Get_balance() * acc_rate);
}

/*  **중요!!**
   멤버변수 acc_rate의 접근범위를 protected로 설정하여 하위클래스(유도클래스)인 High가
   입금함수 계산 시 접근성을 갖게 할 수도 있다. 하지만 정보의 은닉성을 최대한 보장하기
   위해 별도 일반이율(acc_rate) 반환함수 Getrate를 정의하여 준다. */
double Normal::Getrate()
{
    return acc_rate;
}

/*--------------------------------------------------------------------*/
/*about High class*/
// 생성자
High::High(String id, String name, double seed, double rate, char type)
    : Normal(id, name, seed, rate)
{
    // 인자로 입력받은 등급별 상이한 특별 이율 초기화
    switch (type)
    {
    case 'A':
    {
        acc_srate = 0.07;
        break;
    }
    case 'B':
    {
        acc_srate = 0.04;
        break;
    }
    case 'C':
    {
        acc_srate = 0.02;
        break;
    }
    default: // 'A', 'B', 'C' 이외의 값 입력 시 
    {
        cout << "유형을 재입력 하세요" << endl;
    }
   }
}

// 입금함수. 일반이율 & 특별이율 고려
void High::Proccess_depos(double money)
{
// 총 입금액 = 입금액 + 원금 * (일반이율 + 특별이율)
    Account::Proccess_depos(money + Account::Get_balance() * (Getrate() + acc_srate));
}

/*--------------------------------------------------------------------*/
/*about Accounthandler class*/
// 생성자
Accounthandler::Accounthandler()
    :acc_count(0) // 고객번호 0부터 시작
{
}

// 소멸자
// Account형 배열객체 소멸
Accounthandler::~Accounthandler()
{
    for (int i = 0; i < acc_count; i++)
    {
        delete acc[i];
    }
}

// 기능 함수
// 메뉴
void Accounthandler::Menu()
{
    cout << "-------------------------------------------------" << endl;
    cout << "가능 업무" << endl;
    cout << "1. 계좌 생성 " << endl;
    cout << "2. 입금" << endl;
    cout << "3. 출금" << endl;
    cout << "4. 조회" << endl;
    cout << "5. 프로그램 종료" << endl;
}

// 생성
void Accounthandler::Make()
{
    String id;
    String name;
    int balance;
    int type;
// 고객 신상정보 입력
    cout << "계좌번호 : "; cin >> id;
    cout << "성명     : "; cin >> name;
    cout << "예치금   : "; cin >> balance;
    cout << "[계좌유형 결정]" << endl;
    cout << "(Normal=1/High=2) : "; cin >> type;
    double rate;
    char acc_level;
    // 계좌 유형에 따라 다른 형의 동적할당
    switch (type)
    {
    case TYPE::NORMAL:
    {
        cout << "이율   : "; cin >> rate;  // 이율 입력
// Normal클래스 형 동적할당
        acc[acc_count++] = new Normal(id, name, balance, rate);
        break;
    }
    case TYPE::HIGH:
    {
// 이율, 특별이율 입력
        cout << "이율   : "; cin >> rate;
        cout << "고객등급 : "; cin >> acc_level;
// High클래스 형 동적할당
        acc[acc_count++] = new High(id, name, balance, rate, acc_level);
    }
   }
}
// 입금
void Accounthandler::Deposit()
{
    String id;
    int money;
// 입금 거래내용 입력
// 계좌번호 입력
    cout << "계좌번호 입력 : "; cin >> id;
    for (int i = 0; i < acc_count; i++)
    {
        if (acc[i]->Get_id() == id) // 아이디 일치확인
        {
            cout << "금액(만원) : "; cin >> money; // 입금액 입력
            // virtual(가상)함수인 Proccess_depos -> 접근객체의 **클래스 존중**
            acc[i]->Proccess_depos(money);
            cout << "정상 입금 되었습니다." << endl;
            break;
        }
        else if (i == acc_count - 1) // 불일치 시
            cout << "계좌번호가 정확히 입력되었는지 확인하세요" << endl;
    }
}

// 출금
void Accounthandler::Withdraw()
{
    String id;
    int money;
// 출금 거래내용 입력
    cout << "계좌번호 입력 : "; cin >> id;
    for (int i = 0; i < acc_count; i++)
    {
        if (acc[i]->Get_id() == id) // 계좌 일치 확인
        {
            cout << "금액(만원) : "; cin >> money; // 출금액 입력
            acc[i]->Proccess_withdraw(money); // 출금함수 호출
            cout << "정상 출금 되었습니다." << endl;
        }
       else if (i == acc_count - 1) // 불일치 시
            cout << "계좌번호가 정확히 입력되었는지 확인하세요" << endl;
    }
}
// 조회
void Accounthandler::Show()
{
    cout << "------------------";
    for (int i = 0; i < acc_count; i++)
    {
        cout << endl << "[" << i << "번째 고객 정보]" << endl;
        acc[i]->Showinform();
        cout << " 만원" << endl << endl;
    }
}
  • main.cpp
#include "account.h"
# include "string.h"
#include <iostream>
#include<cstring>
using namespace std;

int main()
{
	Accounthandler AH; // Accounthandler클래스 객체 생성, 멤버함수 접근
	int choice;
	while (1)
	{
		AH.Menu(); // 메뉴함수
		cout << "거래를 선택하세요 : "; cin >> choice;
		switch (choice)
		{
		case MAKE:
		{
			AH.Make(); // 생성함수
			break;
		}
		case DEPOSIT:
		{
			AH.Deposit(); // 입금함수
			break;
		}
		case WITHDRAW:
		{
			AH.Withdraw(); // 출금함수
			break;
		}
		case SHOW:
		{
			AH.Show(); // 조회함수
			break;
		}
		case EXIT:
		{
			cout << "거래를 종료합니다." << endl;
			return 0;
		}
		default:
		{
			cout << "다른 옵션을 선택하세요" << endl;
		}
		}
		char further[5];
		cout << "거래를 계속 하시겠습니까?(yes / no) : "; cin >> further;
		if (strcmp(further, "no") == 0)
		{
			return 0;
		}
	}
	return 0;
}

3. 결과

결과는 이전버전들과 동일하며 이것은 본인이 의도했던 바이다. controller클래스와 결과를 건들지 않고 1. 파일을 세분화 하고 2. String클래스 개념을 이식하는 데 성공하였다.


4. 결론

자주 사용하는 기능을 함수로 만들어 호출해 사용하는 것 처럼 자주 사용하는 기능을 클래스로 만들어 해당클래스의 객체를 통해 그 기능에 접근(함수 호출)할 수 있다. 파일을 세분화 하여 보다 정돈된 느낌과 용량이 커져가는 이번 프로젝트에 차후 혼란을 방지할 수 있다.

profile
미생 개발자

0개의 댓글