C++ 상속 (Inheritance) 정리
1. 기본 개념
상속이란 기존 클래스를 기반으로 새로운 클래스를 만드는 객체지향 개념입니다.
공통된 속성과 동작을 코드 재사용을 통해 자식 클래스에서 활용할 수 있게 해줍니다.
class 부모클래스
{
public:
void 공통기능()
{
std::cout << "모든 자식에게 공통!" << std::endl;
}
};
class 자식클래스 : public 부모클래스
{
public:
void 고유기능()
{
std::cout << "자식 고유 기능" << std::endl;
}
};
C++
복사
2. 상속 키워드 종류
종류 | 의미 |
public | 부모 클래스의 public/protected 멤버 → 그대로 유지 |
protected | 부모의 public → protected로 |
private | 부모의 모든 public/protected → private로 |
보통은 **public 상속**을 사용합니다.
3. 단일 상속 예제
#include <iostream>
using namespace std;
class Animal
{
public:
void Eat()
{
cout << "먹는다" << endl;
}
};
class Dog : public Animal
{
public:
void Bark()
{
cout << "멍멍!" << endl;
}
};
class Cat : public Animal
{
public:
void Bark()
{
cout << "야옹" << endl;
}
};
int main()
{
Dog d;
d.Eat(); // Animal의 함수 호출
d.Bark(); // Dog 고유 함수 호출
Cat c;
c.Eat(); // Animal의 함수 호출
c.Bark(); // Cat 고유 함수 호출S
return 0;
}
C++
복사
Dog는 Animal을 상속받아서 Eat()을 사용할 수 있습니다.
4. 상속받은 함수 호출 (이름이 같을떄)
class Animal {
public:
void Speak() {
std::cout << "???";
}
};
class Cat : public Animal {
public:
void Speak() {
std::cout << "야옹!";
}
};
C++
복사
이 경우 함수 이름은 같지만 동작은 다릅니다.
5. virtual 함수와 다형성 (Polymorphism)
가상함수테이블(Virtual function table)
컴파일 시 가상함수가 정의된 클래스가 있다면 가상함수테이블(Virtual function table)이 만들어져서 바이너리 'rdata'영역에 기록되며 해당 클래스로 만들어진 객체에서 함수를 호출할 때 해당 클래스의 가상함수 테이블을 참조해서 함수를 호출된다. 편의를 위해 가상함수테이블은 "vtable" 이라고 명칭한다.
가상함수 테이블은 클래스 단위로 만들어 진다
객체를 생성하면 위 사진과 같이 각 객체마다 메모리 공간이 생성된다. 해당 객체의 시작 주소에는 참조해야 할 vtable의 주소가 저장된다.
부모 클래스를 상속받는 자식 클래스에서 오버라이딩된 함수들은 vtable의 함수 주소가 자식 클래스의 함수 주소로 업데이트된다. 자식 클래스에서 추가로 생성된 가상함수는 해당 클래스의 vtable 아래에 추가된다.
vtable을 보면 같은 함수더라도 Child에서 오버라이딩된 함수(func1, func3)는 주소가 다르고, 오버라이딩되지 않은 함수(func2)는 Parent 클래스 vtable의 함수 주소와 같다.
자식 클래스의 vtable은 부모 클래스의 vtable 값이 그대로 복사되며, 오버라이딩된 함수만 주소가 새로 업데이트된다. 만약 자식 클래스에 부모에 없는 새로운 가상함수를 추가할 경우, 해당 함수는 vtable의 마지막 부분에 추가된다.
vtable에는 virtual로 선언된 가상 함수만 저장된다. SourceCode를 보면 Parent에 func4만 일반 멤버 함수로 선언되어 있다. [image-1]에서 Parent 클래스 vtable을 보면 func4는 없는 것을 확인할 수 있다.
vtable 데이터 영역에 할당된다.
#include <iostream>
#include <cstring>
#pragma warning(disable: 4996)
using std::cout;
using std::cin;
using std::endl;
class AAA
{
private:
int num1;
public:
virtual void Func1() {
cout << "Func1" << endl;
}
virtual void Func2() {
cout << "Func2" << endl;
}
};
class BBB : public AAA
{
private:
int num2;
public:
virtual void Func1() {
cout << "BBB::Func1" << endl;
}
void Func3() {
cout << "Func3" << endl;
}
};
int main()
{
AAA* aptr = new AAA();
aptr->Func1();
BBB* bptr = new BBB();
bptr->Func1();
return 0;
}
C++
복사
A클래스에 virtual로 선언된 가상함수가 존재한다. 이렇듯 한 개 이상의 가상함수를 포함하는 클래스
대해서는 컴파일러가 다음 표와 같은 형태의 '가상함수 테이블'이란 것을 만든다.
이를 간단히 'V-Table(Virtual Table)'이라고도 하는데, 이는 실제 호출되어야 할 함수의 위치정보를 담고
있는 테이블이다.
위의 가상함수 테이블을 보면, key가 있고 value가 있다.
key는 호출하고자 하는 함수를 구분지어주는 구분자의 역할을 한다.
value는 구분자에 해당하는 함수의 주소정보를 알려주는 역할을 한다.
그래서 AAA객체의 Func1함수를 호출해야 할 경우, 위의 테이블에 첫 번째 행의 정보를 참조하여, 0x1
번지에 등록되어 있는 Func1함수를 호출하게 되는 것이다.
BBB클래스 역시 한 개 이상의 가상함수를 포함하고 있으므로 다음의 형태로 가상함수 테이블이 생성된다
virtual 키워드를 사용하면 부모 포인터/레퍼런스를 통해도 자식의 오버라이딩된 함수가 호출됩니다.
이걸 동적 바인딩 또는 런타임 다형성이라고 합니다.
6. 순수 가상 함수 (Abstract Class)
class Shape {
public:
virtual double Area() const = 0; // 순수 가상 함수
};
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
double Area() const override {
return 3.14 * radius * radius;
}
};
C++
복사
= 0이 붙은 함수는 구현이 없는 순수 가상 함수입니다.
이걸 포함한 클래스는 추상 클래스가 되며, 객체를 직접 생성할 수 없습니다.
7. 상속 관련 주의사항
•
소멸자는 반드시 virtual ~Base()로 선언하여 자식 객체 소멸 보장
•
생성자/소멸자는 상속되지 않음 → 자식에서 명시 호출 필요 가능
•
private 멤버는 자식에서 접근 불가 (접근은 protected or public만)
핵심 요약
개념 | 요약 |
public 상속 | 일반적으로 사용, 부모 멤버 그대로 유지 |
virtual 함수 | 다형성(오버라이딩)을 가능하게 함 |
= 0 | 순수 가상 함수 → 추상 클래스가 됨 |
virtual ~Base() | 메모리 해제시 자식까지 소멸시키기 위해 필요 |
C++ 상속 연습문제 50선 (동적할당 X) - Part 1
// C++ 상속 연습문제 50선 (virtual + 다형성 + 정적할당 + 주소값 배열 + for 루프)
// 난이도를 5단계로 세분화하여 완만한 학습 곡선 제공
// Part 1: 문제 1-25 | Part 2: 문제 26-50
레벨 1: 기본 상속과 단일 가상함수 (문제 1-10)
문제 1. 동물 소리 출력
Animal 클래스는 이름(name)을 멤버로 가지며 Speak() 함수는 이름과 함께 동물의 울음소리를 출력합니다. Dog는 "barks!", Cat은 "meows!"로 출력되도록 구현하세요.
Dog d1("Baekgu"); Cat c1("Nabi");
Animal* animals[] = { &d1, &c1 };
for (int i = 0; i < 2; ++i)
animals[i]->Speak();
C++
복사
문제 2. 도형 면적 계산
Shape 클래스는 Area()라는 가상 함수를 가지고 있으며, Circle은 반지름, Rectangle은 가로/세로 값을 멤버로 가집니다. 각 도형의 면적을 정확히 계산하여 출력하도록 구현하세요.
Circle c(3.0); Rectangle r(4.0, 2.0);
Shape* shapes[] = { &c, &r };
for (int i = 0; i < 2; ++i)
std::cout << shapes[i]->Area() << std::endl;
C++
복사
문제 3. 직원 급여 계산
Employee 클래스는 이름과 기본급을 가지고 있으며, Manager는 50% 보너스, Developer는 10% 인센티브를 적용해 급여를 계산합니다.
Manager m("Kim", 5000); Developer d("Lee", 3000);
Employee* staff[] = { &m, &d };
for (int i = 0; i < 2; ++i)
std::cout << staff[i]->CalculateSalary() << std::endl;
C++
복사
문제 4. 악기 연주
Instrument 클래스는 Play() 가상함수를 가집니다. Piano는 "ding ding!", Guitar는 "strum strum!"을 출력합니다.
Piano p; Guitar g;
Instrument* instruments[] = { &p, &g };
for (int i = 0; i < 2; ++i)
instruments[i]->Play();
C++
복사
문제 5. 게임 캐릭터 공격
Character 클래스는 이름을 멤버로 가지며 Attack()은 각 클래스에 맞는 공격 동작을 출력해야 합니다. Knight: "slashes with sword!", Mage: "casts a spell!"
Knight k("Arthur"); Mage m2("Merlin");
Character* party[] = { &k, &m2 };
for (int i = 0; i < 2; ++i)
party[i]->Attack();
C++
복사
문제 6. 스마트기기 명령
SmartDevice 클래스는 Execute(command) 함수를 가지며 명령어에 따라 다르게 반응해야 합니다.
SmartLight sl; SmartSpeaker ss;
SmartDevice* devices[] = { &sl, &ss };
for (int i = 0; i < 2; ++i)
devices[i]->Execute("turn on");
C++
복사
문제 7. 유닛 이동
Unit 클래스는 이름을 멤버로 가지며 Move() 함수에서 각 유닛의 이동 특성을 출력하세요.
Warrior w("Thor"); Archer a("Robin");
Unit* units[] = { &w, &a };
for (int i = 0; i < 2; ++i)
units[i]->Move();
C++
복사
문제 8. 알림 시스템
Notification은 수신자 정보를 가지며 Send() 함수로 알림 전송 메시지를 출력합니다.
EmailNotification e("kim@example.com");
SMSNotification s("010-1234-5678");
Notification* notices[] = { &e, &s };
for (int i = 0; i < 2; ++i)
notices[i]->Send();
C++
복사
문제 9. 몬스터 전투
Monster 클래스는 이름과 HP를 가지며 Attack() 가상함수를 가집니다. Goblin은 "throws rock!", Orc는 "swings axe!"를 출력합니다.
Goblin g("Goblin", 50); Orc o("Orc", 100);
Monster* monsters[] = { &g, &o };
for (int i = 0; i < 2; ++i)
monsters[i]->Attack();
C++
복사
문제 10. 결제 시스템
Payment 클래스는 금액을 가지며 Process() 가상함수를 가집니다. CreditCard는 "Processing credit card payment", Cash는 "Processing cash payment"를 출력합니다.
CreditCard cc(10000); Cash cash(5000);
Payment* payments[] = { &cc, &cash };
for (int i = 0; i < 2; ++i)
payments[i]->Process();
C++
복사

