티스토리 뷰

다음은 자바 코드를 C++로 바꾸는 과정을 보여줍니다. 이 프로그램은 Item 클래스를 이용해서 제품을 나타내고 Store 클래스는 그것을 이용해서 제품 데이터를 읽어들이고 출력한 후 주문을 받아 주문 내역을 출력합니다. 그리고 주문이 끝나면 그날의 매출 통계를 출력해 줍니다. 

먼저 입출력의 형식을 살펴봅시다. 입력은 상품의 개수와 상품별로 이름, 가격, 종류(성별)이 나옵니다. 이것을 출력할 때는 상품의 고유번호를 보여주고 이름, 종류, 가격 순으로 보기 좋게 출력합니다. 

11
후드티_ST1 5000 f
후드티_ST2 10000 c
면티_ST1__ 5000 f
면티_ST2__ 10000 f
면티_ST3__ 8800 c
치마_주름_롱 10000 f
치마_주름_숏 8000 f
반바지_청_숏 12000 f
바지_면_일자 10000 m
츄리닝____ 8000 c
줄무늬티___ 7000 c

=========== 상품리스트 ==============
[ 1] 후드티_ST1 [Man] 5000원
[ 2] 후드티_ST2 [Man] 10000원
[ 3] 면티_ST1__ [Man] 5000원
[ 4] 면티_ST2__ [Man] 10000원
[ 5] 면티_ST3__ [Man] 8800원
[ 6] 치마_주름_롱 [Man] 10000원
[ 7] 치마_주름_숏 [Man] 8000원
[ 8] 반바지_청_숏 [Man] 12000원
[ 9] 바지_면_일자 [Man] 10000원
[10] 츄리닝____ [Man] 8000원
[11] 줄무늬티___ [Man] 7000원

hw4-output.txt
0.00MB

이렇게 하기 위해 상품 클래스를 먼저 설계해 보겠습니다. 필요한 데이터는 이름, 가격, 성별이 있고 입력에는 없지만 상품의 고유번호도 필요합니다. (이것을 차례로 자동 할당해 주어야 합니다)

import java.util.Scanner;

public class Item {
	static int serialNo = 1;
	int id;
	String name;
	String type;
	int price;

	void read(Scanner scan) {
		id = serialNo++;
		name = scan.next();
		price = scan.nextInt();
		type = scan.next();
	}

	void print() {
		String t = "Man";
		if (type.equals("f")) 
        	t = "Woman";
		else if (type.equals("c")) 
        	t = "Common";
		System.out.printf("[%d] %s %s ", 
        			id, name, t);
		System.out.print(price + "원\n");
	}
}

자바에서는 하나의 파일 안에 클래스의 필드와 메소드가 모두 나타납니다. 그러나 C++은 클래스 선언부(헤더 파일)이 먼저 정의되고 멤버 함수의 코드가 나타나게 됩니다. 다음 예에서는 상품의 아이디와 이름, 타입, 가격이 필드로 나타납니다. 

먼저 static int serialNo; 정적 변수의 처리를 살펴봅시다. 자바에서는 static 키워드를 붙여서 필드를 선언하면 모든 객체가 공유하게되는 하나의 정적 변수가 선언됩니다. 그러나 C++에서는 정적 변수는 헤더에서 선언만 하고 클래스 선언부 밖에서 초기화를 해 주어야 합니다. int Item::serialNo = 1;가 그 초기화를 하는 부분입니다. 즉 헤더파일에 선언된 이름은 실제 변수를 할당하지는 않습니다.  이렇게 초기화를 하면 변수가 생성되고 1이라는 값이 저장됩니다. (자바에서는 static으로 선언된 변수들은 따로 모아서 미리 메모리를 할당해 줍니다.)

