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 고유 함수 호출
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 로 선언된 가상 함수만 저장된다. SouceCode를 보면 Parent에 func4만 일반 멤버 함수로 선언되어 있다. [image-1]에서 Parent 클래스 vtable을 보면 func4는 없는 것을 확인할 수 있다.
#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(const int argc, const char* const argv[])
{
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클래스 역시 한 개 이상의 가상함수를 포함하고 있으므로 다음의 형태로 가상함수 테이블이 생성된다
이걸 동적 바인딩 또는 런타임 다형성이라고 합니다.
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++
복사
이걸 포함한 클래스는 추상 클래스가 되며, 객체를 직접 생성할 수 없습니다.
7. 상속 관련 주의사항
•
소멸자는 반드시 virtual ~Base()로 선언하여 자식 객체 소멸 보장
•
생성자/소멸자는 상속되지 않음 → 자식에서 명시 호출 필요 가능
•
private 멤버는 자식에서 접근 불가 (접근은 protected or public만)
핵심 요약
개념 | 요약 |
public 상속 | 일반적으로 사용, 부모 멤버 그대로 유지 |
virtual 함수 | 다형성(오버라이딩)을 가능하게 함 |
= 0 | 순수 가상 함수 → 추상 클래스가 됨 |
virtual ~Base() | 메모리 해제시 자식까지 소멸시키기 위해 필요 |
C++ 상속 연습문제 20선 (동적할당 X)
//
C++ 상속 연습문제 20선 (virtual + 다형성 + 정적할당 + 주소값 배열 + for 루프)
문제 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. 운동 기록
Exercise 클래스는 운동 시간을 기반으로 칼로리를 계산하며, PrintCalories()로 출력합니다.
Running run(30); Cycling cyc(60);
Exercise* logs[] = { &run, &cyc };
for (int i = 0; i < 2; ++i)
logs[i]->PrintCalories();
C++
복사
문제 10. 도서관 아이템
LibraryItem 클래스는 제목(title)을 멤버로 가지며, Book과 DVD는 각 타입에 맞는 정보를 출력합니다.
Book b("C++ Guide"); DVD d2("Inception");
LibraryItem* items[] = { &b, &d2 };
for (int i = 0; i < 2; ++i)
items[i]->PrintInfo();
C++
복사
문제 11. 교통수단
Vehicle 클래스는 Move() 가상함수를 가지며, Bus와 Train은 각각 고유한 이동 방식 메시지를 출력합니다.
Bus bus; Train train;
Vehicle* vehicles[] = { &bus, &train };
for (int i = 0; i < 2; ++i)
vehicles[i]->Move();
C++
복사
문제 12. 동물 병원 백신
Pet 클래스는 이름을 가지며, Dog와 Bird는 각각 종에 따라 다른 백신 접종 메시지를 출력합니다.
Dog d3("Bori"); Bird b2("Chirpy");
Pet* pets[] = { &d3, &b2 };
for (int i = 0; i < 2; ++i)
pets[i]->Vaccinate();
C++
복사
문제 13. 전자기기 전원
Device 클래스는 제품명을 멤버로 가지며, PowerOn()은 기기마다 고유 전원 켜짐 메시지를 출력합니다.
Laptop l("Lenovo"); Tablet t("iPad");
Device* gadgets[] = { &l, &t };
for (int i = 0; i < 2; ++i)
gadgets[i]->PowerOn();
C++
복사
문제 14. 결제 수단
PaymentMethod 클래스는 계정 정보를 멤버로 가지고 Pay(amount)를 통해 결제 메시지를 출력합니다.
CreditCard cc("Kim", "1234"); PayPal pp("lee@mail.com");
PaymentMethod* pay[] = { &cc, &pp };
for (int i = 0; i < 2; ++i)
pay[i]->Pay(10000);
C++
복사
문제 15. 학교 구성원 소개
Person 클래스는 이름을 멤버로 가지며, Student/Teacher는 역할과 함께 자신을 소개하는 Introduce()를 구현합니다.
Student stu("Jisoo"); Teacher tea("Minho");
Person* members[] = { &stu, &tea };
for (int i = 0; i < 2; ++i)
members[i]->Introduce();
C++
복사
문제 16. 전투 피해량 계산
Fighter 클래스는 체력과 공격력을 멤버로 가지며 DealDamage()는 피해량을 출력합니다.
Swordman sw(100, 20); Mage mg(100, 30);
Fighter* fighters[] = { &sw, &mg };
for (int i = 0; i < 2; ++i)
fighters[i]->DealDamage();
C++
복사
문제 17. 세금 계산기
TaxPayer는 이름과 소득을 멤버로 가지며, Employee와 Freelancer는 서로 다른 세율로 세금을 계산합니다.
Employee emp("Kim", 5000); Freelancer fr("Lee", 3000);
TaxPayer* people[] = { &emp, &fr };
for (int i = 0; i < 2; ++i)
people[i]->CalculateTax();
C++
복사
문제 18. 주문 시스템
MenuItem 클래스는 가격을 멤버로 가지며, PrintPrice()는 항목 이름과 가격을 출력합니다.
Burger bur(5000); Pizza piz(8000);
MenuItem* order[] = { &bur, &piz };
for (int i = 0; i < 2; ++i)
order[i]->PrintPrice();
C++
복사
문제 19. 전시회 작품 전시
ArtWork 클래스는 작품명을 멤버로 가지며, Painting과 Sculpture는 Display()에서 고유 메시지를 출력합니다.
Painting p2("Mona Lisa"); Sculpture s2("David");
ArtWork* gallery[] = { &p2, &s2 };
for (int i = 0; i < 2; ++i)
gallery[i]->Display();
C++
복사
문제 20. 운동 시뮬레이터
Workout 클래스는 반복 횟수를 멤버로 가지며, Pushup과 Squat은 각 동작명과 함께 반복 횟수를 Show()로 출력합니다.
Pushup pu(20); Squat sq(30);
Workout* program[] = { &pu, &sq };
for (int i = 0; i < 2; ++i)
program[i]->Show();
C++
복사