레벨 2: 다단계 상속과 복수 가상함수 (문제 11-20)
문제 11. 운동 기록
Exercise 클래스는 운동 시간을 기반으로 칼로리를 계산하며, PrintCalories()로 출력합니다. Running은 시간당 10kcal, Cycling은 시간당 6kcal를 소모합니다.
Running run(30); Cycling cyc(60);
Exercise* logs[] = { &run, &cyc };
for (int i = 0; i < 2; ++i)
logs[i]->PrintCalories();
C++
복사
문제 12. 책 정보 출력
Book 클래스는 제목과 저자를 멤버로 가지며 Display() 가상함수를 가집니다. EBook은 파일 크기, PaperBook은 페이지 수를 추가로 가지며 각각 Display()를 오버라이딩합니다.
EBook eb("Clean Code", "Robert Martin", 5.2);
PaperBook pb("Refactoring", "Martin Fowler", 450);
Book* books[] = { &eb, &pb };
for (int i = 0; i < 2; ++i)
books[i]->Display();
C++
복사
문제 13. RPG 캐릭터 3단계 상속
GameUnit (기본 HP) → Character (이름 추가, ShowStatus() 가상함수) → Warrior/Wizard (공격력 추가, ShowStatus() 오버라이딩)
각 클래스에서 ShowStatus()는 자신의 모든 정보를 출력합니다.
Warrior w("Gorath", 100, 30);
Wizard wiz("Azura", 80, 50);
Character* party[] = { &w, &wiz };
for (int i = 0; i < 2; ++i)
party[i]->ShowStatus();
C++
복사
문제 14. 다형성 함수 체인 (여러 가상함수)
Vehicle 클래스는 StartEngine(), Drive(), StopEngine() 세 개의 가상함수를 가지며, Car와 Motorcycle은 각각 모든 함수를 오버라이딩합니다.
Car c; Motorcycle m;
Vehicle* vehicles[] = { &c, &m };
for (int i = 0; i < 2; ++i) {
vehicles[i]->StartEngine();
vehicles[i]->Drive();
vehicles[i]->StopEngine();
}
C++
복사
문제 15. 상점 아이템 시스템
Item 클래스는 이름과 가격을 가지며 GetInfo() 가상함수를 가집니다. Weapon은 공격력, Potion은 회복량을 추가로 가지며 각각 GetInfo()를 오버라이딩합니다.
Weapon sword("Iron Sword", 100, 25);
Potion hp("Health Potion", 50, 30);
Item* items[] = { &sword, &hp };
for (int i = 0; i < 2; ++i)
items[i]->GetInfo();
C++
복사
문제 16. 계좌 시스템
Account 클래스는 잔액을 가지며 Deposit(), Withdraw(), ShowBalance() 가상함수를 가집니다. SavingsAccount는 이자율 추가, CheckingAccount는 수수료를 추가하여 각각 오버라이딩합니다.
SavingsAccount sa(1000, 0.03);
CheckingAccount ca(2000, 1.5);
Account* accounts[] = { &sa, &ca };
for (int i = 0; i < 2; ++i) {
accounts[i]->Deposit(500);
accounts[i]->Withdraw(200);
accounts[i]->ShowBalance();
}
C++
복사
문제 17. 음료 자판기
Drink 클래스는 이름과 가격을 가지며 Dispense() 가상함수를 가집니다. Soda는 "Dispensing soda with ice", Juice는 "Dispensing fresh juice"를 출력합니다.
Soda soda("Coke", 1500); Juice juice("Orange", 2000);
Drink* drinks[] = { &soda, &juice };
for (int i = 0; i < 2; ++i)
drinks[i]->Dispense();
C++
복사
문제 18. 교통수단 요금 계산
Transport 클래스는 거리를 가지며 CalculateFare() 가상함수를 가집니다. Taxi는 거리 × 1000원, Bus는 거리와 관계없이 1500원을 반환합니다.
Taxi taxi(5); Bus bus(5);
Transport* transports[] = { &taxi, &bus };
for (int i = 0; i < 2; ++i)
std::cout << transports[i]->CalculateFare() << std::endl;
C++
복사
문제 19. 학생 성적 관리
Student 클래스는 이름과 점수를 가지며 GetGrade() 가상함수를 가집니다. UndergraduateStudent는 90점 이상 A, GraduateStudent는 95점 이상 A로 판정합니다.
UndergraduateStudent us("Kim", 92);
GraduateStudent gs("Lee", 92);
Student* students[] = { &us, &gs };
for (int i = 0; i < 2; ++i)
std::cout << students[i]->GetGrade() << std::endl;
C++
복사
문제 20. 로봇 동작 제어
Robot 클래스는 이름을 가지며 Move(), Work() 두 개의 가상함수를 가집니다. CleaningRobot은 "moves slowly"/"cleans floor", DeliveryRobot은 "moves fast"/"delivers package"를 출력합니다.
CleaningRobot cr("Roomba"); DeliveryRobot dr("DeliveryBot");
Robot* robots[] = { &cr, &dr };
for (int i = 0; i < 2; ++i) {
robots[i]->Move();
robots[i]->Work();
}
C++
복사