다음으로 오른쪽 멤버 함수의 선언부를 보면 클래스 선언하면서 바로 { ... }와 같이 코드를 써준 것을 볼 수 있습니다. 이것은 C++에서는 인라인 멤버함수라고 합니다. 즉 코드 부분에서 다시 string Item::getName() { ... } 을 써주어야 하는데 짧은(한줄 정도) 코드를 위해 너무 번거롭다고 생각되는 경우 그것을 그냥 헤더 안에 코드로 바로 써줄 수 있습니다. 이것은 여기서처럼 get/set 같은 간단한 코드에만 적용하는 것이 좋습니다.

그리고 나머지는 별도의 cpp 파일에 멤버함수의 구현부를 써주게 됩니다. 여기에 실제 코드가 나타납니다.

#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
using namespace std;

class Item {
	static int serialNo;
	int id;
	string name;
	string type;
	int price;
public:
	void read(ifstream& scan);
	void print();
	string getName() { return name; };
	int getPrice() { return price; };
};
============= Item.cpp =============
#include "item.h"
int Item::serialNo = 1;

void Item::read(ifstream& scan) {
	scan >> name >> price >> type;
}

void Item::print() {
	string t = "Man";
	if (type == "f") t = "Woman";
	else if (type == "c") t = "Common";
	cout << id < ". " << name << '\t' << setw(8) << price
		<< "원 " << t << endl;
}

위 코드에 나타난 C++ 프로그램의 특징을 다음과 같이 요약해 볼 수 있습니다.

  1. c++ 프로그램에서는 클래스 헤더(Item.h)와 구현부 파일(Item.cpp)로 나누는 것이 일반적입니다. (하나의 파일에 넣어도 되지만 별로 바람직하지 않습니다.)
  2. static 변수의 사용이 자바와는 좀 다릅니다. static 멤버 변수는 클래스 선언부에서는 static이라는 것만 선언하고 실제 그 값의 정의와 초기화는 cpp 파일에 나타나야 합니다.
  3. 자바의 String 대신 string 타입을 사용합니다.

다음으로 메인 함수와 Item 클래스를 이용하는 코드 부분을 살펴보겠습니다. 다음은 아이템 클래스를 이용하는 스토어 클래스의 자바 코드입니다. 이것은 메인 함수를 포함하는 메인 클래스이기도 합니다. 다음의 자바코드를 아래에 있는 C++ 코드와 비교해 보세요.

더보기
import java.util.Scanner;

public class Store {
	public static void main(String[] args) {
		Store a = new Store();
		a.mymain();
	}
	Scanner scan = new Scanner(System.in);
	Item itemList[] = null;
	int nItems;
	void mymain() {
		System.out.print("상품 갯수 입력: ");
		nItems = scan.nextInt();
		itemList = new Item[nItems];
		sales = new int[nItems];
		for (int i = 0; i < nItems; i++) {
			itemList[i] = new Item();
			itemList[i].read(scan);
		}
		for (int i = 0; i < nItems; i++) {
			itemList[i].print();
		}
		
		int subtotal = 0;
		int order[] = null;
		int n;
		while (true) {
			System.out.print(">>");
			n = scan.nextInt();
			if (n == 0)
			  break;
			order = readOrder(n);
			subtotal = computeTotal(order);
			printOrder(order, subtotal);
			total += subtotal;
		}
		printSales();
		System.out.println("Bye!");
	}
	int[] readOrder(int count) {
		int result[] = new int[count];
		for (int i = 0; i < count; i++) {
			result[i] = scan.nextInt();
			result[i]--;
		}
		return result;
	}
	int computeTotal(int[] order) {
		int subtotal = 0;
		for (int n : order) {
			subtotal += itemList[n].price;
			sales[n]++;
		}
		total += subtotal;
		return subtotal;
	}
	void printOrder(int[] order, int total) {
		for (int n : order) {
			itemList[n].print();
		}
		System.out.printf("합계 : %d원\n", 
        			total);
	}
	int sales[] = null;
	int total = 0;
	void printSales() {
		System.out.println("============ 매출 통계 ============");
		for (int i = 0; i < nItems; i++) {
			if (sales[i] == 0) continue;
			System.out.printf("[%d] %s %d개\n", 
				i+1, itemList[i].name, sales[i]);
		}
		System.out.printf("매출액 총계 : %d원\n", 
        		total);
	}
}

