C-C++/명품 C++ Programming

[(개정판) 명품 C++ programming] 10장 실습 문제

최옥구 2023. 1. 7. 23:00
반응형

1. 배열을 받아 가장 큰 값을 리턴하는 제네릭 함수 biggest()를 작성하라. 또한 main() 함수를 작성하여 biggest()를 호출하는 몇 가지 사례를 보여라.

[결과]

[소스 코드]

#include <iostream>
#include <vector>
using namespace std;

template <class T>
void biggest(T src[], int y) {
	int max;
	for (int i = 0; i < y; i++)
	{
		max = src[0];
		if (max < src[i])
			max = src[i];
	}
	cout << max << endl;
}

int main()
{
	int x[] = { 1, 2, 3, 4, 5 };
	int y[] = { 3, 4, 5, 6, 7 };
	int z[] = { 5 ,6, 7, 8, 10 };
	biggest(x, 5);
	biggest(y, 5);
	biggest(z, 5);
}

 


 

2. 두 개의 배열을 비교하여 같으면 true를, 아니면 false를 리턴하는 제네릭 함수 equalArrays()를 작성하라. 또한 main() 함수를 작성하여 equalArrays()를 호출하는 몇 가지 사례를 보여라. equalArrays()를 호출하는 코드 사례는 다음과 같다.

[결과]

[소스 코드]

#include <iostream>
#include <vector>
using namespace std;

template <class T>
bool equalArrays(T x[], T y[], int num)
{
	for (int i = 0; i < num; i++)
	{
		if (x[i] != y[i])
		{
			return false;
		}
	}
	return true;
}

int main()
{
	int x[] = { 1, 10, 100, 5, 4 };
	int y[] = { 1, 10, 100, 5, 4 };
	if (equalArrays(x, y, 5)) {
		cout << "같다." << endl;
	}
	else
		cout << "다르다." << endl;
}

 


 

3. 배열의 원소를 반대 순서대로 뒤집는 reverseArray() 함수를 템플릿으로 작성하라. reverseArray()의 첫 번째 매개 변수는 배열에 대한 포인터이며 두 번째 매개 변수는 배열의 개수이다. reverseArray()의 호출 사례는 다음과 같다.

[결과]

[소스 코드]

#include <iostream>
#include <vector>
using namespace std;

template <class T>
void reverseArray(T x[], int size)
{
	int j = size;
	int* p = new int[size];
	for (int i = 0; i < size; i++)
	{
		p[i] = x[--j];
	}
	for (int i = 0; i < size; i++)
	{
		x[i] = p[i];
	}
	delete[]p;
}

int main()
{
	int x[] = { 1, 10, 100, 5, 4 };
	reverseArray(x, 5);
	for (int i = 0; i < 5; i++)
	{
		cout << x[i] << " ";
	}
	cout << endl;
}

 


 

4. 배열에서 원소를 검색하는 search() 함수를 템플릿으로 작성하라. search()의 첫 번째 매개 변수는 검색하고자 하는 원소 값이고, 두 번째 매개 변수는 배열이며, 세 번째 매개 변수는 개수이다. search() 함수가 검색에 성공하면 true를, 아니면 false를 리턴한다. search()의 호출 사례는 다음과 같다.

[결과]

[소스 코드]

#include <iostream>
using namespace std;

template <class T>
bool search(T num, T x[], int size)
{
	for (int i = 0; i < size; i++)
	{
		if (x[i] == num)
			return true;
	}
	return false;
}

int main()
{
	int x[] = { 1, 10, 100, 5, 4 };
	if (search(100, x, 5))
		cout << "100이 배열 x에 포함되어있다. " << endl;
	else if(!search(100, x, 5))
		cout << "100이 배열 x에 포함되어있지 않다." << endl;
}

 


5. 다음 함수는 매개 변수로 주어진 두 개의 int 배열을 연결한 새로운 int 배열을 동적 할당받아 리턴한다. . . .

concatint 배열뿐 아니라 다른 타입의 배열도 처리할 수 있도록 일반화하라.

[결과]

[소스 코드]

#include <iostream>
using namespace std;

