상속의 개념과 구현
상속의 개념
상속(Inheritance)은 객체 지향 프로그래밍의 핵심 개념 중 하나입니다.
상속은 기존 클래스의 특성을 새로운 클래스가 물려받아 재사용하고 확장할 수 있게 해주는 메커니즘입니다.
상속의 주요 목적
- 코드 재사용
- 계층 구조 표현
- 다형성 구현의 기반
상속 관계의 용어
- 기본 클래스 (Base class) 또는 부모 클래스 (Parent class) : 특성을 물려주는 클래스
- 파생 클래스 (Derived class) 또는 자식 클래스 (Child class) : 특성을 물려받는 클래스
C++에서의 상속 구현
C++에서 상속은 다음과 같은 문법으로 구현됩니다.
class DerivedClass : [access-specifier] BaseClass {
// 파생 클래스의 멤버
};
여기서 access-specifier
는 public
, protected
, 또는 private
일 수 있으며, 생략 시 기본값은 private
입니다.
상속의 종류
1. public 상속
- 가장 일반적인 형태
- 기본 클래스의 public 멤버 → 파생 클래스에서 public
- 기본 클래스의 protected 멤버 → 파생 클래스에서 protected
2. protected 상속
- 기본 클래스의 public 멤버 → 파생 클래스에서 protected
- 기본 클래스의 protected 멤버 → 파생 클래스에서 protected
3. private 상속
- 기본 클래스의 public 멤버 → 파생 클래스에서 private
- 기본 클래스의 protected 멤버 → 파생 클래스에서 private
class Base {
public:
void publicMethod() {}
protected:
void protectedMethod() {}
private:
void privateMethod() {}
};
class PublicDerived : public Base {
// publicMethod() is public
// protectedMethod() is protected
// privateMethod() is not accessible
};
class ProtectedDerived : protected Base {
// publicMethod() is protected
// protectedMethod() is protected
// privateMethod() is not accessible
};
class PrivateDerived : private Base {
// publicMethod() is private
// protectedMethod() is private
// privateMethod() is not accessible
};
생성자와 소멸자
상속 관계에서 객체 생성 및 소멸 시 호출 순서는 다음과 같습니다.
1. 객체 생성 시
- 기본 클래스의 생성자
- 파생 클래스의 생성자
2. 객체 소멸 시
- 파생 클래스의 소멸자
- 기본 클래스의 소멸자
class Base {
public:
Base() { std::cout << "Base constructor" << std::endl; }
~Base() { std::cout << "Base destructor" << std::endl; }
};
class Derived : public Base {
public:
Derived() { std::cout << "Derived constructor" << std::endl; }
~Derived() { std::cout << "Derived destructor" << std::endl; }
};
int main() {
Derived d;
return 0;
}
Base constructor
Derived constructor
Derived destructor
Base destructor
멤버 초기화
파생 클래스의 생성자에서 기본 클래스의 생성자를 명시적으로 호출할 수 있습니다.
class Animal {
protected:
std::string name;
public:
Animal(const std::string& n) : name(n) {}
};
class Dog : public Animal {
private:
std::string breed;
public:
Dog(const std::string& n, const std::string& b)
: Animal(n), breed(b) {}
};
상속과 접근 제어
private
멤버: 파생 클래스에서 직접 접근 불가protected
멤버 : 파생 클래스에서 접근 가능, 외부에서는 접근 불가public
멤버 : 어디서든 접근 가능
class Base {
private:
int privateMember;
protected:
int protectedMember;
public:
int publicMember;
};
class Derived : public Base {
public:
void accessMembers() {
// privateMember = 1; // Error: private member not accessible
protectedMember = 2; // OK: protected member accessible
publicMember = 3; // OK: public member accessible
}
};
실습 : 도형 클래스 계층 구현
다음 요구사항을 만족하는 도형 클래스 계층을 구현해보세요.
Shape
를 기본 클래스로 하고,Circle
,Rectangle
,Triangle
을 파생 클래스로 구현- 각 클래스는 적절한 생성자를 가져야 함
- 모든 도형은 넓이를 계산하는
getArea()
메서드를 가져야 함 - 모든 도형은 둘레를 계산하는
getPerimeter()
메서드를 가져야 함 - 모든 도형은 자신의 정보를 출력하는
print()
메서드를 가져야 함
#include <iostream>
#include <cmath>
#include <vector>
class Shape {
protected:
std::string name;
public:
Shape(const std::string& n) : name(n) {}
virtual double getArea() const = 0;
virtual double getPerimeter() const = 0;
virtual void print() const {
std::cout << "Shape: " << name << std::endl;
}
virtual ~Shape() {} // 가상 소멸자
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : Shape("Circle"), radius(r) {}
double getArea() const override {
return M_PI * radius * radius;
}
double getPerimeter() const override {
return 2 * M_PI * radius;
}
void print() const override {
Shape::print();
std::cout << "Radius: " << radius
<< ", Area: " << getArea()
<< ", Perimeter: " << getPerimeter() << std::endl;
}
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : Shape("Rectangle"), width(w), height(h) {}
double getArea() const override {
return width * height;
}
double getPerimeter() const override {
return 2 * (width + height);
}
void print() const override {
Shape::print();
std::cout << "Width: " << width << ", Height: " << height
<< ", Area: " << getArea()
<< ", Perimeter: " << getPerimeter() << std::endl;
}
};
class Triangle : public Shape {
private:
double a, b, c; // 세 변의 길이
public:
Triangle(double side1, double side2, double side3)
: Shape("Triangle"), a(side1), b(side2), c(side3) {}
double getArea() const override {
// 헤론의 공식 사용
double s = (a + b + c) / 2;
return sqrt(s * (s - a) * (s - b) * (s - c));
}
double getPerimeter() const override {
return a + b + c;
}
void print() const override {
Shape::print();
std::cout << "Sides: " << a << ", " << b << ", " << c
<< ", Area: " << getArea()
<< ", Perimeter: " << getPerimeter() << std::endl;
}
};
int main() {
std::vector<Shape*> shapes;
shapes.push_back(new Circle(5));
shapes.push_back(new Rectangle(4, 6));
shapes.push_back(new Triangle(3, 4, 5));
for (const auto& shape : shapes) {
shape->print();
}
// 메모리 해제
for (auto shape : shapes) {
delete shape;
}
return 0;
}
연습 문제
Vehicle
클래스를 기본 클래스로 하고,Car
와Motorcycle
클래스를 파생 클래스로 구현하세요. 각 클래스는 적절한 속성(예 : 속도, 연료량)과 메서드(예 : 가속, 감속)를 가져야 합니다.Employee
클래스를 기본 클래스로 하고,Manager
와Engineer
클래스를 파생 클래스로 구현하세요. 각 클래스는 적절한 속성(예 : 이름, 급여)과 메서드(예 : 급여 계산)를 가져야 합니다.BankAccount
클래스를 기본 클래스로 하고,SavingsAccount
와CheckingAccount
클래스를 파생 클래스로 구현하세요. 각 클래스는 적절한 속성(예 : 계좌번호, 잔액)과 메서드(예 : 입금, 출금, 이자 계산)를 가져야 합니다.
참고자료
- Stroustrup, Bjarne. "The C++ Programming Language (4th Edition)"
- Meyers, Scott. "Effective C++ : 55 Specific Ways to Improve Your Programs and Designs"
- Sutter, Herb and Alexandrescu, Andrei. "C++ Coding Standards : 101 Rules, Guidelines, and Best Practices"
- C++ Reference : Derived classes
- ISO C++ Core Guidelines : C.120 : Use class hierarchies to represent concepts with inherent hierarchical structure