Programming Languages/C++
다형성(Polymorphism)
newclass
2025. 3. 28. 00:06
5.6 다형성(Polymorphism)
5.6.1 함수 오버라이딩(Function Overriding)
파생 클래스에서 기본 클래스의 메서드를 재정의하는 것:
#include <iostream>
#include <string>
class Shape {
protected:
std::string name;
public:
Shape(const std::string& n) : name(n) {}
void display() const {
std::cout << "도형: " << name << std::endl;
}
double area() const {
std::cout << "기본 도형의 면적 계산" << std::endl;
return 0.0;
}
};
class Circle : public Shape {
private:
double radius;
public:
Circle(const std::string& n, double r) : Shape(n), radius(r) {}
// display() 메서드 오버라이딩
void display() const {
std::cout << "원: " << name << ", 반지름: " << radius << std::endl;
}
// area() 메서드 오버라이딩
double area() const {
return 3.14159 * radius * radius;
}
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(const std::string& n, double w, double h)
: Shape(n), width(w), height(h) {}
// display() 메서드 오버라이딩
void display() const {
std::cout << "직사각형: " << name << ", 가로: " << width
<< ", 세로: " << height << std::endl;
}
// area() 메서드 오버라이딩
double area() const {
return width * height;
}
};
int main() {
Shape shape("일반 도형");
Circle circle("원A", 5.0);
Rectangle rect("직사각형B", 4.0, 6.0);
shape.display();
circle.display();
rect.display();
std::cout << "일반 도형 면적: " << shape.area() << std::endl;
std::cout << "원 면적: " << circle.area() << std::endl;
std::cout << "직사각형 면적: " << rect.area() << std::endl;
return 0;
}
5.6.2 가상 함수(Virtual Functions)와 동적 바인딩
가상 함수는 런타임에 적절한 함수 버전을 결정하는 다형성의 핵심입니다:
#include <iostream>
#include <string>
#include <vector>
class Shape {
protected:
std::string name;
public:
Shape(const std::string& n) : name(n) {}
// 가상 함수
virtual void display() const {
std::cout << "도형: " << name << std::endl;
}
// 가상 함수
virtual double area() const {
return 0.0;
}
};
class Circle : public Shape {
private:
double radius;
public:
Circle(const std::string& n, double r) : Shape(n), radius(r) {}
// 가상 함수 오버라이딩
void display() const override {
std::cout << "원: " << name << ", 반지름: " << radius << std::endl;
}
// 가상 함수 오버라이딩
double area() const override {
return 3.14159 * radius * radius;
}
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(const std::string& n, double w, double h)
: Shape(n), width(w), height(h) {}
// 가상 함수 오버라이딩
void display() const override {
std::cout << "직사각형: " << name << ", 가로: " << width
<< ", 세로: " << height << std::endl;
}
// 가상 함수 오버라이딩
double area() const override {
return width * height;
}
};
int main() {
// 기본 클래스 포인터로 파생 클래스 객체 가리키기
Shape* shapes[3];
shapes[0] = new Shape("일반 도형");
shapes[1] = new Circle("원A", 5.0);
shapes[2] = new Rectangle("직사각형B", 4.0, 6.0);
// 동적 바인딩: 실제 객체 타입에 따라 적절한 함수 호출
for (int i = 0; i < 3; i++) {
shapes[i]->display(); // 가상 함수 호출
std::cout << "면적: " << shapes[i]->area() << std::endl;
}
// 메모리 해제
for (int i = 0; i < 3; i++) {
delete shapes[i];
}
return 0;
}
가상 함수의 특징:
- virtual 키워드로 선언
- 파생 클래스에서 override 키워드(C++11)로 명시적 재정의 가능
- 동적 바인딩을 통해 런타임에 올바른 함수 버전 호출
5.6.3 순수 가상 함수(Pure Virtual Functions)와 추상 클래스(Abstract Classes)
순수 가상 함수는 구현이 없는 가상 함수로, 이를 포함하는 클래스는 추상 클래스가 됩니다:
#include <iostream>
#include <string>
#include <vector>
// 추상 기본 클래스
class Shape {
protected:
std::string name;
public:
Shape(const std::string& n) : name(n) {}
// 일반 가상 함수
virtual void display() const {
std::cout << "도형: " << name << std::endl;
}
// 순수 가상 함수 (구현 없음)
virtual double area() const = 0; // '= 0'으로 순수 가상 함수 지정
// 가상 소멸자
virtual ~Shape() {
std::cout << name << " 소멸" << std::endl;
}
};
// 구체 파생 클래스
class Circle : public Shape {
private:
double radius;
public:
Circle(const std::string& n, double r) : Shape(n), radius(r) {}
void display() const override {
std::cout << "원: " << name << ", 반지름: " << radius << std::endl;
}
// 순수 가상 함수 구현 필수
double area() const override {
return 3.14159 * radius * radius;
}
};
// 또 다른 구체 파생 클래스
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(const std::string& n, double w, double h)
: Shape(n), width(w), height(h) {}
void display() const override {
std::cout << "직사각형: " << name << ", 가로: " << width
<< ", 세로: " << height << std::endl;
}
// 순수 가상 함수 구현 필수
double area() const override {
return width * height;
}
};
int main() {
// Shape shape("일반 도형"); // 오류: 추상 클래스는 인스턴스화 불가
Circle circle("원A", 5.0);
Rectangle rect("직사각형B", 4.0, 6.0);
// 다형성 활용
Shape* shapes[2];
shapes[0] = &circle;
shapes[1] = ▭
double totalArea = 0.0;
for (int i = 0; i < 2; i++) {
shapes[i]->display();
double shapeArea = shapes[i]->area();
std::cout << "면적: " << shapeArea << std::endl;
totalArea += shapeArea;
}
std::cout << "총 면적: " << totalArea << std::endl;
return 0;
}
추상 클래스의 특징:
- 최소 하나 이상의 순수 가상 함수 포함
- 직접 인스턴스화 불가능
- 파생 클래스의 인터페이스 역할
- 파생 클래스는 모든 순수 가상 함수를 구현해야 함
5.6.4 가상 소멸자(Virtual Destructors)
다형성을 사용할 때 기본 클래스 포인터로 파생 클래스 객체를 삭제할 경우, 가상 소멸자가 필요합니다:
#include <iostream>
#include <string>
class Base {
public:
Base() {
std::cout << "Base 생성자" << std::endl;
}
// 비가상 소멸자
// ~Base() {
// std::cout << "Base 소멸자" << std::endl;
// }
// 가상 소멸자
virtual ~Base() {
std::cout << "Base 가상 소멸자" << std::endl;
}
};
class Derived : public Base {
private:
int* data;
public:
Derived() : Base() {
std::cout << "Derived 생성자" << std::endl;
data = new int(100); // 동적 메모리 할당
}
~Derived() {
std::cout << "Derived 소멸자" << std::endl;
delete data; // 동적 메모리 해제
}
};
int main() {
// 비가상 소멸자일 경우
std::cout << "비가상 소멸자 문제 시연:" << std::endl;
Base* ptr = new Derived();
delete ptr; // 가상 소멸자가 아니면 Derived의 소멸자 호출되지 않음 -> 메모리 누수
return 0;
}
가상 소멸자의 중요성:
- 기본 클래스 포인터로 파생 클래스 객체를 삭제할 때 적절한 소멸자 체인 보장
- 메모리 누수 방지
- 다형성을 사용하는 모든 기본 클래스에 가상 소멸자 선언 권장