template <class T>
T *concat(T a[], int sizea, T b[], int sizeb) {
	T *p = new T[sizea + sizeb];
	for (int i = 0; i < sizea; i++)
		p[i] = a[i];
	for (int i = 0; i < sizeb; i++)
		p[i + sizea] = b[i];

	return p;
}

int main()
{
	int xInt[] = { 1, 2, 3, 4, 5 }; // 정수형 배열일 경우
	int yInt[] = { 6, 7, 8, 9, 10 };
	
	int xSize = size(xInt);
	int ySize = size(yInt);
	int sizeT = xSize + ySize;
	auto *p = concat(xInt, xSize, yInt, ySize);

	for (int i = 0; i < sizeT; i++)
	{
		cout << p[i] << " ";
	}
	cout << endl;
	delete[] p;

	double xDouble[] = { 1.1, 2.2, 3.3, 4.4, 5.5 }; // 실수형 배열일 경우
	double yDouble[] = { 6.6, 7.7, 8.8, 9.9, 10.1 };

	xSize = size(xDouble);
	ySize = size(yDouble);
	sizeT = xSize + ySize;
	auto *q = concat(xDouble, xSize, yDouble, ySize);

	for (int i = 0; i < sizeT; i++)
	{
		cout << q[i] << " ";
	}
	cout << endl;
	delete[] q;
}

 


 

6. 다음 함수는 매개 변수로 주어진 int 배열 src에서 배열 minus에 들어있는 같은 정수를 모두 삭제한 새로운 int 배열을 동적으로 할당받아 리턴한다. retSize는 remove() 함수의 실행 결과를 리턴하는 배열의 크기를 전달받는다.

[결과]

[소스 코드]

#include <iostream>
using namespace std;

template <class T>
T* remove(T src[], int sizeSrc, T minus[], int sizeMinus, int& retsize) 
{
    int j;
    T* Array = new T[sizeSrc];
    for (int i = 0; i < sizeSrc; i++) 
    {
        for (j = 0; j < sizeMinus; j++) 
        {
            if (src[i] == minus[j]) 
            {
                --j;
                break;
            }
        }
        if (j == sizeMinus) 
        { 
            Array[retsize] = src[i]; 
            retsize++;  
        }
    }
    return Array;
}

int main() 
{
    int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int b[] = { 2, 4, 6, 8, 10 };
    int retSize = 0;
    int* Arr = remove(a, 10, b, 5, retSize);

    for (int i = 0; i < retSize; ++i)
        cout << Arr[i] << ' ';
    cout << endl;

    delete[] Arr;
}

 


 

7. 다음 프로그램은 컴파일 오류가 발생한다. 소스의 어디에서 왜 컴파일 오류가 발생하는가?

// <객체 자체를 템플릿 함수로 비교하려고 하고있기 때문이다.>

template <class T>
T bigger(T a, T b)
{
	if (a > b)
		return a;
	else
		return b;
}

int main()
{
	int a = 20, b = 50, c;
	c = bigger(a, b);
	cout << "20과 50중 큰 값은 " << c << endl;
	Circle waffle(10), pizza(20), y;
	y = bigger(waffle, pizza);
	cout << "waffle과 pizza 중 큰 것의 반지름은 " << y.getRadius() << endl;
}

아래와 같이 결과가 출력되도록 프로그램을 수정하라.

[결과]

[소스 코드]

#include <iostream>
using namespace std;

class Circle
{
	int radius;
public:
	Circle(int radius = 1) { this->radius = radius; }
	int getRadius() { return radius; }
};

template <class T>
T bigger(T a, T b)
{
	if (a > b)
		return a;
	else
		return b;
}

int main()
{
	int a = 20, b = 50, c;
	c = bigger(a, b);
	cout << "20과 50중 큰 값은 " << c << endl;
	Circle waffle(10), pizza(20), y;
	y = bigger(waffle.getRadius(), pizza.getRadius());
	cout << "waffle과 pizza 중 큰 것의 반지름은 " << y.getRadius() << endl;
}

 


 

