상속 (Inheritance)
개요
상속(Inheritance)은 객체 지향 프로그래밍의 핵심 개념 중 하나로, 기존 클래스(기반 클래스 또는 부모 클래스)의 멤버(변수, 메서드)를 새로운 클래스(파생 클래스 또는 자식 클래스)가 물려받아 사용할 수 있게 하는 기능입니다. 이를 통해 코드 재사용성을 높이고, 클래스 간의 계층 구조를 형성하여 프로그램을 더 체계적으로 구성할 수 있습니다.
C++에서는 한 클래스가 다른 클래스로부터 상속받을 때, 파생 클래스는 기반 클래스의 public
및 protected
멤버에 접근할 수 있습니다.
C++ 버전별 주요 키워드 도입 시기
- C++98:
class
,public
,protected
,private
,virtual
, 생성자 초기화 목록 순서는 멤버 변수 선언 순서와 일치해야 함 - C++11:
override
,final
,= default
,= delete
내용 설명
제공된 예제는 무기(Weapon)라는 개념을 상속을 통해 구체화하는 과정을 보여줍니다.
1. AttackInterface
- 순수 가상 클래스
class AttackInterface
{
public:
AttackInterface() = default;
~AttackInterface() = default;
virtual void attack() const = 0;
};
AttackInterface
는 '공격'이라는 행위를 정의하는 순수 가상 클래스(인터페이스)입니다.virtual void attack() const = 0;
는 순수 가상 함수로, 이 함수를 포함하는 클래스는 객체를 직접 생성할 수 없는 추상 클래스가 됩니다.- 이 인터페이스를 상속받는 모든 클래스는
attack
메서드를 반드시 구현해야 합니다.
2. Weapon
- 추상 기반 클래스
class Weapon : public AttackInterface
{
public:
Weapon(const std::string &_name,
const float _damage,
const float _attack_speed,
const bool _single_handed,
const bool _dual_handed)
: name(_name), damage(_damage), attack_speed(_attack_speed),
single_handed(_single_handed), dual_handed(_dual_handed){};
virtual ~Weapon() = default;
// ... getter methods ...
protected:
std::string name;
float damage;
float attack_speed;
bool single_handed;
bool dual_handed;
};
Weapon
클래스는AttackInterface
를 상속받아 공격 기능을 가지면서, 모든 무기가 공통으로 가질 속성(이름, 공격력 등)을 정의합니다.AttackInterface
의attack
메서드를 구현하지 않았기 때문에,Weapon
클래스 역시 추상 클래스로 남아 객체를 생성할 수 없습니다.- 멤버 변수들을
protected
로 선언하여 파생 클래스에서 접근할 수 있도록 허용합니다. - 생성자 초기화 목록: 생성자 초기화 목록(
: name(_name), ...
)의 순서는 멤버 변수가 클래스에 선언된 순서와 일치시켜야 합니다.
3. Axe
, Longbow
- 구체적인 파생 클래스
class Axe : public Weapon
{
public:
Axe(const std::string &_name,
const float _damage,
const float _attack_speed)
: Weapon(_name, _damage, _attack_speed, true, false){};
virtual ~Axe() = default;
void attack() const final;
};
Axe
와Longbow
는Weapon
클래스를 상속받는 구체적인 클래스입니다.- 각 클래스는 생성자에서
Weapon
의 생성자를 호출하여 무기 종류에 맞는 속성(한손무기, 양손무기 등)을 초기화합니다. attack
메서드를final
키워드와 함께 오버라이딩(재정의)하여 각 무기에 맞는 공격 동작을 구현합니다.final
은 이 메서드가 더 이상 파생 클래스에서 재정의될 수 없음을 명시합니다.
예제 코드
헤더 파일 (Weapons.h
)
#pragma once
#include <string>
class AttackInterface
{
public:
AttackInterface() = default;
virtual ~AttackInterface() = default;
virtual void attack() const = 0;
};
class Weapon : public AttackInterface
{
public:
Weapon(const std::string &_name,
const float _damage,
const float _attack_speed,
const bool _single_handed,
const bool _dual_handed)
: name(_name), damage(_damage), attack_speed(_attack_speed),
single_handed(_single_handed), dual_handed(_dual_handed){};
virtual ~Weapon() = default;
std::string get_name() const;
float get_damage() const;
float get_attack_speed() const;
bool get_single_handed() const;
bool get_dual_handed() const;
protected:
std::string name;
float damage;
float attack_speed;
bool single_handed;
bool dual_handed;
};
class Axe : public Weapon
{
public:
Axe(const std::string &_name,
const float _damage,
const float _attack_speed)
: Weapon(_name, _damage, _attack_speed, true, false){};
~Axe() = default;
void attack() const final;
};
class Longbow : public Weapon
{
public:
Longbow(const std::string &_name,
const float _damage,
const float _attack_speed)
: Weapon(_name, _damage, _attack_speed, false, true){};
~Longbow() = default;
void attack() const final;
};
정의 파일 (Weapons.cpp
)
#include <iostream>
#include "Weapons.h"
std::string Weapon::get_name() const
{
return name;
}
float Weapon::get_damage() const
{
return damage;
}
float Weapon::get_attack_speed() const
{
return attack_speed;
}
bool Weapon::get_single_handed() const
{
return single_handed;
}
bool Weapon::get_dual_handed() const
{
return dual_handed;
}
void Axe::attack() const
{
std::cout << "Attacking with an axe\n";
}
void Longbow::attack() const
{
std::cout << "Attacking with a longbow\n";
}
사용법 (main.cpp
)
#include <iostream>
#include "Weapons.h"
int main()
{
const auto axe = Axe{"Great dwarfen axe", 12.0F, 1.2F};
const auto longbow = Longbow{"Bow of the emperer", 25.0F, 0.5F};
axe.attack();
longbow.attack();
return 0;
}
실행 결과
Attacking with an axe
Attacking with a longbow
활용팁
상속의 가장 큰 장점 중 하나는 다형성(Polymorphism)을 구현할 수 있다는 것입니다. 기반 클래스의 포인터나 참조를 통해 파생 클래스의 객체를 다룰 수 있습니다. 예를 들어, AttackInterface*
타입의 배열에 Axe
와 Longbow
객체를 담아두고, 반복문을 통해 모든 무기의 attack
메서드를 일관된 방식으로 호출할 수 있습니다.
#include <vector>
#include <memory>
// ... Weapons.h, Weapons.cpp는 동일 ...
int main()
{
std::vector<std::unique_ptr<AttackInterface>> weapons;
weapons.push_back(std::make_unique<Axe>("Great dwarfen axe", 12.0F, 1.2F));
weapons.push_back(std::make_unique<Longbow>("Bow of the emperer", 25.0F, 0.5F));
for (const auto& weapon : weapons)
{
weapon->attack();
}
return 0;
}
위와 같이 코드를 수정하면, 새로운 무기 종류가 추가되더라도 main
함수의 반복문 코드는 변경할 필요가 없어 확장성 높은 코드를 작성할 수 있습니다.
'개발 > C++ (98,03,11,14,17,20,23)' 카테고리의 다른 글
Modern C++ : 클래스(Class) (98, 11, 17) (0) | 2025.09.17 |
---|---|
Modern C++ : 유틸리티 함수 활용 (11, 17) (1) | 2025.09.15 |
Modern C++ : 직접 구현한 표준 알고리즘, equal, fill_n, iota, copy, accumulate (98, 11) (0) | 2025.09.14 |
Modern C++ : std::function (11) (0) | 2025.09.13 |
Modern C++ : std::min, max, equal, any_of, all_of, none_of (98, 11, 17) (0) | 2025.09.12 |
Modern C++ : std::remove, std::sort, std::replace (98, 20) (1) | 2025.09.11 |
Modern C++ : std::transform & std::accumulate (98) (0) | 2025.09.10 |
Modern C++ : std::generate, local static variables (98, 11, 14) (0) | 2025.09.09 |