위의 코드를 C++로 바꾸기 위해서는 스토어 클래스를 만들 수도 있지만 C++은 일반 씨 함수 형태를 혼용하여 쓸 수 있습니다. 실제로 위에서 정의한 아이템 클래스를 이용하여 메인 함수를 씨 함수처럼 작성하는 것도 가능합니다.

#include "item.h"
int* readOrder(int);  // 메인에서 필요한 함수들 전방선언
int computeTotal(int*, int);
void printOrder(int*, int, int);
void printSales();
void runSale();

ifstream scan("week5-input.txt");
Item* itemList = NULL;
int nItems;
int* sales = NULL;  // 전역변수 미리 선언
int total = 0;

int main() {
	scan >> nItems;
	itemList = new Item[nItems];
	sales = new int[nItems];
	for (int i = 0; i < nItems; i++) {
		itemList[i].read(scan);
	}
	for (int i = 0; i < nItems; i++) {
		itemList[i].print();
	}
	runSale();
	cout << "Bye!" << endl;
	system("PAUSE");
}
void runSale() {
	int subtotal = 0;
	int* order = NULL;
	int n;
	while (true) {
		cout  << ">> ";
		cin >> n;
		if (n == 0)
			break;
		order = readOrder(n);
		subtotal = computeTotal(order, n);
		printOrder(order, n, subtotal);
		total += subtotal;
	}
	printSales();
}
int* readOrder(int count) {
	int* result = new int[count];
	for (int i = 0; i < count; i++) {
		cin >> result[i];
		result[i]--;
	}
	return result;
}
int computeTotal(int* order, int sz) {
	int subtotal = 0, n;
	for (int i = 0; i < sz; i++) {
		n = order[i];
		subtotal += itemList[n].getPrice();
		sales[n]++;
	}
	total += subtotal;
	return subtotal;
}
void printOrder(int* order, int sz, 
				int total) {
	int n;
	for (int i = 0; i < sz; i++) {
		n = order[i];
		itemList[n].print();
	}
	cout << "합계 : " << total << "원\n";
}