8. 문제 7을 푸는 다른 방법을 소개한다. bigger() 함수의 다음 라인에서 > 연산자 때문에 T에 Circle과 같은 클래스 타입이 대입되면, 구체화가 실패하여 컴파일 오류가 발생한다. 이 문제를 해결하기 위해 다음과 같은 추상 클래스 Comparable을 제안한다. Circle 클래스가 Comparable을 상속받아 순수 가상 함수를 모두 구현하면, 앞의 bigger() 템플릿 함수를 사용하는데 아무 문제 없다. Circle뿐 아니라, Comparable을 상속받은 모든 클래스를 bigger()에 사용할 수 있다. Comparable을 상속받는 Circle 클래스를 완성하고 문제 7의 main()을 실행하여 테스트하라.

[결과]

[소스 코드]

#include <iostream>
using namespace std;

class Comparable
{
public:
	virtual bool operator > (Comparable& op2) = 0;
	virtual bool operator < (Comparable& op2) = 0;
	virtual bool operator == (Comparable& op2) = 0;
};


class Circle : public Comparable
{
	int radius;
public:
	Circle(int radius = 1) { this->radius = radius; }
	int getRadius() { return radius; }

    bool operator > (Comparable& op2) 
    {
        Circle* c;
        c = (Circle*)&op2;
        if (this->radius > c->getRadius())
            return true;
        return false;
    }
    bool operator < (Comparable& op2) 
    {
        Circle* c;
        c = (Circle*)&op2;
        if (this->radius < c->getRadius())
            return true;
        return false;
    }
    bool operator == (Comparable& op2) 
    {
        Circle* c;
        c = (Circle*)&op2;
        if (this->radius == c->getRadius())
            return true;
        return false;
    }
};

template <class T>
T bigger(T a, T b)
{
	if (a > b)
		return a;
	else
		return b;
}

int main()
{
	int a = 20, b = 50, c;
	c = bigger(a, b);
	cout << "20과 50중 큰 값은 " << c << endl;
	Circle waffle(10), pizza(20), y;
	y = bigger(waffle, pizza);
	cout << "waffle과 pizza 중 큰 것의 반지름은 " << y.getRadius() << endl;
}
 

9. STL의 vector 클래스를 이용하는 간단한 프로그램을 작성해 보자. vector 객체를 생성하고, 키보드로부터 정수를 입력받을 때마다 정수를 벡터에 삽입하고 지금까지 입력된 수와 평균을 출력하는 프로그램을 작성하라. 0을 입력하면 프로그램이 종료한다.

[결과]

[소스 코드]

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	int num;	
	double sum{};
	while (true)
	{
		cout << "정수를 입력하세요(0을 입력하면 종료) >> ";
		cin >> num;
		if (num == 0)
			return 0;
		v.push_back(num);

		for (int i = 0; i < size(v); i++){
			cout << v[i] << " ";
			sum += v[i];
		}
		cout << endl;
		double regular = sum / size(v);
		cout << "평균 = " << regular << endl;
		sum = 0;
	}
}

 


 

10. 나라의 수도 맞추기 게임에 vector를 활용해 보자. 나라 이름(nation)과 수도 (capital) 문자열로 구성된 Nation 클래스를 만들고, vector<Nation> v;로 생성한 벡터를 이용하여 나라 이름과 수도 이름을 삽입할 수도 있고 랜덤하게 퀴즈를 볼 수도 있다. 프로그램 내에서 벡터에 Nation 객체를 여러 개 미리 삽입하여 퀴즈를 보도록 하라. 실행 화면은 다음과 같으며, 저자는 9개의 나라의 이름과 수도를 미리 프로그램에 삽입하였다. 문자열은 string 클래스를 이용하라.

[결과]

[소스 코드]

#include <iostream>
#include <string>
#include <vector>
using namespace std;

class Nation
{
protected:
	string nations;
	string capitals;
public:
	Nation(string nations, string capitals) { this->nations = nations; this->capitals = capitals; }
	bool isCorrect(string capitals)
	{
		if (capitals != this->capitals)
			return false;
		else if (capitals == this->capitals)
			return true;
	}
	void getNation() { cout << nations << "의 수도는? >> "; }
	
};

