상세 컨텐츠

본문 제목

[객체지향언어1] 명품 C++ Programming 6장 실습 문제

C++/문제 풀이

by uzichoi 2025. 6. 14. 16:03

본문

[C++] 명품 C++ Programming 6장 연습, 실습 문제풀이

 

 

0. 알아두자!!

  1. 오버로딩에서 중복 판별(함수 선택)과 바인딩은 모두 컴파일 타임에 결정된다. 이로 인해 런타임에는 오버헤드 없이 곧바로 해당 함수가 호출된다.
  2. 중복 함수의 호출 시 어떤 함수를 호출하는지 판단할 수 없는 '모호성' 문제는 다음과 같은 상황에서 발생한다.
    1. 형 변환으로 인한 모호성
    2. 참조 매개 변수로 인한 모호성
    3. 디폴트 매개 변수로 인한 모호성
  3. 정적 멤버 함수는 비정적 멤버 변수/함수에 접근할 수 없다. 정적 멤버 함수는 클래스 인스턴스 없이 호출되므로, this 포인터가 존재하지 않으며, 이로 인해 비정적 멤버(객체에 속한 값)에 접근할 수 없다.
  4. 클래스 이름만으로 비정적 멤버 함수에 접근할 수 없다. 비정적(non-static, instance) 멤버 함수는 컴파일러에 의해 암묵적으로 *this를 매개 변수를 가지는데, 이는 호출 객체가 있어야만 생성되기 때문이다. 그러나 클래스 이름만으로는 대상 인스턴스를 지정할 수 없으므로 호출 불가능하다. 
  5. 객체(인스턴스)는 범위 지정 연산자를 사용할 수 없다. 객체는 메모리에 존재하는 인스턴스이므로 클래스 범위를 지시하는 :: 연산자를 사용할 수 없다. 객체를 통해 멤버에 접근할 때는 . 또는 ->를 사용한다. ::는 클래스나 네임스페이스처럼 정적인 범위에 속한 멤버를 지정할 때만 사용할 수 있다.

 

 

 

1. 디폴트 매개 변수를 가진 add() 함수를 작성하고 프로그램을 완성하라.

<실행화면>

<소스코드>

#include <iostream>
using namespace std;

int add(int arr[], int size = 1) {
	int sum = 0;
	for (int i = 1; i < size; i++) {
		sum += arr[i];
	}
	return sum;
}

int main() {
	int a[] = { 1,2,3,4,5 };
	int b[] = { 6,7,8,9,10 };
	int c = add(a, 5);
	int d = add(b, 5);

	cout << c << endl;
	cout << d << endl;

	return 0;
}

 

 

 

2. Person 클래스의 생성자 함수를 디폴트 매개 변수를 가진 하나의 생성자로 완성하라.

<실행화면>

<소스코드>

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

class Person {
	int id; 
	double weight;
	string name;
public:
	Person(int id = 1, string name = "Grace", double weight = 20.5) {
		this->id = id;
		this->name = name;
		this->weight = weight;
	}
	void show() {
		cout << id << ' ' << weight << ' ' << name << endl;
	}
};

int main() {
	Person grace, ashley(2, "Ashley"), helen(3, "Helen", 32.5);
	grace.show();
	ashley.show();
	helen.show();

	return 0;
}

 

 

 

3. big() 함수를 디폴트 매개 변수를 가진 하나의 함수로 작성하라.

<실행화면>

<소스코드>

 

#include <iostream>
using namespace std;

int big(int a, int b, int max = 100) {
	int bigger = a > b ? a : b;
	if (bigger > max) {
		return max;
	}
	else
		return bigger;
}

int main() {
	int x = big(3, 5);
	int y = big(300, 60);
	int z = big(30, 60, 50);

	cout << x << ' ' << y << ' ' << z << endl;

	return 0;
}

 

 

 

4. 다음 클래스에 중복된 생성자를 디폴트 매개 변수를 가진 하나의 생성자로 작성하고 테스트 프로그램을 작성하라.

<실행화면>

<소스코드>

#include <iostream>
using namespace std;

