C프로그래밍 15강 - C++ 언어의 개요
C++ 언어가 C 언어를 바탕으로 어떻게 확장되었는지 알아보고 절차지향 프로그래밍과 객체지향 프로그래밍의 차이를 비교한다. 구조체와 클래스, 객체와 캡슐화, 상속과 다형성의 관계를 원과 삼각형 예제로 정리한다.
1. C++ 언어의 등장과 확장
C++는 1979년 벨 연구소의 비야네 스트롭스트룹(Bjarne Stroustrup)이 C 언어를 확장하여 만든 프로그래밍 언어이다. 처음에는 C with Classes라고 불렸으며 1983년부터 C++라는 이름을 사용하였다.
C++는 C의 기본 문법을 바탕으로 새로운 프로그래밍 패러다임과 오류 처리 기능을 지원하도록 확장되었다.
| 확장 분야 | 주요 내용 |
|---|---|
| 객체지향 프로그래밍 | 클래스, 객체, 상속, 다형성, 동적 바인딩 |
| 일반화 프로그래밍 | 템플릿을 이용한 일반화된 코드 작성 |
| 예외처리 | 프로그램 실행 중 발생하는 예외 상황 처리 |
C++는 C의 문법을 상당 부분 이어받았지만 단순한 C의 다른 이름이 아니라 객체지향·일반화 프로그래밍 등을 지원하며 독립적으로 발전한 언어이다.
2. C 언어와 C++ 언어의 관계
C와 C++는 선행처리, 자료형, 연산자, 명칭의 정의, 상수 표기, 변수와 함수의 선언, 식, 흐름제어 구문 등 기본적인 문법 요소가 대부분 비슷하다. C의 표준함수도 C++에서 대부분 사용할 수 있으며 이에 대응하는 헤더가 제공된다.
| C 헤더 | C++에서 제공되는 헤더 |
|---|---|
<stdio.h> | <cstdio> |
<string.h> | <cstring> |
<math.h> | <cmath> |
그러나 두 언어의 문법이 완전히 같은 것은 아니다. C++에는 이름공간(namespace), 더 엄격한 자료형 검사와 형 변환, 예외처리 구문이 추가되었고 일부 키워드의 용도도 다르다. 또한 객체지향 프로그래밍과 일반화 프로그래밍을 지원하는 문법이 포함된다.
C와 C++는 각각 별개의 표준화 위원회에서 표준을 개발한다. C는 ISO/IEC JTC1/SC22/WG14, C++는 ISO/IEC JTC1/SC22/WG21이 담당한다.
C에서 작성한 코드를 C++에서 그대로 사용할 수 있는 경우가 많지만 모든 C 프로그램이 수정 없이 C++ 프로그램이 되는 것은 아니다. 두 언어의 차이를 확인해야 한다.
3. 절차지향과 객체지향 프로그래밍
절차지향 프로그래밍은 순차적으로 처리되는 절차를 중심으로 프로그램을 설계한다. 순차·선택·반복 제어를 이용해 구조화하고, 의미 있는 처리 단위를 함수로 나누며, 함수에 필요한 자료는 매개변수로 전달한다. 처리 과정에서 필요한 기억공간은 지역변수로 마련한다.
객체지향 프로그래밍은 객체를 중심으로 프로그램을 설계한다. 클래스가 객체를 구성하는 데이터와 기능을 정의하고, 객체는 그 클래스에 해당하는 구체적인 사례가 된다.
| 구분 | 절차지향 프로그래밍 | 객체지향 프로그래밍 |
|---|---|---|
| 설계 중심 | 순차적으로 처리되는 절차 | 상태와 기능을 가진 객체 |
| 주요 구성 | 함수, 매개변수, 지역변수 | 클래스와 객체 |
| 핵심 개념 | 순차·선택·반복, 함수 모듈화 | 추상화, 캡슐화, 상속, 다형성 |
| 적합한 범위 | 비교적 작은 규모의 프로그램 | 크고 복잡하며 확장이 필요한 시스템 |
| 장점 | 처리 흐름이 직접적이고 구조가 단순하다. | 코드 재사용성·확장성·유지보수성이 높다. |
| 주의점 | 큰 시스템에 필요한 재사용·캡슐화 특성이 부족할 수 있다. | 초기 설계가 복잡하고 성능이 조금 떨어질 수 있다. |
C는 절차지향 방식의 프로그램 작성에 주로 사용된다. C++는 절차지향 방식으로도 작성할 수 있으면서 객체 중심의 설계를 지원한다.
4. C 언어의 구조체와 처리 함수
C의 구조체는 서로 관련된 여러 자료를 모아 새로운 자료형을 정의한다. 예를 들어 원은 중심 좌표와 반지름을 하나의 구조체로 표현할 수 있다.
struct Circle {
float cx, cy;
float radius;
};
구조체는 원의 데이터를 묶지만 초기화, 면적 계산, 출력 같은 처리는 구조체 바깥에 별도의 함수로 정의한다. 함수는 구조체 포인터를 매개변수로 받아 멤버를 읽거나 변경한다.
void initCircle(struct Circle *c,
float x, float y, float r) {
c->cx = x;
c->cy = y;
c->radius = r;
}
float areaCircle(struct Circle *c) {
return 3.141592f * c->radius * c->radius;
}
void printCircle(struct Circle *c) {
printf("원: 중심 = (%g, %g), ", c->cx, c->cy);
printf("반경 = %g\n", c->radius);
}
initCircle(&c, 10.0f, 20.0f, 10.0f)로 초기화한 원의 중심은 (10, 20), 반지름은 10이며 면적은 약 314.159이다. 구조체의 데이터와 그 데이터를 처리하는 함수가 서로 분리되어 있다는 점이 중요하다.
5. C++의 클래스와 객체
클래스(class)는 표현하려는 대상을 구성하는 데이터와 객체의 기능을 함께 묶어 선언한 것이다. 객체(object)는 클래스에 해당하는 구체적인 사례(instance)이다. 클래스가 원의 공통 설계라면 Circle c;로 선언한 c는 실제 원 객체이다.
#include <iostream>
using namespace std;
class Circle {
float cx, cy, radius;
public:
void init(float x, float y, float r) {
cx = x;
cy = y;
radius = r;
}
float area() const {
return 3.141592f * radius * radius;
}
void print() const {
cout << "원: 중심 = (" << cx << ", " << cy << ")";
cout << ", 반경 = " << radius << endl;
}
};
객체를 선언한 뒤에는 점 연산자로 공개된 멤버 함수를 호출한다.
Circle c;
c.init(10.0f, 20.0f, 10.0f);
c.print();
cout << "면적 = " << c.area() << endl;
area() const와 print() const에서 뒤의 const는 이 멤버 함수가 객체의 상태를 변경하지 않는 함수임을 나타낸다.
클래스는 데이터와 그 데이터를 처리하는 기능을 하나의 단위로 정의하고, 객체는 그 클래스 정의에 따라 생성된 사례이다.
6. 구조체와 클래스의 비교 및 캡슐화
C의 원 구조체에서는 cx, cy, radius와 같은 데이터만 구조체 안에 두고 initCircle(), areaCircle(), printCircle()은 별도의 함수로 둔다. C++의 원 클래스는 데이터와 init(), area(), print() 기능을 한 클래스 안에 함께 정의한다.
캡슐화(encapsulation)는 객체 내부의 상세한 구현과 외부 사용자의 관점을 분리하는 것이다. TV 시청자가 내부 회로를 알지 않아도 조작 단추로 TV를 사용할 수 있듯이, 객체 외부에서는 공개된 인터페이스를 통해 객체를 사용한다.
| 관점 | 역할 |
|---|---|
| 객체 내부 | 데이터와 상세 구현을 감추어 직접 사용을 제한한다. |
| 객체 외부 | public으로 공개된 멤버 함수를 통해 객체를 간접적으로 사용한다. |
| 정보은닉 | 내부 속성과 구현 세부 사항을 외부에 공개하지 않는다. |
| 공개 인터페이스 | 외부 사용자가 객체에 요청할 수 있는 기능을 제공한다. |
Circle 예제에서 중심 좌표와 반지름은 클래스 내부에 두고, 외부에는 초기화·면적 계산·출력 함수를 공개한다. 사용자는 구현 세부 사항을 직접 조작하는 대신 공개된 함수를 호출한다.
캡슐화는 단순히 여러 코드를 한곳에 모으는 것이 아니라 내부 구현을 감추고 안정된 공개 인터페이스를 통해 사용하도록 경계를 만드는 것이다.
7. 상속과 클래스의 계층적 설계
상속(inheritance)은 공통적인 데이터와 기능을 기초 클래스에 모으고, 파생 클래스가 기초 클래스의 멤버를 이어받도록 하는 방법이다. 유사한 유형의 클래스를 계층적으로 설계할 수 있다.
학생과 직장인은 모두 이름과 나이를 갖는다. 따라서 이름과 나이는 사람 클래스에 모으고, 학생 클래스에는 학과와 학년을, 직장인 클래스에는 부서명과 직급을 추가할 수 있다.
| 클래스 | 자체 멤버 | 상속 관계 |
|---|---|---|
| 사람 | 이름, 나이 | 공통 멤버를 제공하는 기초 클래스 |
| 학생 | 학과, 학년 | 사람의 이름과 나이를 이어받는 파생 클래스 |
| 직장인 | 부서명, 직급 | 사람의 이름과 나이를 이어받는 파생 클래스 |
이처럼 공통 부분을 한곳에 정의하면 중복을 줄이고 기초 클래스의 공통 설계를 여러 파생 클래스에서 재사용할 수 있다.
8. 다형성과 가상함수
다형성(polymorphism)은 하나의 인터페이스로 대상에 맞는 동작을 수행할 수 있게 하는 것이다. 원과 삼각형은 모두 면적을 구하고 자신을 출력할 수 있지만 구체적인 계산법과 출력 내용은 서로 다르다.
공통 인터페이스인 area()와 print()를 기초 클래스 Shape에 선언하고, Circle과 Triangle이 각각 알맞게 구현한다. 동적 다형성을 적용하려면 기초 클래스의 함수를 가상함수로 선언한다.
class Shape {
public:
virtual float area() const = 0;
virtual void print() const = 0;
};
구체적인 모양이 정해지지 않은 Shape만으로는 면적 계산이나 출력을 구현할 수 없으므로 = 0을 붙인 순수 가상함수로 인터페이스만 정의한다. 파생 클래스에서는 override를 사용해 같은 인터페이스를 도형에 맞게 재정의한다.
다형성은 같은 Shape 인터페이스에 원 객체를 전달하면 원의 함수가, 삼각형 객체를 전달하면 삼각형의 함수가 실행되게 한다.
9. Shape·Circle·Triangle 다형성 예제
Circle과 Triangle은 public Shape로 기초 클래스의 공개 인터페이스를 상속하고, 각 도형의 area()와 print()를 구현한다.
class Circle : public Shape {
float cx, cy, radius;
public:
void init(float x, float y, float r) {
cx = x; cy = y; radius = r;
}
float area() const override {
return 3.141592f * radius * radius;
}
void print() const override {
cout << "원: 중심 = (" << cx << ", " << cy << ")";
cout << ", 반경 = " << radius << endl;
}
};
class Triangle : public Shape {
float x1, y1, x2, y2, x3, y3;
public:
void init(float u1, float v1, float u2, float v2,
float u3, float v3) {
x1 = u1; y1 = v1; x2 = u2; y2 = v2; x3 = u3; y3 = v3;
}
float area() const override {
float a = x1 * y2 + x2 * y3 + x3 * y1;
a -= x2 * y1 + x3 * y2 + x1 * y3;
return (a >= 0 ? a : -a) / 2.0f;
}
void print() const override {
cout << "삼각형: (" << x1 << ", " << y1 << ") - ("
<< x2 << ", " << y2 << ") - ("
<< x3 << ", " << y3 << ")" << endl;
}
};
다형성을 이용하는 함수는 특정 도형이 아니라 기초 클래스의 참조를 받는다.
void shapeProc(Shape& s) {
s.print();
cout << "면적 = " << s.area() << endl << endl;
}
Circle c;
Triangle t;
c.init(10.0f, 20.0f, 10.0f);
t.init(0.0f, 0.0f, 10.0f, 0.0f, 5.0f, 10.0f);
shapeProc(c); /* 면적 약 314.159 */
shapeProc(t); /* 면적 50 */
shapeProc()의 코드는 한 번만 작성되지만 실제로 전달된 객체가 원인지 삼각형인지에 따라 알맞은 print()와 area()가 호출된다. 이것이 가상함수를 이용한 동적 다형성이다.
핵심 개념 정리
- C++는 C의 기본 문법을 바탕으로 객체지향 프로그래밍, 일반화 프로그래밍, 예외처리 기능 등을 확장한 언어이다.
- C와 C++의 기본 문법은 대부분 비슷하지만 두 언어는 별개의 표준화 위원회에서 독립적으로 발전한다.
- 절차지향은 처리 절차와 함수를 중심으로, 객체지향은 데이터와 기능을 가진 객체를 중심으로 설계한다.
- 클래스는 객체의 데이터와 기능을 정의하고 객체는 클래스에 해당하는 구체적인 사례이다.
- 캡슐화는 내부 구현을 감추고 공개 인터페이스를 통해 객체를 사용하도록 한다.
- 상속은 공통 멤버를 기초 클래스에 모으고 파생 클래스가 이를 이어받게 한다.
- 다형성은 하나의 인터페이스가 실제 대상에 맞는 동작을 수행하게 한다.
- 순수 가상함수는 기초 클래스에 인터페이스만 정의하고 파생 클래스가 구체적인 동작을 구현하게 한다.
최종 정리: C++의 객체지향 설계는 클래스와 객체를 기반으로 데이터를 기능과 결합하고, 캡슐화로 구현을 보호하며, 상속으로 공통 코드를 재사용하고, 다형성으로 동일한 인터페이스에서 대상별 동작을 선택한다.
예상문제 20선
1. C++ 언어를 개발한 사람은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
Bjarne Stroustrup은 1979년 벨 연구소에서 C 언어를 확장해 C++의 기반을 만들었다.
2. C++가 처음 사용한 이름은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
처음에는 C with Classes라고 불렸으며 1983년부터 C++라는 이름을 사용하였다.
3. C++의 주요 확장 내용에 해당하지 않는 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
C++는 클래스·템플릿·예외처리 등을 확장했으며 기계어만 사용하도록 만든 언어가 아니다.
4. C++에서 C의 <stdio.h>에 대응해 제공되는 헤더는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
C++에서는 C 표준함수와 관련해 stdio.h에 대응하는 cstdio 헤더가 제공된다.
5. C와 C++의 관계에 대한 설명으로 옳은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
두 언어는 많은 기본 문법을 공유하지만 서로 다른 표준화 위원회에서 독립적으로 표준을 개발한다.
6. 절차지향 프로그래밍이 설계의 중심으로 삼는 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
절차지향 프로그래밍은 순차·선택·반복을 이용한 처리 절차와 함수를 중심으로 설계한다.
7. 객체지향 프로그래밍의 핵심 개념에 해당하는 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
추상화, 캡슐화, 상속, 다형성은 객체지향 프로그래밍을 이루는 핵심 개념이다.
8. 객체지향 프로그래밍의 특징으로 옳지 않은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
객체지향 방식은 재사용과 유지보수에 유리하지만 초기 설계가 복잡할 수 있다.
9. C 구조체로 원을 표현할 때 한 구조체에 묶는 자료는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
원 구조체는 서로 관련된 중심의 x·y 좌표와 반지름을 하나의 새로운 자료형으로 묶는다.
10. C 구조체의 자료를 처리하는 강의 예제의 방식은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
C 예제에서는 구조체의 데이터와 처리 함수를 분리하고 함수에 구조체 포인터를 전달한다.
11. 클래스의 정의로 가장 알맞은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
클래스는 객체가 가질 데이터와 수행할 기능을 함께 묶어 선언한다.
12. 객체에 대한 설명으로 옳은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
객체는 클래스가 정의한 데이터와 기능을 갖는 구체적인 사례 또는 인스턴스이다.
13. 캡슐화의 의미로 옳은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
캡슐화는 상세 구현을 감추고 외부에는 객체를 사용하는 공개 인터페이스를 제공한다.
14. 클래스 외부에서 객체를 사용하는 바람직한 방법은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
캡슐화된 객체는 public으로 공개된 멤버 함수 같은 인터페이스를 통해 간접적으로 사용한다.
15. 상속의 목적을 가장 잘 설명한 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
상속은 공통 부분의 중복을 줄이고 클래스 집합을 계층적으로 설계하게 한다.
16. 사람·학생·직장인 예제에서 기초 클래스에 적합한 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
학생과 직장인에게 공통인 이름과 나이를 사람 기초 클래스에 모을 수 있다.
17. 다형성의 의미로 가장 알맞은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
같은 Shape 인터페이스라도 실제 객체가 원인지 삼각형인지에 따라 알맞은 동작이 선택된다.
18. virtual float area() const = 0;에 대한 설명은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
Shape는 구체적인 면적 계산을 할 수 없으므로 순수 가상함수로 인터페이스만 제공한다.
19. 파생 클래스의 함수 선언에 붙인 override의 역할은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
Circle과 Triangle은 override를 붙여 Shape의 가상함수를 도형별 동작으로 재정의한다.
20. shapeProc(Shape& s)에 원 객체와 삼각형 객체를 각각 전달했을 때의 설명으로 옳은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
가상함수와 Shape 참조를 이용하므로 원에는 원의 함수가, 삼각형에는 삼각형의 함수가 동적으로 선택된다.
댓글
댓글 쓰기