int main()
{
	int choice;
	vector <Nation> v;
	string nations;
	string capitals;
	cout << "***** 나라의 수도 맞추기 게임을 시작합니다! *****" << endl;
	while (true)
	{
		cout << "정보 입력 : 1, 퀴즈 : 2, 종료 : 3 >> ";
		cin >> choice;
		if (choice == 1)
		{
				cout << "현재 " << size(v) << "의 나라가 입력되어 있습니다." << endl;
				cout << "나라와 수도를 입력하세요(no no 이면 입력 끝)" << endl;
				while (true)
				{
					cout << size(v) + 1 << " >> ";
					cin >> nations >> capitals; 
					if (nations == "no" && capitals == "no")
						break;
					else {
						Nation nation(nations, capitals);
						v.push_back(nation);
					}
				}
		}
		else if (choice == 2)
		{
			for (int i = 0; i < size(v); i++)
			{
				v[i].getNation();
				cin >> capitals;
				if (capitals == "exit")
					break;
				if (v[i].isCorrect(capitals))
					cout << "Correct !!" << endl;
				else if (!v[i].isCorrect(capitals))
					cout << "NO !!!" << endl;
			}
		}
		else if (choice == 3)
		{
			return 0;
		}
	}
}

 


 

11. 책의 연도, 책 이름, 저자 이름을 담은 Book 클래스를 만들고, Vector<Book> v;로 생성한 벡터를 이용하여 책을 입고하고, 저자와 연도로 검색하는 프로그램을 작성하라.

[결과]

cin.ignore()을 사용하여 연속된 getline()을 사용할 때 발생하는 오류를 방지했습니다. ignore() 함수는 앞 버퍼에 있는 맨 마지막 문자를 지우는 역할을 합니다.

[소스 코드]

#include <iostream>
#include <string>
#include <vector>
using namespace std;

class Book {
protected:
	string name;
	int year;
	string writer;
public:
	Book(int year, string name, string writer)
	{
		this->year = year;
		this->name = name;
		this->writer = writer;
	}
	string getName()
	{
		return name;
	}
	string getWriter()
	{
		return writer;
	}
	int getYear()
	{
		return year;
	}
};

int main() {
	vector<Book> bv;
	string name;
	string writer;
	int year;
	cout << "입고할 책을 입력하세요. 년도에 -1을 입력하면 종료합니다. " << endl;
	while (true)
	{
		cout << "년도 >> ";
		cin >> year;
		if (year == -1) {
			cout << "총 입고된 책은 " << size(bv) << "입니다. " << endl;
			break;
		}
		cin.ignore();
		cout << "책 이름 >> ";
		getline(cin, name);

		cout << "저자 >> ";
		getline(cin, writer);

		Book book(year, name, writer);
		bv.push_back(book);
	}
	cin.ignore(); 
	cout << "검색하고자 하는 책의 저자 이름을 입력하세요 >> ";
	getline(cin, writer);
	for (int i = 0; i < size(bv); i++)
	{
		if (bv[i].getWriter() == writer)
			cout << bv[i].getYear() << ", " << bv[i].getName() << ", " << bv[i].getWriter() << endl;
	}
	cout << "검색하고자 하는 책의 출간 연도를 입력하세요 >> ";
	cin >> year;
	for (int i = 0; i < size(bv); i++)
	{
		if (bv[i].getYear() == year)
			cout << bv[i].getYear() << ", " << bv[i].getName() << ", " << bv[i].getWriter() << endl;
	}
}

 


 

12. Open Challenge를 수정하여 사용자가 어휘를 삽일할 수 있도록 기능을 추가하라. 실행 예는 다음과 같다. . . .

[결과]

[소스 코드]

#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>
using namespace std;

class Word {
    string engWord, korWord;
public:
    Word(string engWord = 0, string korWord = 0) {
        this->engWord = engWord;
        this->korWord = korWord;
    }
    void inputWord(string engWord, string korWord) {
        this->engWord = engWord;
        this->korWord = korWord;
    }
    string getEngWord() { return engWord; }
    string getKorWord() { return korWord; }
};

void inputWord(vector<Word>& v) {
    cout << "영어 단어에 exit을 입력하면 입력 끝\n";
    string engWord, korWord;

    while (true) {
        cout << "영어 >>"; cin >> engWord;
        if (engWord == "exit")
            break;
        cout << "한글 >>"; cin >> korWord;
        v.push_back(Word(engWord, korWord));
    }
}