class MyVector {
    int* mem;
    int size;
public:
    MyVector(int n = 100, int val = 0); // 디폴트 매개 변수는 선언부에만 작성
    ~MyVector() { delete[] mem; }
    void show();
};

MyVector::MyVector(int n, int val) {	// 중복 정의 방지
    mem = new int[n];
    size = n;
    for (int i = 0; i < size; i++) {
        mem[i] = val;
    }
}

void MyVector::show()
{
    for (int i = 0; i < size; i++)
        cout << mem[i] << ' ';
    cout << endl;
}

int main() {
    int x, y;
    x = 10, y = 5;
    MyVector Vector1; 	// 0을 100번 출력
    MyVector Vector2(x, y);
    Vector1.show();
    Vector2.show();

	return 0;
}

 

 

 

5. 동일한 크기로 배열을 변환하는 다음 2개의 static 멤버 함수를 가진 ArrayUtility 클래스를 만들어라/.

<실행화면>

<소스코드>

#include <iostream>
using namespace std;

class ArrayUtility {
public:
	//ArrayUtility();
	static void intToDouble(int source[], double dest[], int size) {
		for (int i = 0; i < size; i++) {
			dest[i] = source[i];
		}
	}
	static void doubleToInt(double source[], int dest[], int size) {
		for (int i = 0; i < size; i++) {
			dest[i] = source[i];
		}
	}
};

int main()
{
	int x[] = { 1, 2, 3, 4, 5 };
	double y[5];
	double z[] = { 9.9, 8.8, 7.7, 6.6, 5.6 };

	ArrayUtility::intToDouble(x, y, 5);
	for (int i = 0; i < 5; i++)
		cout << y[i] << ' ';
	cout << endl;

	ArrayUtility::doubleToInt(z, x, 5);
	for (int i = 0; i < 5; i++)
		cout << x[i] << ' ';
	cout << endl;
}

 

 

 

6. 동일한 크기의 배열을 변환하는 다음 2개의 static 멤버 함수를 가진 ArrayUtility2 클래스를 만들고, 이 클래스를 이용하여 아리 결과와 같이 출력되도록 프로그램을 완성하라.

<실행화면>-

<소스코드>

#include <iostream>
using namespace std;

class ArrayUtility {
public:
    // s1과 s2 연결한 새로운 배열을 동적 생성하고 포인터 리턴
    static int* concat(int s1[], int s2[], int size);
   
    // s1에서 s2에 있는 숫자를 모두 삭제한 새로운 동적 배열을 동적 생성하여 리턴 
    // 리턴하는 배열의 크기는 retSize에 전달. retSize == 0이면 NULL 전달
    static int* remove(int s1[], int s2[], int size, int& retSize); 
};

int* ArrayUtility::concat(int s1[], int s2[], int size) {
    int* arr = new int[size];
    for (int i = 0; i < size/2; i++) {
        arr[i] = s1[i];
    }
    for (int i = 0; i < size/2; i++) {
        arr[i + (size/2)] = s2[i];
    }
    return arr;
}

int* ArrayUtility::remove(int s1[], int s2[], int size, int& retSize) {
    if (retSize == 0) return NULL;

    int* arr = new int[size];

    for (int i = 0; i < size / 2; i++) {
        bool isSame = false;       // 매 반복마다 초기화
        for (int j = 0; j < size / 2; j++) {
            if (s1[i] == s2[j]) {
                isSame = true;     // s1에 s2의 원소가 있으면 true 마킹
                break;             // 반복문 탈출
            }
        }
        if (isSame == false) arr[retSize++] = s1[i];   
    }
    return arr;
}

