1. 스마트 포인터를 통한 안전한 메모리 관리
전통적으로 C++에서는 new와 delete를 사용하여 동적 메모리를 관리했다.
하지만 이는 메모리 누수와 댕글링 포인트 문제를 유발할 수 있다.
모던 C++에서는 스마트 포인터(std::shared_ptr)를 사용하여
RAII 원칙을 적용해 이러한 문제를 해결하였다.
전통 방식
class Resource {
int* data;
public:
Resource() {
data = new int[100]; // 동적 메모리 할당
std::cout << "Resource acquired\n";
}
~Resource() {
delete[] data; // 명시적 해제 필요
std::cout << "Resource released\n";
}
};
- 문제점
- delete를 잊는 경우에는 메모리 누수가 발생한다.
- 예외 발생시 해제가 누락되어 리소스가 해제되지 않을 수 있다.
모던 방식
#include <memory>
#include <iostream>
class Resource {
std::unique_ptr<int[]> data; // 스마트 포인터 사용
public:
Resource() : data(std::make_unique<int[]>(100)) {
std::cout << "Resource acquired\n";
}
~Resource() {
std::cout << "Resource released\n";
}
};
int main() {
{
std::unique_ptr<Resource> resource = std::make_unique<Resource>();
// 리소스 자동 관리
}
// Resource가 범위를 벗어나면 자동으로 소멸자 호출
return 0;
}
- 장점:
- RAII 원칙으로 설계하여 객체가 소멸될 때 메모리에서 자동 해제된다.
- 메모리 누수 방지 및 예외 상황에서도 안전성이 올라간다.
2. 불변 객체
데이터의 불변성을 유지하면 버그가 발생하여 디버깅시 해당 변수가 수정되지 않음을 확인하고
빠르게 문제부분을 찾아 나갈 수 있다.
전통 방식
class Config {
int value; // 쉽게 변경되지 말아야 할 변수
public:
void setValue(int v) { value = v; }
int getValue() const { return value; }
};
- 문제점
- setValue() 함수 호출로 데이터가 쉽게 변경될 수 있다.
- value는 불변성을 보장하기 어렵다.
모던 방식
class Config {
const int value; // 불변 데이터
public:
Config(int v) : value(v) {} // 생성 시 초기화
int getValue() const { return value; } // 데이터 읽기만 가능
};
int main() {
Config config(42);
std::cout << "Config value: " << config.getValue() << "\n";
// config.value = 10; // 오류: value는 const
return 0;
}
- 장점
- 불변 데이터를 사용하여 버그를 방지
- 여러 쓰레드에서 동시에 접근해도 안전하다.
3. 컴포지션 기반 설계
전통적으로 객체지향 설계는 상속(Inheritance)에 의존하는 경우가 많다.
하지만 상속은 복잡성 증가와 코드 결합 문제를 유발한다.
모던 C++에서는 "has-a" 관계를 표현하는 컴포지션을 선호한다.
이를 통해 객체 간의 결합도를 낮추고 재사용성을 높인다.
전통 방식
class Vehicle {
public:
virtual void drive() = 0;
};
class Car : public Vehicle {
public:
void drive() override {
std::cout << "Car is driving\n";
}
};
모던 방식
#include <iostream>
class Engine {
public:
void start() {
std::cout << "Engine started\n";
}
};
class Car {
Engine engine; // "has-a" 관계
public:
void drive() {
engine.start();
std::cout << "Car is driving\n";
}
};
int main() {
Car car;
car.drive();
return 0;
}
- 장점
- has-a 관계로 낮은 결합도를 유지한다.
- 컴포넌트를 재사용하여 다양한 객체 구성이 가능하다.
'Design Pattern' 카테고리의 다른 글
| [Design Pattern] 전략 패턴 (0) | 2025.04.02 |
|---|---|
| [Design Pattern] 디자인 패턴 (0) | 2025.04.01 |
| [DesignPattern] 모던 객체지향 설계 - 추가 원칙 (0) | 2025.03.31 |
| [DesignPattern] 모던 객체지향 설계 - 객체지향의 기본 원칙 (0) | 2025.03.31 |
| [DesignPattern] 디자인 패턴 (1) | 2025.01.08 |