void gameStart(vector<Word>& v) {
    srand((unsigned)time(0));
    int num;

    cout << "영어 어휘 테스트를 시작합니다. 1~4 외 다른 입력시 종료.\n";

    while (true) {
        int randNum = rand() % v.size();
        cout << v.at(randNum).getEngWord() << "?\n";
        string ex[4] = {};
        int tmp = rand() % 4;
        ex[tmp] = v.at(randNum).getKorWord();
        for (int i = 0; i < 4; i++) {
            if (tmp == i)
                continue;
            while (true) { // 중복되는 숫자가 없게 처리
                int numChosen = rand() % v.size();
                if (ex[0] != v.at(numChosen).getKorWord() && ex[1] != v.at(numChosen).getKorWord() &&
                    ex[2] != v.at(numChosen).getKorWord() && ex[3] != v.at(numChosen).getKorWord()) {
                    ex[i] = v.at(numChosen).getKorWord();
                    break;
                }
            }
        }
        for (int i = 0; i < 4; i++)
            cout << "(" << i + 1 << ") " << ex[i] << ' ';
        cout << ":>";
        cin >> num; // 숫자만 입력 
        if (num == -1 || num == 0 || num > 4) { // 입력받은 값이 -1이거나 1~4 외 다른 입력시 종료 
            break;
        }
        else {
            if (ex[num - 1] == v.at(randNum).getKorWord()) // 정답이면 실행
                cout << "Excellent !!\n";
            else
                cout << "No. !!\n";
        }
    }
}

int main() {
    int num;

    vector<Word> v;
    v.push_back(Word("honey", "애인"));
    v.push_back(Word("dall", "인형"));
    v.push_back(Word("painting", "그림"));
    v.push_back(Word("stock", "주식"));
    v.push_back(Word("bear", "곰"));
    v.push_back(Word("deal", "거래"));

    cout << "***** 영어 어휘 테스트를 시작합니다. *****\n";
    while (true) {
        cout << "어휘 삽입: 1, 어휘 테스트 : 2, 프로그램 종료:그외키 >> ";
        cin >> num;
        switch (num) {
        case 1:
            inputWord(v);
            cout << endl;
            break;
        case 2:
            gameStart(v);
            cout << endl;
            break;
        default:
            return 0;
        }
    }
}

 


 

13. map 컨테이너를 이용하여 (이름, 성적)을 저장하고 이름으로 성적을 조회하는 점수 관리 프로그램을 만들어라, 이름은 빈칸 없이 입력하는 것을 원칙으로 한다.

[결과]

[소스 코드]

#include <iostream>
#include <vector>
#include <map>
using namespace std;

int main() {
	int choice, score;
	string name;
	map <string, int> dic;
	cout << "***** 점수관리 프로그램 HIGH SCORE을 시작합니다. *****" << endl;
	while (true) {
		cout << "입력 : 1, 조회 : 2, 종료 : 3 >> ";
		cin >> choice;
		if (choice == 1) {
			cout << "이름과 점수 >> ";
			cin >> name >> score;
			dic[name] = score;
		}
		else if (choice == 2) {
			cout << "이름 >> ";
			cin >> name;
			if (dic.find(name) == dic.end())
				cout << "찾을 수 없습니다.";
			else
				cout << dic[name] << endl;
		}
		else if (choice == 3)
			break;
	}
	cout << "종료합니다..";
	return 0;
}

 


 

14. 암호 관리 응용프로그램은 map을 이용하여 작성하라. 실행 과정은 다음과 같다.

[결과]

[소스 코드]

#include <iostream>
#include <vector>
#include <map>
using namespace std;

int main() {
	int choice;
	string name, pass;
	map <string, string> dic;
	cout << "***** 암호 관리프로그램  WHO를 시작합니다. *****" << endl;
	while (true) {
		cout << "삽입 : 1, 검사 : 2, 종료 : 3 >> ";
		cin >> choice;
		if (choice == 1) {
			cout << "이름 암호 >> ";
			cin >> name >> pass;
			dic[name] = pass;
		}
		else if (choice == 2) {
			string passward;
			cout << "이름 ? ";
			cin >> name;
			if (dic.find(name) == dic.end())
				cout << "이름을 찾을 수 없습니다.";
			while (true){
				cout << "암호 ? ";
				cin >> passward;
				if (dic[name] == passward) {
					cout << "통과!!" << endl;
					break;
				}
				else
					cout << "실패~~!" << endl;
			}
		}
		else if (choice == 3)
			break;
	}
	cout << "종료합니다..";
	return 0;
}

 


 