void printSales() {
	cout << "============ 매출 통계 ============" << endl;
	for (int i = 0; i < nItems; i++) {
		if (sales[i] == 0) continue;
		cout << i + 1 << ' ' << setw(15) 
			<< itemList[i].getName()
            << sales[i] << "개\n";
	}
	cout << "매출액 총계 : " << total << "원\n";
}
  1. C++은 메인 함수 부분을 클래스로 묶을 필요없이 그냥 전역함수(클래스 안에 들어있지 않은 함수) main으로 작성하면 됩니다. 그러므로 자바의 메인 클래스 필드도 그냥 전역변수가 됩니다. 
  2. 인클루드에서 item.h를 통해 아이템 클래스 선언부를 사용할 수 있게 됩니다. 여기에 있는 다른 라이브러리 헤더 인클루드도 같이 들어오게 됩니다.
  3. 또한 C/C++은 선언 후 사용 원칙을 따르는 언어이므로 변수와 함수가 사용되기 전에 선언되어야 합니다. 그래서 모든 함수 전방선언이 앞에 나타남을 볼 수 있습니다.
  4. int 배열 선언이 int array[] = new ... 이런 형태에서 int* array = new ... 형태로 바뀌는 것을 볼 수 있습니다. 반환형이나 매개변수로 전달된 배열에 대해서도 int[] 대신 int*로 바꾸어 줍니다.
  5. 또한 C++ 배열은 크기를 알지 못하므로 배열을 매개변수로 보낼 때는 배열의 크기를 별도의 매개변수로 전달해야 합니다. computeTotal과 printOrder의 배열에 대해 sz 매개변수를 추가했습니다.
  6. C++ 배열에서는 for-each를 사용할 수 없으므로 for (int i = 0; ... 형태로 바꾸어 주었습니다.

이번에는 자바와 같이 스토어 클래스를 만들어 봅시다. 다음 코드는 자바의 스토어 클래스를 그대로 C++ 클래스로 옮긴 것입니다.

#include "item.h"
class Store {
    Item* itemList;
    int nItems;
    int* sales;  // 클래스의 필드를 모두 모아서 선언
    int total;
public:
    int* readOrder(int);  // 헤더에서 멤버 함수들을 미리 선언
    int computeTotal(int*, int);
    void printOrder(int*, int, int);
    void printSales();
    void runSale();
};

int main() {					// C++의 메인 함수는 일반 함수로 선언됨
    Store store;
    store.mymain();
    return 0;
}
Store::Store() {
    itemList = NULL;  // C++에서는 멤버 필드를 헤더에서 초기화할 수 없다.
    nItems = 0;			// 생성자에서 초기화해 주어야 함
    total = 0;
}
int Store::mymain() {
    ifstream scan("week5-input.txt");
	scan >> nItems;
	itemList = new Item[nItems];
	sales = new int[nItems];
	for (int i = 0; i < nItems; i++) {
		itemList[i].read(scan);
	}
	for (int i = 0; i < nItems; i++) {
		itemList[i].print();
	}
	runSale();
	cout << "Bye!" << endl;
	system("PAUSE");
}
void Store::runSale() {
	int subtotal = 0;
	int* order = NULL;
	int n;
	while (true) {
		cout  << ">> ";
		cin >> n;
		if (n == 0)
			break;
		order = readOrder(n);
		subtotal = computeTotal(order, n);  // 배열의 길이를 추가 매개변수로 전달
		printOrder(order, n, subtotal);
		total += subtotal;
	}
	printSales();
}
int* Store::readOrder(int count) {
	int* result = new int[count];
	for (int i = 0; i < count; i++) {
		cin >> result[i];
		result[i]--;
	}
	return result;
}
int Store::computeTotal(int* order, int sz) {
	int subtotal = 0, n;
	for (int i = 0; i < sz; i++) {
		n = order[i];
		subtotal += itemList[n].getPrice();
		sales[n]++;
	}
	total += subtotal;
	return subtotal;
}
void Store::printOrder(int* order, int sz, 
				int total) {
	int n;
	for (int i = 0; i < sz; i++) {
		n = order[i];
		itemList[n].print();
	}
	cout << "합계 : " << total << "원\n";
}

void Store::printSales() {
	cout << "============ 매출 통계 ============" << endl;
	for (int i = 0; i < nItems; i++) {
		if (sales[i] == 0) continue;
		cout << i + 1 << ' ' << setw(15) 
			<< itemList[i].getName()
            << sales[i] << "개\n";
	}
	cout << "매출액 총계 : " << total << "원\n";
}

다음은 입력파일입니다. 프로젝트 디렉토리에 이 파일이 input.txt라는 이름으로 저장되어 있어야 프로그램에서 파일을 읽어들일 수 있습니다.

11 
후드티_ST1 5000 f 
후드티_ST2 10000 c 
면티_ST1__ 5000 f 
면티_ST2__ 10000 f 
면티_ST3__ 8800 c 
치마_주름_롱 10000 f 
치마_주름_숏 8000 f 
반바지_청_숏 12000 f 
바지_면_일자 10000 m 
츄리닝____ 8000 c 
줄무늬티___ 7000 c

'C++ << C & Java' 카테고리의 다른 글

C 구조체에서 클래스로 (기초)  (2) 2019.11.19
C++ 클래스 예제 (1) - 음료 검색  (0) 2019.04.13
C++ 객체 변수와 객체의 생성 및 소멸  (0) 2019.04.01
C++의 참조형  (0) 2019.03.28
C++에서 출력 포매팅  (0) 2019.03.28
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함