레벨 3: 추상 클래스와 인터페이스 패턴 (문제 21-30)
문제 21. 순수 가상함수 기본
Shape (추상 클래스, Area() 순수 가상함수) → Circle/Square (구현 클래스)
각 도형의 면적을 계산하여 출력하세요.
Circle c(5.0);
Square s(4.0);
Shape* shapes[] = { &c, &s };
for (int i = 0; i < 2; ++i)
std::cout << shapes[i]->Area() << std::endl;
C++
복사
문제 22. 추상 클래스와 구현 클래스 혼합
Shape (추상 클래스, Area()와 Perimeter() 순수 가상함수) → Rectangle/Circle (구현 클래스), Triangle은 Rectangle을 상속받아 Area()만 재오버라이딩.
3개 이상의 도형 객체를 배열에 담아 면적과 둘레를 모두 출력하세요.
Rectangle r(4, 5);
Circle c(3);
Triangle t(3, 4, 5); // Rectangle 상속
Shape* shapes[] = { &r, &c, &t };
for (int i = 0; i < 3; ++i) {
std::cout << "Area: " << shapes[i]->Area() << std::endl;
std::cout << "Perimeter: " << shapes[i]->Perimeter() << std::endl;
}
C++
복사
문제 23. 다중 기능 전자기기 (복수 가상함수 + 선택적 오버라이딩)
Device 클래스는 PowerOn(), PowerOff(), ShowInfo() 가상함수를 가집니다. Smartphone은 세 함수를 모두 오버라이딩하고, SmartWatch는 PowerOn()과 ShowInfo()만 오버라이딩합니다.
Smartphone sp("Galaxy");
SmartWatch sw("Apple Watch");
Device* devices[] = { &sp, &sw };
for (int i = 0; i < 2; ++i) {
devices[i]->PowerOn();
devices[i]->ShowInfo();
devices[i]->PowerOff();
}
C++
복사
문제 24. 인터페이스 시뮬레이션 (단일 인터페이스)
IDrawable (순수 가상: Draw()) 추상 클래스를 만들고, Circle과 Rectangle이 구현합니다. 각 도형을 그리는 동작을 출력하세요.
Circle c(5); Rectangle r(4, 3);
IDrawable* drawables[] = { &c, &r };
for (int i = 0; i < 2; ++i)
drawables[i]->Draw();
C++
복사
문제 25. 다중 인터페이스 상속
IRenderable (순수 가상: Render()), IUpdatable (순수 가상: Update()) 두 추상 클래스를 만들고, GameObject는 두 인터페이스를 모두 상속받아 구현합니다. Player와 Enemy는 GameObject를 상속받아 각자 다르게 오버라이딩합니다.
Player p("Hero"); Enemy e("Goblin");
IRenderable* renderables[] = { &p, &e };
IUpdatable* updatables[] = { &p, &e };
for (int i = 0; i < 2; ++i) {
updatables[i]->Update();
renderables[i]->Render();
}
C++
복사
Part 1 난이도별 문제 분포 (문제 1-25)
레벨 | 난이도 | 문제 번호 | 문제 수 | 핵심 개념 |
1 | 1-10 | 10문제 | 기본 상속, 단일 가상함수, 다형성 | |
2 | 11-20 | 10문제 | 다단계 상속, 복수 가상함수 | |
3 | 21-25 | 5문제 | 추상 클래스, 인터페이스 기초 |