15. vector를 이용하여 아래 Circle 클래스의 객체를 삽입하고 삭제하는 프로그램을 작성하라. 삭제 시에는 이름이 같은 모든 원을 삭제한다.

[결과]

[소스 코드]

#include <iostream>
#include <vector>
#include <map>
using namespace std;

class Circle {
	string name;
	int radius;
public:
	Circle(int radius, string name) {
		this->radius = radius;
		this->name = name;
	}
	double getArea() { return 3.14 * radius * radius; }
	string getName() { return name; }
};

int main() {
	int choice, radius;
	string name;
	vector<Circle*> v;
	cout << "원을 삭제하고 삽입하는 프로그램입니다. \n";
	while (true){
		cout << "삽입 : 1, 삭제 : 2, 모두복기 : 3, 종료 : 4 >> ";
		cin >> choice;
		if (choice == 1) {
			cout << "생성하고자 하는 원의 반지름과 이름은 >> ";
			cin >> radius >> name;
			v.push_back(new Circle (radius, name));
		}
		else if (choice == 2) {
			vector<Circle*>::iterator it;
			it = v.begin();
			cout << "삭제하고자 하는 원의 이름은 >> ";
			cin >> name;
			for(int i = 0; i < size(v); i++)
			{
				if (v[i]->getName() == name) {
					it = v.erase(it);
					break;
				}
				it++;
			}
		}
		else if (choice == 3) {
			for (int i = 0; i < size(v); i++){
				cout << v[i]->getName() << endl;
			}
			cout << endl;
		}
		else if (choice == 4) {
			break;
		}
	}
	cout << "프로그램을 종료합니다..";
	return 0;
}

 


 

16. vector<Shape*> v;를 이용하여 간단한 그래픽 편집기를 콘솔 바탕으로 만들어보자. Shape과 Circle, Line, Rect 클래스는 다음과 같다. 생성된 도형 객체를 v에 삽입하고 관리하라. 9장 실습문제 10번의 힌트를 참고하라.

[결과]

[소스 코드]

#include <iostream>
#include <vector>
#include <map>
using namespace std;

class Shape {
protected:
	virtual void draw() = 0;
public:
	void paint() { draw(); }
};

class Circle : public Shape {
protected:
	void draw() { cout << "Circle" << endl; }
};

class Rect : public Shape {
protected:
	void draw() { cout << "Rectangle" << endl; }
};

class Line : public Shape {
protected:
	void draw() { cout << "Line" << endl; }
};



int main() {
	int choice, index;
	string name;
	vector<Shape*> v;
	cout << "그래픽 에디터입니다. \n";
	while (true){
		cout << "삽입 : 1, 삭제 : 2, 모두복기 : 3, 종료 : 4 >> ";
		cin >> choice;
		if (choice == 1) {
			int choiceShape;
			cout << "선 : 1, 원 : 2, 사각형 : 3 >> ";
			cin >> choiceShape;
			if (choiceShape == 1) {
				v.push_back(new Line);
			}
			else if (choiceShape == 2) {
				v.push_back(new Circle);
			}
			else if (choiceShape == 3) {
				v.push_back(new Rect);
			}
		}
		else if (choice == 2) {
			vector<Shape*>::iterator it;
			it = v.begin();
			cout << "삭제하고자 하는 도형의 인덱스 >> ";
			cin >> index;
			for (int i = 0; i < index; i++){
				it++;
			}
			it = v.erase(it);
		}
		else if (choice == 3) {
			for (int i = 0; i < size(v); i++){
				cout << i << " : ";
				v[i]->paint();
			}
		}
		else if (choice == 4) {
			break;
		}
	}
	cout << "프로그램을 종료합니다..";
	return 0;
}

 


 

책에서 나오는 힌트나 조건들의 일부는 저작자 존중을 위해 일부러 작성하지 않았습니다. 

개인 공부용으로 올리는 만큼 풀이에 대한 지적 감사하겠습니다.

반응형