int main() {
    int y[5];
    int x[5];

    cout << "정수를 5 개 입력하라. 배열 x에 삽입한다>>";
    for (int i = 0; i < 5; i++) 
        cin >> x[i];
    cout << "정수를 5 개 입력하라. 배열 y에 삽입한다>>";
    for (int i = 0; i < 5; i++)
        cin >> y[i];

    cout << "합친 정수 배열을 출력한다" << endl;
    int* s3 = ArrayUtility::concat(x, y, 10);
    for (int i = 0; i < 10; i++) {
        cout << s3[i] << ' ';
    }
    cout << endl;
    delete[] s3;

    int cnt = 0;
    int* s4 = ArrayUtility::remove(x, y, 10, cnt);
    cout << "배열 x[]에서 y[]를 뺀 결과를 출력한다. 개수는 " << cnt << endl;
    for (int i = 0; i < cnt; i++) {
        cout << s4[i] << ' ';
    }
    cout << endl;
    delete[] s4;
}
  • remove() 함수에서는 두 배열의 값을 비교하여 s2[]에 존재하지 않는 s1[]의 원소만 새 배열에 복사한다.
  • 이를 위해 외부 반복문에서 원소 하나를 고정하고, 내부 반복문에서 s2[]를 순회하며 같은 값이 존재하는지 확인한다.
  • 이때 isSame이라는 불리언 변수를 매 외부 반복마다 false로 초기화하고, 내부 루프에서 값이 일치하면 true로 설정하며 break한다.
  • 외부 반복 종료 후 isSame == false이면, 해당 원소는 s2[]에 없는 값이므로 결과 배열 arr[]에 추가된다.
  • 동적 할당한 배열은 delete[]로 꼭 해제해야 메모리 누수 방지가 가능하다.
  • NULL 처리 필요. remove() 결과 배열이 비어있을 수 있으므로, 리턴 전에 retSize == 0 검사하여 return NULL; 처리해야 한다.

 

 

 

7. 다음과 같은 static 멤버를 가진 Random 클래스를 완성하라. 그리고 Random 쿨래스를 이용하여 다음과 같이 랜덤한 값을 출력하는 main() 함수도 작성하라. main() 에서 Random 클래스의 seed() 함수를 활용하라.

class Random {
public:
	static void seed() { srand((unsigned)time(0)); }
	static int nextInt(int min = 0, int max = 32767);
	static char nextAlphabet();
	static double nextDouble();
};

<실행화면>

<소스코드>

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

class Random {
public:
	static void seed() { srand((unsigned)time(0)); }
	static int nextInt(int min = 0, int max = 32767) {
		int range = max - min + 1;
		int r = rand() % range + min;
		return r;
	}
	static char nextAlphabet() {
		char c = rand() % 52;	// 대소문자 각 26개
		if (c < 26) return 'a' + c;		// 소문자
		else return 'A' + c - 26;		// 대문자
	}
	static double nextDouble() {
		return (double)rand() / 32767;
	}
};

int main() {
	Random::seed();

	cout << "1에서 100까지 랜덤한 정수 10개를 출력합니다" << endl;
	for (int i = 0; i < 10; i++) {
		cout << Random::nextInt(1, 100) << ' ';
	}
	cout << endl;

	cout << "알파벳을 랜덤하게 10개를 출력합니다" << endl;
	for (int i = 0; i < 10; i++) {
		cout << Random::nextAlphabet() << ' ';
	}
	cout << endl;

	cout << "랜덤한 실수를 10개를 출력합니다" << endl;
	for(int i=0; i<10; i++){
		if (i == 5) cout << endl;
		cout << Random::nextDouble() << ' ';
	}
	cout << endl;

	return 0;
}
  • 정수 난수의 범위 지정: int r = rand() % (max - min + 1) + min;
  • 알파벳 난수: 알파벳 총 개수(52개), 아스키코드 이용
  • 실수 난수: rand()는 0 이상 RAND_MAX(윈도우: 32767) 이하의 정수를 생성한다. 따라서 생성된 난수를 RAND_MAX로 나누면 0.0 이상 1.0 이하의 실수가 생성된다. 그러나 RAND_MAX는 시스템마다 다르므로 위 문제에서는 안정성을 위해 32767로 작성하였다.

 

 

 

8. 디버깅에 필요한 정보를 저장하는 Trace 클래스를 만들어보자. 저자의 경험에 의하면, 멀티테스크 프로그램을 개발하거나 특별한 환경에서 작업할 때, Visual Studio의 디버거와 같은 소스 레벨 디버거를 사용하지 못하는 경우가 더러 있었고, 이때 실행 도중 정보를 저장하기 위해 Trace 클래스를 만들어 사용하였다. Trace클래스를 활용하는 다음 프로그램과 결과를 참고하여 Trace 클래스를 작성하고 전체 프로그램을 완성하라. 디버깅 정보는 100개로 제한한다.

