학습 개요
- 클래스 선언, 객체의 정의 및 활용 방법을 프로그램 예를 통해 복습함
- 스택을 표현하는 클래스를 활용하는 예를 학습함
- 스택은 Last-In-First-Out 특성을 갖는 자료 구조임
- 복소수를 표현하는 클래스를 활용하는 예를 학습함
- 클래스를 선언하는 형식, 생성자의 활용, 여러 가지 멤버의 선언 방법 등을 다시 한번 익혀 봄
- C++11 이후 사용할 수 있는 생성자의 활용법을 심화 학습함
학습 목표
- 스택을 클래스로 선언하여 활용할 수 있음
- 복소수를 표현하는 클래스를 선언하여 활용할 수 있음
- 소속 클래스의 다른 생성자를 이용하여 다른 생성자를 정의할 수 있음
- 초기화 리스트 생성자를 정의할 수 있음
주요 용어
- 스택(stack)
push
와pop
연산을 이용하여 데이터를 저장하고 인출할 수 있는 자료 구조로서, 나중에 저장된 데이터가 먼저 인출될 수 있는 특성(LIFO: Last In, First Out)을 갖음
- 위임 생성자(delegating constructor)
- 클래스의 다른 생성자를 이용하여 선언되는 생성자로서, 생성자를 작성하는 코드의 중복을 줄일 수 있음
- 초기화 리스트 생성자(initializer-list constructor)
- 첫 번째 매개 변수가
std::initializer_list<Type>
인 생성자
- 첫 번째 매개 변수가
강의록
스택 클래스 - CharStack
스택의 개념
- 스택(stack)
- 데이터를 저장하는 자료 구조의 하나
- 스택의 기본 연산
push
- 데이터를 저장하는 연산
pop
- 마지막으로 저장한 데이터를 인출하는 연산
- LIFO(Last In, First Out)
- 스택의 동작
push('a')
,push('b')
,push('c')
에 의해 스택에 데이터가 추가 됨pop()
에 의해 스택에 데이터 인출 됨c → b → a 순서로 인출 됨
스택 구현을 위한 구조 설계
예제 - CharStack 클래스
- CharStack 클래스
- 문자를 최대 20개까지 저장할 수 있는 스택 객체를 만들 수 있는 CharStack 클래스를 선언하라.
- CharStack 객체는 문자 데이터를 저장(
push
)하거나 인출(pop
)할 수 있으며, 스택이 비어있는지, 가득 차 있는 지를 검사할 수 있다.
행위
멤버함수 비고 CharStack()
생성자 bool chkEmpty()
스택이 비어 있는지 검사함 bool chkFull()
스택이 가득 차 있는지 검사함 bool push(char)
스택에 데이터를 저장함 char pop()
스택에서 데이터를 꺼냄 속성
데이터 멤버 비고 int top
가장 위에 있는 데이터 위치를 가리킴 char buf[20]
데이터 저장 공간
예제: CharStack 클래스 - CharStack.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class CharStack {
enum { size = 20 }; // 스택의 크기
int top; // 마지막 데이터를 가리키는 포인터
char buf[size]; // 스택의 저장 공간
public:
CharStack() : top{ size } {} // 생성자
bool chkEmpty() const { // 스택에 데이터가 없으면 true
return top == size;
}
bool chkFull() const { // 스택이 가득 차 있으면 true
return !top;
}
bool push(char ch); // 스택에 데이터를 넣음
char pop(); // 스택에서 데이터를 꺼냄
};
예제: CharStack 클래스 - CharStack.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include "CharStack.h"
using namespace std;
bool CharStack::push(char ch)
{
if (chkFull()) {
cout << "스택이 가득 차 있습니다." << endl;
return false;
}
buf[--top] = ch; // 스택에 공간이 있으면 저장
return true;
}
char CharStack::pop()
{
if (chkEmpty()) {
cout << "스택에 데이터가 없습니다." << endl;
return 0;
}
return buf[top++]; // top 위치의 데이터 반환
}
push('b')
pop()
→ b 반환
예제: CharStack 클래스 - CSMain.cpp
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
#include <iostream>
#include "CharStack.h"
using namespace std;
int main()
{
CharStack chStack; // 20개의 문자를 저장할 수 있는 스택
char str[20];
cout << "영어 단어를 입력하시오 : ";
cin >> str;
char* pt = str; // 포인터로 문자열 시작 위치를 가리킴
while (*pt) // 문자열의 끝이 아니면 반복
{
chStack.push(*(pt++)); // 스택에 문자를 넣음
}
cout << "역순 단어 출력 : ";
while (!chStack.chkEmpty()) // 스택이 비어 있지 않으면 반복
{
cout << chStack.pop(); // 스택에서 인출한 문자를 출력
}
cout << endl;
return 0;
}
복소수 클래스 - Complex1
복소수
- 복소수(Complex Number)
- 실수 부(real part)와 허수 부(imaginary part)로 구성될 수 있는 수
- 복소수의 표현
a + jb
a
- 실수 부의 값
b
- 허수 부의 값
j
- 허수 단위로서, j^2 = -1임
- 켤레 복소수 (complex conjugate)
- 허수 부의 부호가 반대인 복소수
- a + jb의 켤레 복소수는 a - jb임
- 복소수 연산
- 덧셈(뺄셈)
- 실수 부의 합(차)과 허수 부의 합(차)을 각각 구함
- (a + jb) + (d + je) = (a + d) + j(b + e)
- (a + jb) - (d + je) = (a - d) + j(b - e)
- 곱셈
- (a + jb)(d + je) = (ad - be) + j(ae + bd)
- 나눗셈
- a + jb / d + je = ad + be / d^2 + e^2 + j (bd - ae) / (d^2 + e^2)
- 덧셈(뺄셈)
예제: Complex1 클래스
- Complex1 클래스
- 복소수를 표현하는 클래스를 선언하라.
- 복소수의 사칙 연산 및 켤레 복소수를 구하는 멤버 함수를 포함하며, 실수 부의 값이 a, 허수 부의 값이 b일 때 (a + jb) 형태로 출력할 수 있도록 한다.
행위
멤버 함수 비고 Complex1(double r, double i)
생성자 Complex1 conj()
켤레 복소수 반환 Complex1 add(const Complex1& c)
덧셈 Complex1 sub(const Complex1& c)
뺄셈 Complex1 mul(const Complex1& c)
곱셈 Complex1 div(const Complex1& c)
나눗셈 void display()
복소수의 값 출력 속성
데이터 멤버 비고 double rPart
실수 부의 값 double iPart
허수 부의 값
예제: Complex1 클래스 - Complex1.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Complex1 {
double rPart, iPart; // 실수 부 및 허수 부
public:
// 생성자
Complex1(double r=0, double i=0) : rPart(r), iPart(i) {}
Complex1 conj() const {
return Complex1(rPart, -iPart); // Complex1 클래스의 임시 객체를 생성 -> 문장 실행 후 소멸
}
Complex1 add(const Complex1& c) const {
return Complex1(rPart + c.rPart, iPart + c.iPart); // Complex1 클래스의 임시 객체를 생성 -> 문장 실행 후 소멸
}
Complex1 sub(const Complex1& c) const {
return Complex1(rPart - c.rPart, iPart - c.iPart); // Complex1 클래스의 임시 객체를 생성 -> 문장 실행 후 소멸
}
Complex1 mul(const Complex1& c) const;
Complex1 div(const Complex1& c) const;
void display() const; // 복소수 값을 출력
};
예제: Complex1 클래스 - Complex1.cpp
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
#include <iostream>
#include "Complex1.h"
using namespace std;
Complex1 Complex1::mul(const Complex1& c) const
{
double r = rPart * c.rPart - iPart * c.iPart;
double i = rPart * c.iPart + iPart * c.rPart;
return Complex1(r, i);
}
Complex1 Complex1::div(const Complex1& c) const
{
double d = c.rPart * c.rPart + c.iPart * c.iPart;
Complex1 c1 = mul(c.conj());
return Complex1(c1.rPart/d, c1.iPart/d);
}
void Complex1::display() const
{
cout << "(" << rPart;
if (iPart > 0)
cout << "+" << iPart << "j";
else if (iPart < 0)
cout << "-" << -iPart << "j";
cout << ")";
}
복소수 클래스 - C1Main.cpp
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
#include <iostream>
#include "Complex1.h"
using namespace std;
int main()
{
Complex1 c1(1, 2);
Complex1 c2(2, 3);
Complex1 c3 = c1.add(c2);
c1.display();
cout << " + ";
c2.display();
cout << " = ";
c3.display();
cout << endl;
c3 = c1.mul(10.0); // 묵시적 형 변환 -> c3 = c1.mul(Complex1(10.0, 0.0));
c1.display();
cout << " * 10 = ";
c3.display();
cout << endl;
return 0;
}
// (1+2j) + (2+j3) = (3+j5)
// (1+j2) * 10 = (10+j20)
심화 학습
생성자 처리의 위임 (C++11 이후)
- 위임 생성자 (delegating constructor)의 선언
- 멤버 초기화 리스트에 클래스의 다른 생성자를 사용하여 새로운 생성자를 선언할 수 있음
- 위임 생성자
- 클래스의 다른 생성자를 이용하여 선언되는 생성자
- 타겟 생성자
- 위임의 대상이 되는 생성자
- 위임 생성자
- 생성자를 작성하는 코드의 중복을 줄일 수 있음
- 멤버 초기화 리스트에 클래스의 다른 생성자를 사용하여 새로운 생성자를 선언할 수 있음
VecF 클래스
1 2 3 4 5 6 7 8 9 10 11 12 13
class VecF { int n; float *arr; public: VecF(int d, const float* a=nullptr) : n{ d } { arr = new float[d]; if (a) memcpy(arr, a, sizeof(float) * n); } VecF(const VecF& fv) : n{ fv.n } { arr = new float[n]; memcpy(arr, fv.arr, sizeof(float)*n); } };
1 2 3 4 5 6 7 8 9 10 11
class VecF { int n; float *arr; public: VecF(int d, const float* a=nullptr) : n{ d } { // 타겟 생성자 arr = new float[n]; if (a) memcpy(arr, a, sizeof(float) * n); } VecF(const VecF& fv) : VecF(fv.n, fv.arr) {} // 위임 생성자 };
초기화 리스트 생성자
- 초기화 리스트 생성자(initializer-list constructor)
첫 번째 매개 변수가
std::initializer_list<Type>
인 생성자1
std::initializer_list<Type>
- 지정된 자료형의 값들을
{ }
안에 나열한 리스트 - 헤더 파일
#include <initializer_list>
멤버 함수 용도 begin()
첫 번째 요소에 대한 포인터를 반환함 end()
마지막 요소의 다음 위치에 대한 포인터를 반환함 size()
initializer_list
의 원소 수를 반환함1
initializer_list<int> ilst{ 1, 2, 3 };
- 지정된 자료형의 값들을
초기화 리스트 생성자의 활용
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
class VecF { int n; float *arr; public: VecF(int d, const float* a=nullptr) : n{ d } { arr = new float[d]; if (a) memcpy(arr, a, sizeof(float) * n); } VecF(initializer_list<float> lst) : n{ static_cast<int>(lst.size()) } { arr = new float[n]; copy(lst.begin(), lst.end(), arr); } }; int main() { float a[4] = {1.0f, 2.0f, 3.0f, 4.0f}; VecF v1(4, a); VecF v2{2.0f, 4.0f, 6.0f, 8.0f}; // 초기화 리스트 생성자 호출 }
연습 문제
다음 중 스택에서 데이터를 인출하는 순서에 대한 올바른 설명은?
a. Last In, First Out
- 스택은 나중에 저장된 데이터가 먼저 인출되는 구조를 가지고 있음
- First In, First Out과 같이 저장된 순서에 따라 인출하는 자료구조는 큐(
queue
)임
생성자를 직접 호출하는 형식의 문장 사용에 대한 올바른 설명은?
a. 이름이 없는 임시 객체가 생성된다.
- 생성자를 직접 호출하는 명령은 임시 객체를 만들며, 그 문장을 수행한 후 제거 됨
위 지문과 같이 선언된 ClassA의 객체 obj1과 obj2에 대한 다음 문장에 대한 올바른 설명은?
1 2 3 4 5 6 7 8 9 10 11 12
obj1 = obj2.add(10); class ClassA { int x, y; public: ClassA(int a=0, int b=0) : x(a), y(b) {} ClassA add(const ClassA& objA) { x += objA.x; y += objA.y; return *this; } };
a. 10은 생성자(a는 10, b는 디폴트 값인 0이 사용됨)를 이용하여 ClassA의 객체로 자동 형변환이 되어
add()
의 형식 매개변수로 전달된다.- int 값을 인수로 전달 받는
add()
는 선언되지 않았지만, 생성자를 이용하여 int 값이 ClassA의 객체로 자동 형 변환이 이루어질 수 있으므로 명령은 올바로 실행 됨 - 그 결과 obj2의 x에는 10이, y에는 0이 더해지며, 변화된 obj2가 obj1에 대입 됨
- int 값을 인수로 전달 받는
정리 하기
- 스택은
push
와pop
연산을 이용하여 데이터를 저장하고 인출할 수 있는 자료 구조로서, 나중에 저장된 데이터가 먼저 인출될 수 있는 특성(LIFO: Last In, First Out)을 갖음 - 배열과 top을 이용하여 스택을 구현할 수 있음
push
는 top을 1 감소 시킨 후 배열의 top 위치에 값을 저장하고,pop
은 top 위치의 값을 반환하며 top을 1 증가 시킴
- 실수 부와 허수 부의 값을 저장하는 데이터 멤버와 사칙 연산 등 필요한 연산을 멤버 함수로 정의하여 복소수 클래스를 구현할 수 있음
- 생성자를 직접 호출하는 형식의 연산은 해당 클래스의 임시 객체를 만듬
- 어떤 클래스의 객체가 필요한 위치에 그 클래스의 생성자의 형식 매개변수에 해당되는 값을 사용하면 묵시적 형 변환을 통해 그 클래스의 객체로 변환 됨
- 위임 생성자는 클래스에 선언된 다른 생성자를 이용하여 선언되는 생성자로서, 코드 중복을 줄일 수 있음
std::initialize_list
는 지정된 자료형의 값을 임의 개수 나열한 리스트를 만드는 데 사용되며, 첫 번째 매개변수가std::initializer_list<Type>
인 생성자를 초기화 리스트 생성자(initializer-list constructor)라고 함