방송대 C프로그래밍 15강: C++의 클래스와 다형성
C에서 원의 데이터와 처리 함수는 서로 떨어져 있지만, C++의 클래스는 데이터와 기능을 하나의 대상으로 묶습니다. 여기서 구현을 감추는 캡슐화, 공통점을 물려주는 상속, 같은 호출이 객체에 따라 달라지는 다형성으로 설계 관점이 확장됩니다. 이 글은 같은 도형 예제를 단계별로 바꾸며 각 문법이 왜 필요한지 연결합니다.
C++는 C의 문법 기반 위에 새로운 설계 관점을 더했다
강의에서는 C++의 출발을 1979년 Bell 연구소에서 Bjarne Stroustrup이 C를 확장한 C with Classes로 설명합니다. 1983년부터 C++라는 이름을 사용했고, 이후 클래스·객체·상속·다형성·동적 바인딩을 포함한 객체지향 프로그래밍, 템플릿을 이용한 일반화 프로그래밍, 예외처리 등의 기능이 더해졌습니다.
| 관점의 변화 | C에서 익힌 기반 | C++에서 확장된 부분 |
|---|---|---|
| 프로그램 구성 | 변수, 식, 함수, 흐름 제어 | 클래스와 객체 중심의 구성 가능 |
| 재사용 | 함수와 자료형을 조합 | 상속, 다형성, 템플릿 활용 |
| 오류 처리 | 반환값과 조건 검사 중심 | 예외처리 구문 추가 |
| 이름 관리 | 기존 명칭 규칙 | namespace 등의 문법 추가 |
C++는 C의 기본 문법을 폭넓게 공유하지만 단순히 이름만 바꾼 같은 언어는 아닙니다. 두 언어는 각각 별개의 표준화 작업을 통해 독립적으로 발전합니다. 따라서 ‘C 코드는 아무 수정 없이 모두 C++ 코드다’라고 일반화하기보다 공통 기반과 차이를 함께 확인해야 합니다.
공통 문법은 학습 자산이지만 언어 규칙은 따로 확인한다
선행처리, 기본 자료형, 연산자, 명칭과 상수, 변수·함수 선언, 식과 흐름 제어 등은 두 언어에서 크게 이어집니다. C의 표준 함수를 C++에서도 대부분 사용할 수 있으며, C 헤더에 대응하는 C++ 형식의 헤더도 제공됩니다.
| C 헤더 | C++ 대응 헤더 | 대표 영역 |
|---|---|---|
<stdio.h> | <cstdio> | 표준 입출력 |
<string.h> | <cstring> | C 문자열 처리 |
<math.h> | <cmath> | 수학 함수 |
반면 C++에는 namespace, 더 엄격한 자료형 검사와 형 변환, 예외처리 구문 등이 있고 일부 키워드의 용도도 다릅니다. 강의는 C 표준화를 ISO/IEC JTC1/SC22/WG14, C++ 표준화를 WG21이 담당한다고 구분합니다. C 지식은 C++ 학습의 출발점이지만, 두 표준의 차이를 생략할 근거는 아닙니다.
오개념 교정: C++를 C에 객체지향 문법 몇 개만 붙인 완전한 상위 호환 언어로 보면 자료형 검사와 문법 차이를 놓칠 수 있습니다. 공통 문법은 재사용하되 C++ 코드의 규칙으로 다시 검사해야 합니다.
절차 중심 질문은 ‘어떤 순서로 처리하는가’이다
절차지향 프로그래밍은 순차적으로 처리되는 절차를 중심으로 프로그램을 설계하는 패러다임입니다. 순차·선택·반복 제어로 실행 흐름을 만들고, 함수를 이용해 의미 있는 처리 단위로 모듈화합니다. 함수는 매개변수로 필요한 자료를 받고 지역변수로 처리 공간을 마련합니다.
작은 프로그램에서는 처리 순서가 직접 드러나 이해하기 쉽습니다. 그러나 시스템이 커지고 여러 자료형이 비슷한 기능을 반복하면 데이터와 관련 함수의 연결을 일관되게 관리하기 어려울 수 있습니다. 코드 재사용과 정보 은닉도 설계자가 별도로 체계화해야 합니다.
관점 핵심: 절차지향은 함수를 쓰지 않는 방식이 아닙니다. 오히려 함수를 중심으로 작업을 나누되, 설계의 주된 질문이 객체의 정체성보다 처리 단계와 절차에 놓입니다.
객체 중심 질문은 ‘누가 데이터와 기능을 책임지는가’이다
객체지향 프로그래밍(OOP)은 객체를 중심으로 프로그램을 설계합니다. 클래스(class)는 객체를 구성하는 데이터와 기능을 정의한 것이고, 객체(object)는 그 클래스에 해당하는 구체적인 사례, 즉 인스턴스입니다.
| 구분 | 의미 | 원 예제 |
|---|---|---|
| 클래스 | 공통 데이터와 기능을 정의한 설계 | class Circle |
| 객체 | 클래스를 바탕으로 만들어진 개별 사례 | Circle c; |
| 데이터 | 객체가 보유하는 상태 | 중심 좌표와 반지름 |
| 기능 | 객체가 제공하는 동작 | 초기화, 면적 계산, 출력 |
객체지향 설계는 추상화, 캡슐화, 상속, 다형성을 이용해 코드 재사용과 확장·유지보수를 돕습니다. 다만 초기 설계가 복잡해질 수 있고 추상화 계층에 따른 비용도 고려해야 합니다. C++는 객체지향만 강제하는 언어가 아니라 C에서 익힌 절차지향 방식도 함께 사용할 수 있는 언어입니다.
C 구조체에서는 데이터와 처리 함수가 나란히 협력한다
원의 중심 좌표와 반지름을 C 구조체로 묶을 수 있지만, 초기화·면적 계산·출력 기능은 구조체 바깥의 별도 함수로 정의합니다.
#include <stdio.h>
struct Circle {
float cx;
float 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;
}
함수 이름에 Circle을 넣고 구조체 포인터를 전달해 어느 자료를 처리하는지 연결합니다. initCircle은 원본의 멤버를 바꾸고, areaCircle은 반지름을 읽어 면적을 반환합니다. 중심이 (10, 20)이고 반지름이 10이면 면적은 약 314.159입니다.
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;
}
};
int main()
{
Circle c;
c.init(10.0f, 20.0f, 10.0f);
c.print();
cout << "면적 = " << c.area() << endl;
}
Circle c;는 클래스의 객체를 만들고, c.init(), c.print(), c.area()는 그 객체가 제공하는 기능을 호출합니다. C 코드에서 함수마다 전달하던 &c가 사라지고, 호출 대상 c가 어느 데이터에 동작할지를 이미 정합니다.
변환 관찰: areaCircle(&c)가 c.area()로 바뀐 것은 계산식만 줄인 것이 아닙니다. ‘원 자료를 처리하는 기능’을 원 객체의 책임 안으로 이동시킨 것입니다.
캡슐화는 내부 구현과 공개 사용법을 분리한다
캡슐화(encapsulation)는 객체 내부의 상세한 구현과 외부 사용자의 관점을 분리하는 원리입니다. TV 사용자가 내부 회로를 직접 조작하지 않고 외부 버튼을 사용하는 것처럼, 객체 밖의 코드는 공개된 인터페이스를 통해 기능을 요청합니다.
class Circle {
float cx, cy, radius; // 내부 상태
public:
void init(float x, float y, float r);
float area() const;
void print() const;
};
이 클래스에서 좌표와 반지름은 외부에서 직접 사용하는 부분으로 공개하지 않고, public: 아래의 멤버 함수를 외부 인터페이스로 제공합니다. 이것이 정보 은닉입니다. 외부 코드는 면적 공식이나 출력 구현이 바뀌어도 공개 함수의 사용법이 유지된다면 내부 변경을 직접 따라갈 필요가 줄어듭니다.
판단 핵심: 캡슐화는 단순히 데이터와 함수를 같은 중괄호에 넣는 데서 끝나지 않습니다. 무엇을 내부에 감추고 어떤 동작을 공개할지 경계를 설계해야 합니다.
상속은 여러 클래스의 공통 부분을 위로 모은다
상속(inheritance)은 공통 멤버를 가진 기초 클래스를 만들고 파생 클래스가 그 멤버를 이어받는 계층적 설계 방법입니다. 학생과 직장인이 모두 이름과 나이를 가진다면 공통 부분을 사람 클래스에 두고, 학생에는 학과·학년, 직장인에는 부서명·직급을 추가할 수 있습니다.
| 계층 | 공통 또는 고유 멤버 | 역할 |
|---|---|---|
기초 클래스 Person | 이름, 나이 | 두 유형이 공유하는 일반 특성 정의 |
파생 클래스 Student | 학과, 학년 | 학생에게만 필요한 특성 추가 |
파생 클래스 Worker | 부서명, 직급 | 직장인에게만 필요한 특성 추가 |
상속의 목적은 비슷해 보이는 코드를 무조건 합치는 것이 아니라, 여러 대상이 실제로 공유하는 개념과 인터페이스를 기초 클래스에 올리는 것입니다. 파생 클래스는 공통 부분을 이어받고 자기 역할에 필요한 데이터와 기능을 덧붙입니다.
다형성은 하나의 인터페이스를 객체별 동작으로 연결한다
다형성(polymorphism)은 하나의 인터페이스로 요청하되 실제 대상에 맞는 동작이 수행되게 하는 것입니다. 원과 삼각형은 데이터와 면적 공식은 다르지만 둘 다 ‘면적을 구한다’와 ‘자신을 출력한다’는 기능을 가질 수 있습니다.
class Shape {
public:
virtual float area() const = 0;
virtual void print() const = 0;
};
Shape는 구체적인 도형이 아니어서 면적 공식이나 출력 내용을 하나로 정할 수 없습니다. 그래서 area()와 print()를 가상함수로 선언하고 = 0을 붙여 인터페이스만 제시합니다. 실제 구현은 각 파생 클래스가 담당합니다.
파생 클래스는 같은 함수 이름에 자기 공식을 구현한다
Circle과 Triangle은 모두 Shape를 public으로 상속하고 같은 인터페이스를 재정의합니다.
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 << "원, 반경 = " << 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 << "삼각형" << endl;
}
};
override는 기초 클래스의 가상함수 인터페이스를 파생 클래스가 자기 방식으로 구현한다는 뜻을 드러냅니다. 호출 이름은 area로 같지만 원은 반지름 공식, 삼각형은 세 꼭짓점 좌표 공식을 사용합니다.
삼각형 면적은 좌표 대입 과정을 펼쳐 검산한다
강의 예제의 삼각형 꼭짓점은 (0, 0), (10, 0), (5, 10)입니다. 코드의 중간 변수 a를 그대로 계산하면 다음과 같습니다.
- 첫 합:
0×0 + 10×10 + 5×0 = 100 - 뺄 합:
10×0 + 5×0 + 0×10 = 0 - 차의 절댓값:
|100-0| = 100 - 2로 나눈 면적:
100÷2 = 50
따라서 Triangle::area()는 50을 반환합니다. 원 객체는 반지름 10이므로 약 314.159를 반환합니다. 같은 이름의 함수라도 객체가 가진 데이터와 재정의한 공식에 따라 결과가 달라집니다.
Shape 참조 하나가 실제 객체에 맞는 함수를 고른다
다형성의 핵심은 공통 처리 함수가 구체적인 도형 종류를 일일이 검사하지 않는다는 데 있습니다.
void shapeProc(Shape& shape)
{
shape.print();
cout << "면적 = " << shape.area() << endl;
}
int main()
{
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);
shapeProc(t);
}
| 호출 | Shape&가 참조하는 실제 객체 | 선택되는 print() | 선택되는 area() |
|---|---|---|---|
shapeProc(c) | Circle | Circle::print() | Circle::area() → 약 314.159 |
shapeProc(t) | Triangle | Triangle::print() | Triangle::area() → 50 |
매개변수 형은 두 호출 모두 Shape&이지만, 가상함수 호출은 실행 시점의 실제 객체에 맞는 재정의 함수를 선택합니다. 이것이 강의에서 말하는 동적 다형성입니다.
오개념 교정: 함수 이름이 같다는 사실만으로 다형성이 완성되지는 않습니다. 공통 기초 클래스의 가상함수 인터페이스, 파생 클래스의 재정의, 기초 클래스 참조를 통한 호출이 연결되어야 실제 객체에 맞는 동작이 선택됩니다.
C에서 C++로 읽을 때는 책임의 이동을 추적한다
- 데이터 묶음 확인: C 구조체에 어떤 상태가 모여 있는지 찾습니다.
- 관련 함수 확인: 그 상태를 반복해서 받는 외부 함수들을 찾습니다.
- 클래스 경계 설정: 데이터와 관련 기능을 한 클래스의 책임으로 묶습니다.
- 공개 범위 결정: 내부 상태는 감추고 외부가 사용할 인터페이스를 정합니다.
- 공통점 추출: 여러 클래스가 공유하는 기능을 기초 클래스 인터페이스로 올립니다.
- 대상별 구현 연결: 가상함수를 재정의해 같은 요청이 실제 객체에 맞게 동작하도록 만듭니다.
설계 점검: 클래스가 있다는 이유만으로 객체지향 설계가 되는 것은 아닙니다. 데이터의 책임, 공개 인터페이스, 공통 계층, 객체별 동작이라는 관계가 실제 문제와 맞는지 확인해야 합니다.
핵심 개념 정리
- C와 C++는 기본 문법을 폭넓게 공유하지만 별개의 표준과 규칙으로 발전하는 언어입니다.
- 절차지향은 처리 단계와 함수를 중심으로, 객체지향은 데이터와 기능의 책임을 가진 객체를 중심으로 설계합니다.
- 클래스는 데이터와 기능의 정의이고 객체는 그 클래스의 구체적인 사례입니다.
- 캡슐화는 내부 구현과 공개 인터페이스를 분리하며, 정보 은닉으로 외부의 직접 접근을 제한합니다.
- 상속은 공통 멤버를 기초 클래스에 모으고 파생 클래스가 공통 부분을 이어받아 고유 특성을 추가하게 합니다.
- 다형성은 기초 클래스의 가상함수 인터페이스를 통해 실제 객체가 재정의한 동작을 선택합니다.
C++의 객체지향 문법을 외울 때는 struct → class → public 인터페이스 → base/derived 계층 → virtual 호출의 책임 이동을 따라가세요. 원과 삼각형처럼 데이터는 달라도 같은 요청을 공유하는 예에서 이 흐름을 반복하면 클래스·캡슐화·상속·다형성이 하나의 설계 논리로 연결됩니다.
예상문제 10선
1. C++에서 클래스와 객체의 관계를 바르게 설명한 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
- ① 오답: 사례에 해당하는 것은 객체이며 클래스는 그 객체들이 가질 데이터와 기능을 정의합니다.
- ② 정답:
class Circle이 설계를 정의하고Circle c가 그 설계에 따른 구체적 객체를 만듭니다. - ③ 오답: 클래스는 여러 데이터 멤버와 멤버 함수를 함께 포함할 수 있으며 매개변수 목록과 같은 개념이 아닙니다.
- ④ 오답: 헤더 파일은 선언을 제공하는 파일이고 클래스·객체는 프로그램의 자료와 동작을 구성하는 개념입니다.
2. C와 C++의 관계에 대한 설명으로 옳은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
- ① 오답: 공통 문법이 많아도 namespace, 자료형 검사와 예외처리 등 차이가 있고 표준화도 분리됩니다.
- ② 오답: 변수, 함수, 연산자와 흐름 제어 등 C에서 익힌 기본 요소가 C++에도 폭넓게 이어집니다.
- ③ 오답: C++는 절차지향 방식도 사용할 수 있으며 객체지향 설계를 지원하도록 기능이 확장되었습니다.
- ④ 정답: 공통 기반과 독립적인 발전을 함께 인정해야 두 언어의 관계를 정확히 설명할 수 있습니다.
3. 절차지향과 객체지향의 중심 질문을 알맞게 연결한 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
- ① 정답: 절차지향은 함수와 실행 단계를, 객체지향은 상태와 관련 동작을 맡는 객체를 주요 구성 축으로 삼습니다.
- ② 오답: 객체 계층은 상속을 활용하는 객체지향 설계와 관련되고 전처리만으로 객체지향을 설명할 수 없습니다.
- ③ 오답: 절차지향도 함수를 적극 사용하며 객체지향 코드에서도 순차·선택·반복 제어를 사용합니다.
- ④ 오답: 처리 절차와 객체 책임이라는 설계 중심의 차이가 두 패러다임을 구분합니다.
4. C의 areaCircle(&c)를 클래스의 책임으로 옮긴 C++ 호출 형태는?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
- ① 오답: 클래스 이름, 객체 이름과 함수 이름의 역할이 뒤섞여 원 객체의 멤버 호출이 되지 않습니다.
- ② 오답: 객체의 멤버 함수를 호출하려면 객체 뒤에 점 연산자와 멤버 함수 이름을 둡니다.
- ③ 정답: 원 객체 c가 자기 상태에 대해 제공하는 area 멤버 함수를 호출하는 표현입니다.
- ④ 오답: c는 포인터가 아니며 주소 연산자와 화살표·점 연산자를 이 방식으로 결합하지 않습니다.
5. 캡슐화를 가장 잘 설명한 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
- ① 오답: 외부의 직접 접근을 허용하는 것은 정보 은닉과 반대 방향입니다.
- ② 정답: 구현과 사용 관점을 분리하면 외부는 공개된 조작 방법에 의존하고 내부 상세는 감출 수 있습니다.
- ③ 오답: 객체별 공식을 같은 인터페이스로 연결하는 것은 주로 다형성의 설명입니다.
- ④ 오답: 헤더 표기 변경은 언어 라이브러리 사용법이며 객체의 공개 경계를 설계하는 캡슐화와 다릅니다.
6. 학생과 직장인의 공통 멤버를 상속으로 설계할 때 기초 클래스에 둘 항목은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ①
- ① 정답: 두 유형 모두 사람이라는 공통 개념에서 이름과 나이를 가지므로 기초 클래스에 모을 수 있습니다.
- ② 오답: 학과와 학년은 학생에게만 필요한 고유 멤버이므로 학생 파생 클래스에 적합합니다.
- ③ 오답: 부서명과 직급은 직장인 유형의 고유 특성입니다.
- ④ 오답: 서로 다른 파생 클래스의 고유 멤버를 한 쌍으로 묶으면 공통점을 추출한 것이 아닙니다.
7. 꼭짓점이 (0,0), (10,0), (5,10)인 강의 예제 삼각형의 면적은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
- ① 오답: 좌표식에서 얻은 차 100을 4로 나눈 값이며 코드의 마지막 연산은 2로 나눕니다.
- ② 오답: 밑변 10과 높이 10을 사용한 삼각형 면적 계산과 맞지 않습니다.
- ③ 오답: 좌표 곱의 차는 100이지만 삼각형 면적은 그 절댓값을 2로 나누어야 합니다.
- ④ 정답:
|100-0|÷2또는10×10÷2로 계산하면 50입니다.
8. shapeProc(Shape& s)에 Circle 객체를 전달했을 때 가상함수 호출 과정으로 옳은 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ③
- ① 오답: Shape의 순수 가상함수는 공통 인터페이스를 제공하며 실제 객체의 구현으로 연결됩니다.
- ② 오답: 소스 선언 순서가 아니라 Shape 참조가 가리키는 실제 객체의 형이 선택 기준입니다.
- ③ 정답: 동적 바인딩은 실행 시 Circle 객체에 맞는
print와area재정의를 선택합니다. - ④ 오답: 한 가상함수 호출은 실제 객체에 해당하는 재정의 구현 하나로 연결됩니다.
9. ‘함수 이름이 같기만 하면 동적 다형성이 완성된다’는 주장에 빠진 핵심 조건은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ④
- ① 오답: Circle과 Triangle처럼 데이터 멤버가 달라도 공통 함수 인터페이스를 가질 수 있습니다.
- ② 오답: 다형성의 목적은 같은 요청에 객체별로 다른 결과와 동작을 제공하는 데 있습니다.
- ③ 오답: 헤더 포함은 선언을 제공할 뿐 실제 객체에 맞는 가상함수 선택 구조를 만들지 않습니다.
- ④ 정답: 이름의 일치가 아니라 상속 계층의 virtual 인터페이스와 실제 객체의 override가 호출 경로로 연결되어야 합니다.
10. C의 구조체 기반 도형 코드를 객체지향 구조로 바꾸는 순서로 가장 적절한 것은?
정답입니다.
오답입니다. 답안을 다시 선택해 보세요.
정답 및 해설 보기
정답: ②
- ① 오답: 내부 상태의 무조건 공개와 main 집중은 캡슐화와 책임 분리의 방향에 맞지 않습니다.
- ② 정답: 클래스 책임, 캡슐화, 상속에 의한 공통점 추출, 다형적 재정의를 순서대로 연결한 설계입니다.
- ③ 오답: 이름만 바꾸면 데이터와 기능의 소속, 공개 경계와 객체별 동작이라는 구조 변화가 생기지 않습니다.
- ④ 오답: 공용체의 메모리 공유는 클래스 계층과 다른 목적이며 가상함수를 지우면 동적 다형성의 연결점이 사라집니다.
참고 자료와 작성 기준
이 글은 해당 차시 강의자료를 바탕으로 학습 목적에 맞게 재구성한 비공식 학습자료입니다. 구조체에서 클래스로의 변환, 도형 면적 계산과 가상함수 호출 추적은 학습자의 이해를 돕도록 구성하고 검토했습니다.
- 작성·편집: 올에이클래스 학습연구팀
- 주요 근거: 한국방송통신대학교 C프로그래밍 15강 「C++ 언어의 개요」 강의자료(2025)
- 보충 자료: 외부 자료를 사용하지 않았으며 강의자료의 개념 범위 안에서 재구성
- 편집 원칙: 올에이클래스 편집 정책
- 최종 내용 검토: 2026-09-07
댓글
댓글 쓰기