<실행화면>

<소스코드>

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

class Trace {
public:
	static int max, num;
	static string trace[100][2];	
	static void put(string tag, string info);	// tag: 태그, info: 디버깅 정보
	static void print(string tag);
};

int Trace::max = 100;
int Trace::num = 0;
string Trace::trace[100][2];

void Trace::put(string function, string info) {
	if (num < max) {	// 디버깅 정보 최대 100개로 제한
		trace[num][0] = function;	
		trace[num++][1] = info;		// info까지 저장 후 인덱스 번호 증가
	}
	else {
		cout << "디버깅 정보 100개 초과" << endl;
		return;
	}
}

void Trace::print(string tag = "all") {
	if (tag == "all") {
		cout << "----- 모든 Trace 정보를 출력합니다. -----" << endl;
		for (int i = 0; i < num; i++) {
			cout << trace[i][0] << ":" << trace[i][1] << endl;
		}
	}
	else {
		cout << "----- " << tag << "태그의 Trace 정보를 출력합니다. -----" << endl;
		for (int i = 0; i < num; i++) {
			if (trace[i][0] == tag) {
				cout << trace[i][0] << ":" << trace[i][1] << endl;
			}
		}
	}
}

void f() {
	int a, b, c;
	cout << "두 개의 정수를 입력하세요>>";
	cin >> a >> b;
	Trace::put("f()", "정수를 입력 받았음");		// 디버깅 정보 저장
	c = a + b;
	Trace::put("f()", "합 계산");	// 디버깅 정보 저장
	cout << "합은 " << c << endl;
}

int main() {
	Trace::put("main()", "프로그램 시작합니다");	// 디버깅 정보 저장
	f();
	Trace::put("main()", "종료");	// 태그, 디버깅 정보

	Trace::print("f()");	// f() 태그를 가진 디버깅 정보 모두 출력
	Trace::print();		// 모든 디버깅 정보 출력

	return 0;
}
  • static 멤버 변수는 반드시 전역 공간에 정의해야 한다. 단순 선언만으로는 공간이 만들어지지 않기 때문에, 링커가 연결할 '실제 메모리 공간'을 알 수 있도록 정의가 필요하다.

 

 

 

9. 게시판 프로그램을 작성해보자. 멀티테스킹의 경우 여러 사용자들이 게시판에 글을 올리기 때문에 게시판 객체는 전체 하나만 있어야 한다. 그러므로 게시판 객체의 멤버들은 static으로 작성한다. 다음은 게시판 기능을 하는 Board 클래스를 활용하는 main() 코드이다. 실행 결과를 참고하여 Board 클래스를 만들고 전체 프로그램을 완성하라. static 연습이 목적이기 때문에 게시판 기능을 글을 올리는 기능과 게시글을 모두 출력하는 기능으로 제한하고 main()도 단순화하였다.

<실행화면>

<소스코드>

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

class Board {
public:	
	static int max, num;
	static string board[100];
	static void add(string text) {
		if (num < max) board[num++] = text;
		else {
			cout << "게시판이 포화 상태입니다." << endl;
			return;
		}
	}
	static void print() {
		cout << "*************** 게시판입니다. ***************" << endl;
		for (int i = 0; i < num; i++) {
			cout << i << ": " << board[i] << endl;
		}
		cout << endl;
	}
};

int Board::max = 100;
int Board::num = 0;
string Board::board[100];

int main()
{
	Board::add("중간고사는 감독 없는 자율 시험입니다.");
	Board::add("코딩 라운지 많이 이용해 주세요");
	Board::print();
	Board::add("진소린 학생이 경진대회 입상하였습니다. 축하해주세요");
	Board::print();
}

 

  • 외부에서 접근하도록 하기 위해서는 반드시 접근 지정자를 public으로 설정해야 한다.
  • 8번과 마찬가지로 정적 멤버 변수를 전역 공간에서 정의해야 링크 오류가 발생하지 않는다.

 

관련글 더보기