1. C++ OOP (객체 지향 프로그래밍)
이번 장에서는 C++의 핵심인 객체 지향 프로그래밍(Object-Oriented Programming, OOP) 에 대해 알아보겠습니다.
C++의 가장 큰 특징은 객체 지향 프로그래밍(OOP) 과 높은 성능(Performance) 을 동시에 추구하는 언어라는 점입니다.
따라서 C++에서는 단순히 객체 지향 개념만 이해하는 것이 아니라, 객체가 메모리에 어떻게 배치되고 관리되는지까지 함께 이해하는 것이 중요합니다.
OOP Part에서 학습할 내용
1.1 객체와 메모리 구조
객체가 메모리에 어떻게 생성되고 배치되는지 살펴보며, 객체 지향 개념을 메모리 관점에서 이해해 보겠습니다.
1.2 static의 의미
객체 지향 프로그래밍에서 자주 사용하는 static 키워드가 정적(Static) 메모리 영역과 어떤 관계가 있는지 알아보겠습니다.
메모리 구조와 함께 이해하면 static의 동작 원리를 보다 명확하게 이해할 수 있습니다.
1.3 생성자 (Constructor)와 대입 연산 (Assignment)
객체를 생성하고 복사하거나 이동할 때 사용되는 다음 개념들을 학습합니다.
- Constructor
- Copy Constructor
- Move Constructor
- Copy Assignment
- Move Assignment
이들은 이전 장에서 학습한 L-value, R-value, 그리고 std::move 개념을 기반으로 동작합니다.
1.4 Rule of Three와 Rule of Five
생성자와 복사 및 이동 연산을 이해한 후에는 C++ 객체 설계에서 중요한 규칙인 다음 내용을 살펴봅니다.
- Rule of Three
- Rule of Five
이 규칙들은 객체가 자원을 안전하고 효율적으로 관리하기 위해 반드시 알아두어야 하는 핵심 개념입니다.
1.5 객체 지향과 성능
객체 지향 프로그래밍은 코드의 구조를 개선하고 유지보수를 쉽게 해 주지만, 과도하게 사용하면 C++의 장점인 성능을 충분히 활용하지 못할 수도 있습니다.
따라서 이번 장에서는 객체 지향 설계와 성능 사이의 균형을 어떻게 맞추는지도 함께 살펴보겠습니다.
정리
이번 장에서는 다음 내용을 중심으로 학습합니다.
- 객체와 메모리 구조
- static과 정적 메모리
- 생성자(Constructor)
- Copy/Move Constructor
- Copy/Move Assignment
- Rule of Three
- Rule of Five
- 객체 지향 프로그래밍과 성능의 관계
이 내용들은 C++의 객체 지향 프로그래밍을 이해하는 데 가장 중요한 핵심 개념이며, 이후의 클래스 설계와 자원 관리 기법을 배우기 위한 기반이 됩니다.
2. Four pillars of OOP: 객체 지향 프로그래밍의 핵심 4원칙

위 이미지는 OOP의 네 가지 핵심 개념을 시각화한 것입니다:
각 개념 설명
캡슐화 (Encapsulation)
- 데이터를 객체 내부에 숨기고, 필요한 메서드만 외부에 공개합니다. 보안 강화와 의도하지 않은 변경 방지.
상속 (Inheritance)
- 부모 클래스의 특성과 메서드를 자식 클래스가 물려받습니다. 코드 재사용성 증대.
다형성 (Polymorphism)
- 같은 메서드 이름이지만 객체마다 다른 방식으로 동작합니다. Truck과 Car가 모두 brake()를 가지지만 다르게 작동.
추상화 (Abstraction)
- 복잡한 내부 동작을 숨기고 필요한 인터페이스만 제공합니다. 사용자는 drive(car)라고 부르기만 하면, 내부가 어떻게 작동하는지는 신경 쓸 필요 없음.
2.1 캡슐화 (Encapsulation in OOP)
캡슐화(Encapsulation)는 객체의 내부 데이터를 보호하고, 제어된 방식으로만 접근하도록 하는 OOP의 핵심 원칙입니다.
핵심 아이디어
// ❌ 캡슐화 없음 (위험)
class BankAccount {
public:
double balance; // 누구나 직접 변경 가능
};
BankAccount acc;
acc.balance = 1000;
acc.balance = -999; // 음수 잔액? 문제!
acc.balance = 0; // 누가 무단으로 0으로 만들 수도 있음
// ✓ 캡슐화 (안전)
class BankAccount {
private:
double balance; // 직접 접근 불가
public:
void deposit(double amount) {
if (amount > 0) {
balance += amount; // 검증 후 변경
}
}
bool withdraw(double amount) {
if (amount > 0 && balance >= amount) {
balance -= amount;
return true;
}
return false;
}
double getBalance() const {
return balance; // 읽기만 가능
}
};
예제 1: 사람 클래스
class Person {
private:
std::string name;
int age;
std::string email;
public:
// 나이 설정 - 검증 로직 포함
void setAge(int newAge) {
if (newAge >= 0 && newAge <= 150) {
age = newAge; // 유효한 범위만 허용
} else {
std::cerr << "Invalid age!\n";
}
}
int getAge() const {
return age;
}
// 이메일 설정 - 형식 검증
void setEmail(const std::string& newEmail) {
if (isValidEmail(newEmail)) {
email = newEmail;
} else {
std::cerr << "Invalid email format!\n";
}
}
std::string getEmail() const {
return email;
}
void setName(const std::string& newName) {
if (!newName.empty()) {
name = newName;
}
}
std::string getName() const {
return name;
}
private:
// 헬퍼 함수
bool isValidEmail(const std::string& email) {
// 이메일 형식 검증 로직
return email.find('@') != std::string::npos;
}
};
// 사용 예
Person person;
person.setAge(25); // ✓ OK
person.setAge(-5); // ✗ 거부됨
person.setEmail("john@example.com"); // ✓ OK
person.setEmail("invalid"); // ✗ 거부됨
std::cout << person.getAge() << "\n";
예제 2: 은행 계좌 (심화)
class BankAccount {
private:
std::string accountNumber;
double balance;
std::string accountHolder;
std::vector<Transaction> transactionHistory;
// 거래 기록하기
void recordTransaction(const std::string& type, double amount) {
transactionHistory.push_back(Transaction(type, amount));
}
public:
// 생성자
BankAccount(const std::string& holder, const std::string& accNum, double initialBalance)
: accountHolder(holder), accountNumber(accNum), balance(initialBalance) {
recordTransaction("open", initialBalance);
}
// 입금
bool deposit(double amount) {
if (amount <= 0) {
std::cerr << "Deposit amount must be positive\n";
return false;
}
balance += amount;
recordTransaction("deposit", amount);
std::cout << "Deposited: " << amount << "\n";
return true;
}
// 출금
bool withdraw(double amount) {
if (amount <= 0) {
std::cerr << "Withdrawal amount must be positive\n";
return false;
}
if (balance < amount) {
std::cerr << "Insufficient funds\n";
return false;
}
balance -= amount;
recordTransaction("withdraw", amount);
std::cout << "Withdrawn: " << amount << "\n";
return true;
}
// 잔액 조회 (읽기만 가능)
double getBalance() const {
return balance;
}
// 거래 내역 조회
void printTransactionHistory() const {
std::cout << "Transaction history for " << accountHolder << ":\n";
for (const auto& t : transactionHistory) {
std::cout << t.type << ": " << t.amount << "\n";
}
}
// 계좌 정보 (읽기만)
std::string getAccountNumber() const {
return accountNumber;
}
private:
struct Transaction {
std::string type;
double amount;
Transaction(const std::string& t, double a) : type(t), amount(a) {}
};
};
// 사용
int main() {
BankAccount acc("John Doe", "1234567890", 1000);
acc.deposit(500); // ✓ OK
acc.withdraw(200); // ✓ OK
acc.withdraw(2000); // ✗ 거부: 잔액 부족
std::cout << "Balance: " << acc.getBalance() << "\n";
acc.printTransactionHistory();
// acc.balance = -999; // ✗ 컴파일 에러: private 접근 불가
return 0;
}
예제 3: 학생 점수 관리
class StudentGrades {
private:
std::string studentId;
std::map<std::string, double> grades; // 과목 -> 점수
double calculatedGPA;
// 내부 GPA 계산
void recalculateGPA() {
if (grades.empty()) {
calculatedGPA = 0;
return;
}
double sum = 0;
for (const auto& [subject, grade] : grades) {
sum += grade;
}
calculatedGPA = sum / grades.size();
}
public:
StudentGrades(const std::string& id) : studentId(id), calculatedGPA(0.0) {}
// 점수 추가 - 유효성 검사
void addGrade(const std::string& subject, double grade) {
if (grade < 0 || grade > 4.0) {
std::cerr << "Grade must be between 0 and 4.0\n";
return;
}
grades[subject] = grade;
recalculateGPA(); // 점수 변경 시 자동 재계산
std::cout << "Grade added: " << subject << " = " << grade << "\n";
}
// 점수 조회
double getGrade(const std::string& subject) const {
auto it = grades.find(subject);
if (it != grades.end()) {
return it->second;
}
std::cerr << "Subject not found\n";
return -1;
}
// GPA 조회 (읽기만)
double getGPA() const {
return calculatedGPA;
}
// 모든 과목 조회
void printAllGrades() const {
std::cout << "Grades for student " << studentId << ":\n";
for (const auto& [subject, grade] : grades) {
std::cout << " " << subject << ": " << grade << "\n";
}
std::cout << "GPA: " << calculatedGPA << "\n";
}
};
// 사용
int main() {
StudentGrades student("S12345");
student.addGrade("Math", 3.8);
student.addGrade("Physics", 3.9);
student.addGrade("English", 3.5);
student.addGrade("Biology", 5.0); // ✗ 거부: 4.0 초과
std::cout << "GPA: " << student.getGPA() << "\n";
student.printAllGrades();
// student.grades["Math"] = -1; // ✗ 컴파일 에러: 직접 변경 불가
return 0;
}
예제 4: 자동차 클래스
class Car {
private:
std::string make;
std::string model;
int year;
double mileage; // 마일리지는 증가만 가능
double fuelLevel; // 0 ~ 100
bool isRunning;
// 헬퍼 함수
bool validateYear(int y) const {
int currentYear = 2026;
return y >= 1886 && y <= currentYear;
}
public:
Car(const std::string& m, const std::string& mo, int y, double initial_fuel)
: make(m), model(mo), year(y), mileage(0), fuelLevel(initial_fuel), isRunning(false) {
if (!validateYear(y)) {
std::cerr << "Invalid year\n";
}
}
// 시동 켜기
void start() {
if (fuelLevel > 0) {
isRunning = true;
std::cout << make << " " << model << " started\n";
} else {
std::cerr << "No fuel!\n";
}
}
// 시동 끄기
void stop() {
isRunning = false;
std::cout << "Engine stopped\n";
}
// 주행 (마일리지 증가)
void drive(double distance) {
if (!isRunning) {
std::cerr << "Start the car first\n";
return;
}
double fuelNeeded = distance / 10; // 10km당 1리터
if (fuelLevel < fuelNeeded) {
std::cerr << "Not enough fuel\n";
return;
}
mileage += distance;
fuelLevel -= fuelNeeded;
std::cout << "Drove " << distance << "km\n";
}
// 연료 충전
void refuel(double amount) {
if (amount < 0 || fuelLevel + amount > 100) {
std::cerr << "Invalid refuel amount\n";
return;
}
fuelLevel += amount;
std::cout << "Refueled " << amount << " liters\n";
}
// 정보 조회
void printInfo() const {
std::cout << year << " " << make << " " << model << "\n";
std::cout << "Mileage: " << mileage << "km\n";
std::cout << "Fuel: " << fuelLevel << "%\n";
std::cout << "Status: " << (isRunning ? "Running" : "Stopped") << "\n";
}
// Getter들
double getMileage() const { return mileage; }
double getFuelLevel() const { return fuelLevel; }
};
// 사용
int main() {
Car myCar("Toyota", "Camry", 2020, 50);
myCar.start();
myCar.drive(100);
myCar.stop();
myCar.refuel(30);
myCar.printInfo();
// myCar.mileage = 0; // ✗ 컴파일 에러: private 접근 불가
// myCar.fuelLevel = 1000; // ✗ 컴파일 에러
return 0;
}
캡슐화의 접근 제어
class Example {
public:
// 어디서나 접근 가능
int publicVar;
protected:
// 같은 클래스 + 파생 클래스에서 접근 가능
int protectedVar;
private:
// 같은 클래스 내에서만 접근 가능
int privateVar;
};
캡슐화 vs 추상화 (명확한 정의)
캡슐화 (Encapsulation):
→ private/public으로 데이터 **보호**
→ "데이터를 어떻게 보관할 것인가"
→ 접근 제어, 불변성 보장
추상화 (Abstraction):
→ 복잡한 구현을 **숨기기**
→ "어떤 인터페이스를 제공할 것인가"
→ 세부사항 감추기, 간단한 인터페이스
캡슐화의 핵심 원칙 (PIMPL)
// ✓ 좋은 관례: Getter/Setter
class Rectangle {
private:
double width;
double height;
public:
void setWidth(double w) {
if (w > 0) width = w;
}
double getWidth() const {
return width;
}
double getArea() const {
return width * height;
}
};
캡슐화의 장점
| 장점 | 설명 |
| 데이터 보호 | 잘못된 값이 할당되는 것을 방지 |
| 검증 | Setter에서 입력값 검증 가능 |
| 유지보수 | 내부 구현 변경 시 인터페이스만 유지하면 됨 |
| 일관성 | 객체 상태를 항상 유효하게 유지 |
| 복잡도 감소 | 사용자는 단순한 인터페이스만 사용 |
* 결론
캡슐화의 핵심: "데이터는 보호하되, 제어된 인터페이스를 통해 접근을 허용한다."
이를 통해 객체의 무결성을 보장하고, 예측 불가능한 버그를 방지합니다.
2.2 상속 (Inheritance in OOP)
상속(Inheritance)은 기존 클래스의 특성과 메서드를 새로운 클래스가 물려받는 OOP의 핵심 메커니즘입니다. 코드 재사용성을 극대화하고 계층적 관계를 표현합니다.
핵심 아이디어
// 기본 클래스 (부모 클래스)
class Animal {
public:
void eat() {
std::cout << "Animal is eating\n";
}
};
// 파생 클래스 (자식 클래스)
class Dog : public Animal { // Animal을 상속받음
public:
void bark() {
std::cout << "Woof! Woof!\n";
}
};
// 사용
Dog myDog;
myDog.eat(); // ✓ 상속받은 메서드
myDog.bark(); // ✓ 자신의 메서드
예제 1: 동물 분류 체계
#include <iostream>
#include <string>
// 기본 클래스 (부모)
class Animal {
protected:
std::string name;
int age;
public:
Animal(const std::string& n, int a) : name(n), age(a) {
std::cout << "Animal created: " << name << "\n";
}
virtual void speak() { // virtual: 자식 클래스에서 오버라이드 가능
std::cout << name << " makes a sound\n";
}
virtual void move() {
std::cout << name << " moves\n";
}
void sleep() {
std::cout << name << " is sleeping\n";
}
virtual ~Animal() {
std::cout << "Animal destroyed\n";
}
};
// 파생 클래스 1
class Dog : public Animal {
private:
std::string breed;
public:
Dog(const std::string& n, int a, const std::string& b)
: Animal(n, a), breed(b) {
std::cout << "Dog created\n";
}
// speak() 메서드 오버라이드
void speak() override {
std::cout << name << " barks: Woof! Woof!\n";
}
void move() override {
std::cout << name << " runs on four legs\n";
}
void fetch() {
std::cout << name << " fetches the ball\n";
}
};
// 파생 클래스 2
class Bird : public Animal {
private:
double wingSpan;
public:
Bird(const std::string& n, int a, double ws)
: Animal(n, a), wingSpan(ws) {
std::cout << "Bird created\n";
}
void speak() override {
std::cout << name << " chirps: Tweet! Tweet!\n";
}
void move() override {
std::cout << name << " flies in the sky\n";
}
void buildNest() {
std::cout << name << " builds a nest\n";
}
};
// 파생 클래스 3
class Fish : public Animal {
private:
double waterDepth;
public:
Fish(const std::string& n, int a, double d)
: Animal(n, a), waterDepth(d) {
std::cout << "Fish created\n";
}
void speak() override {
std::cout << name << " makes bubbles: Blub blub\n";
}
void move() override {
std::cout << name << " swims\n";
}
};
// 사용
int main() {
Dog dog("Max", 3, "Golden Retriever");
dog.speak();
dog.move();
dog.sleep(); // 상속받은 메서드
dog.fetch();
std::cout << "\n";
Bird bird("Tweety", 2, 0.5);
bird.speak();
bird.move();
bird.sleep();
bird.buildNest();
std::cout << "\n";
Fish fish("Nemo", 1, 50);
fish.speak();
fish.move();
fish.sleep();
return 0;
}
/* 출력:
Animal created: Max
Dog created
Max barks: Woof! Woof!
Max runs on four legs
Max is sleeping
Max fetches the ball
Animal created: Tweety
Bird created
Tweety chirps: Tweet! Tweet!
Tweety flies in the sky
Tweety is sleeping
Tweety builds a nest
Animal created: Nemo
Fish created
Nemo makes bubbles: Blub blub
Nemo swims
Nemo is sleeping
Fish destroyed
Bird destroyed
Dog destroyed
Animal destroyed
*/
예제 2: 직원 관리 시스템
#include <iostream>
#include <string>
// 기본 클래스
class Employee {
protected:
std::string name;
std::string employeeId;
double salary;
public:
Employee(const std::string& n, const std::string& id, double sal)
: name(n), employeeId(id), salary(sal) {}
virtual void displayInfo() {
std::cout << "Name: " << name << "\n";
std::cout << "ID: " << employeeId << "\n";
std::cout << "Salary: $" << salary << "\n";
}
virtual double calculateBonus() {
return salary * 0.1; // 기본 10% 보너스
}
virtual ~Employee() {}
};
// 파생 클래스 1: 개발자
class Developer : public Employee {
private:
std::string programmingLanguage;
public:
Developer(const std::string& n, const std::string& id,
double sal, const std::string& lang)
: Employee(n, id, sal), programmingLanguage(lang) {}
void displayInfo() override {
Employee::displayInfo(); // 부모 메서드 호출
std::cout << "Position: Developer\n";
std::cout << "Language: " << programmingLanguage << "\n";
}
double calculateBonus() override {
return salary * 0.15; // 개발자는 15% 보너스
}
void writeCode() {
std::cout << name << " is writing " << programmingLanguage
<< " code\n";
}
};
// 파생 클래스 2: 관리자
class Manager : public Employee {
private:
int teamSize;
public:
Manager(const std::string& n, const std::string& id,
double sal, int size)
: Employee(n, id, sal), teamSize(size) {}
void displayInfo() override {
Employee::displayInfo();
std::cout << "Position: Manager\n";
std::cout << "Team Size: " << teamSize << "\n";
}
double calculateBonus() override {
return salary * 0.2; // 관리자는 20% 보너스
}
void conductMeeting() {
std::cout << name << " is conducting a team meeting\n";
}
};
// 파생 클래스 3: 인턴
class Intern : public Employee {
private:
std::string university;
public:
Intern(const std::string& n, const std::string& id,
double sal, const std::string& uni)
: Employee(n, id, sal), university(uni) {}
void displayInfo() override {
Employee::displayInfo();
std::cout << "Position: Intern\n";
std::cout << "University: " << university << "\n";
}
double calculateBonus() override {
return salary * 0.05; // 인턴은 5% 보너스
}
void learnSkills() {
std::cout << name << " is learning new skills\n";
}
};
// 사용
int main() {
Developer dev("Alice", "DEV001", 100000, "C++");
Manager mgr("Bob", "MGR001", 120000, 5);
Intern intern("Charlie", "INT001", 30000, "MIT");
// 정보 출력
dev.displayInfo();
std::cout << "Bonus: $" << dev.calculateBonus() << "\n\n";
mgr.displayInfo();
std::cout << "Bonus: $" << mgr.calculateBonus() << "\n\n";
intern.displayInfo();
std::cout << "Bonus: $" << intern.calculateBonus() << "\n\n";
// 각자의 메서드
dev.writeCode();
mgr.conductMeeting();
intern.learnSkills();
return 0;
}
예제 3: 도형 클래스 계층
#include <iostream>
#include <cmath>
// 기본 클래스
class Shape {
protected:
std::string name;
public:
Shape(const std::string& n) : name(n) {}
virtual double calculateArea() = 0; // 순수 가상 함수
virtual double calculatePerimeter() = 0;
virtual void display() {
std::cout << "Shape: " << name << "\n";
std::cout << "Area: " << calculateArea() << "\n";
std::cout << "Perimeter: " << calculatePerimeter() << "\n";
}
virtual ~Shape() {}
};
// 원
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : Shape("Circle"), radius(r) {}
double calculateArea() override {
return M_PI * radius * radius;
}
double calculatePerimeter() override {
return 2 * M_PI * radius;
}
};
// 직사각형
class Rectangle : public Shape {
private:
double width;
double height;
public:
Rectangle(double w, double h)
: Shape("Rectangle"), width(w), height(h) {}
double calculateArea() override {
return width * height;
}
double calculatePerimeter() override {
return 2 * (width + height);
}
};
// 삼각형
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 calculateArea() override {
// 헤론의 공식
double s = (a + b + c) / 2;
return std::sqrt(s * (s - a) * (s - b) * (s - c));
}
double calculatePerimeter() override {
return a + b + c;
}
};
// 사용
int main() {
Circle circle(5);
Rectangle rect(4, 6);
Triangle tri(3, 4, 5);
circle.display();
std::cout << "\n";
rect.display();
std::cout << "\n";
tri.display();
return 0;
}
상속의 종류
(1) 단일 상속 (Single Inheritance)
class Parent {};
class Child : public Parent {}; // 한 개의 부모만
(2) 다중 상속 (Multiple Inheritance)
class Parent1 {};
class Parent2 {};
class Child : public Parent1, public Parent2 {}; // 여러 부모
// ⚠️ 주의: 다이아몬드 문제 발생 가능
class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {}; // A를 두 번 상속받음!
(3) 다단계 상속 (Hierarchical Inheritance)
class Animal {};
class Mammal : public Animal {};
class Dog : public Mammal {}; // 계층 구조
접근 제어와 상속
class Parent {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
class Child1 : public Parent {
// public 상속: 접근 권한 유지
// public → public
// protected → protected
// private → 접근 불가
};
class Child2 : protected Parent {
// protected 상속: 모두 protected로 변경
// public → protected
// protected → protected
};
class Child3 : private Parent {
// private 상속: 모두 private로 변경
// public → private
// protected → private
};
메서드 오버라이딩
class Vehicle {
public:
virtual void start() {
std::cout << "Vehicle started\n";
}
};
class Car : public Vehicle {
public:
void start() override { // override 키워드 권장
std::cout << "Car engine started\n";
}
};
class ElectricCar : public Vehicle {
public:
void start() override {
std::cout << "Electric motor activated\n";
}
};
// 다형성: 런타임에 실제 객체 타입에 따라 메서드 호출
int main() {
Vehicle* vehicle;
Car car;
vehicle = &car;
vehicle->start(); // "Car engine started" 출력
ElectricCar eCar;
vehicle = &eCar;
vehicle->start(); // "Electric motor activated" 출력
}
가상 함수 (Virtual Functions)
class Base {
public:
virtual void display() { // 가상 함수
std::cout << "Base display\n";
}
};
class Derived : public Base {
public:
void display() override { // 오버라이드
std::cout << "Derived display\n";
}
};
int main() {
Base* ptr;
Derived d;
ptr = &d;
ptr->display(); // "Derived display" 출력 (가상 함수이므로)
// 가상 함수가 없으면 "Base display" 출력됨
}
순수 가상 함수 (Pure Virtual Functions)
// 추상 클래스: 직접 인스턴스화 불가
class Shape {
public:
virtual void draw() = 0; // 순수 가상 함수
virtual ~Shape() {}
};
class Circle : public Shape {
public:
void draw() override {
std::cout << "Drawing circle\n";
}
};
int main() {
// Shape s; // ✗ 컴파일 에러: 추상 클래스
Circle c;
c.draw(); // ✓ OK
}
생성자와 소멸자
class Parent {
public:
Parent() {
std::cout << "Parent constructor\n";
}
virtual ~Parent() {
std::cout << "Parent destructor\n";
}
};
class Child : public Parent {
public:
Child() : Parent() { // 부모 생성자 명시적 호출
std::cout << "Child constructor\n";
}
virtual ~Child() {
std::cout << "Child destructor\n";
}
};
int main() {
Child c;
// 출력:
// Parent constructor
// Child constructor
// Child destructor
// Parent destructor
}
상속의 장점
| 장점 | 설명 |
| 코드 재사용 | 공통 기능을 한 번만 작성 |
| 계층 구조 | 현실 세계의 분류를 표현 |
| 유지보수 | 부모 클래스 수정으로 모든 자식에 반영 |
| 확장성 | 새로운 클래스 쉽게 추가 가능 |
| 다형성 | 다양한 객체를 동일한 인터페이스로 처리 |
상속 vs 컴포지션
// ❌ 상속 오용
class Animal {};
class Engine {};
class Car : public Animal, public Engine {}; // 말이 안 됨
// ✓ 컴포지션 (더 나은 방법)
class Car {
private:
Engine engine; // 엔진을 포함
};
결론
상속의 핵심: "공통의 특성을 부모 클래스에서 정의하고, 각 자식 클래스에서 고유한 행동을 구현한다."
상속은 강력한 도구지만, 과도하게 사용하면 복잡도가 증가합니다. 상속이 필수일 때만 사용하고, 그 외에는 컴포지션을 고려하세요.
2.3 다형성 (Polimophisim in OOP)
다형성(Polymorphism)은 "많은 형태를 가진다"는 뜻으로, 같은 인터페이스가 다른 방식으로 동작할 수 있게 하는 OOP의 강력한 개념입니다.
핵심 아이디어
// 같은 메서드 이름, 다른 동작
class Dog {
public:
void speak() { std::cout << "Woof!\n"; }
};
class Cat {
public:
void speak() { std::cout << "Meow!\n"; }
};
// 문제: 다양한 동물을 하나로 처리할 수 없음
Dog dog;
Cat cat;
dog.speak(); // "Woof!"
cat.speak(); // "Meow!"
다형성의 종류
다형성은 두 가지로 나뉩니다:
- 컴파일 타임 다형성 (정적): 함수 오버로딩, 연산자 오버로딩, 템플릿
- 런타임 다형성 (동적): 가상 함수 오버라이딩
(1) 컴파일 타임 다형성 (Static Polymorphism)
예제 1: 함수 오버로딩 (Function Overloading)
#include <iostream>
#include <string>
class Calculator {
public:
// 정수 덧셈
int add(int a, int b) {
std::cout << "Adding integers\n";
return a + b;
}
// 실수 덧셈
double add(double a, double b) {
std::cout << "Adding doubles\n";
return a + b;
}
// 문자열 연결
std::string add(const std::string& a, const std::string& b) {
std::cout << "Concatenating strings\n";
return a + b;
}
// 세 개의 정수 덧셈
int add(int a, int b, int c) {
std::cout << "Adding three integers\n";
return a + b + c;
}
};
int main() {
Calculator calc;
std::cout << "Result: " << calc.add(5, 10) << "\n"; // 정수
// Adding integers
// Result: 15
std::cout << "Result: " << calc.add(5.5, 10.3) << "\n"; // 실수
// Adding doubles
// Result: 15.8
std::cout << "Result: " << calc.add("Hello ", "World") << "\n"; // 문자열
// Concatenating strings
// Result: Hello World
std::cout << "Result: " << calc.add(1, 2, 3) << "\n"; // 세 개의 정수
// Adding three integers
// Result: 6
return 0;
}
예제 2: 연산자 오버로딩 (Operator Overloading)
#include <iostream>
#include <cmath>
class Vector {
private:
double x, y;
public:
Vector(double x = 0, double y = 0) : x(x), y(y) {}
// + 연산자 오버로딩
Vector operator+(const Vector& other) const {
return Vector(x + other.x, y + other.y);
}
// - 연산자 오버로딩
Vector operator-(const Vector& other) const {
return Vector(x - other.x, y - other.y);
}
// * 연산자 오버로딩 (스칼라 곱)
Vector operator*(double scalar) const {
return Vector(x * scalar, y * scalar);
}
// == 연산자 오버로딩
bool operator==(const Vector& other) const {
return x == other.x && y == other.y;
}
// [] 연산자 오버로딩
double operator[](int index) const {
return (index == 0) ? x : y;
}
// () 연산자 오버로딩
double operator()() const {
return std::sqrt(x * x + y * y); // 크기 계산
}
void display() const {
std::cout << "(" << x << ", " << y << ")";
}
};
int main() {
Vector v1(3, 4);
Vector v2(1, 2);
std::cout << "v1 = ";
v1.display();
std::cout << "\n";
std::cout << "v2 = ";
v2.display();
std::cout << "\n";
// + 연산자
Vector v3 = v1 + v2;
std::cout << "v1 + v2 = ";
v3.display();
std::cout << "\n";
// - 연산자
Vector v4 = v1 - v2;
std::cout << "v1 - v2 = ";
v4.display();
std::cout << "\n";
// * 연산자
Vector v5 = v1 * 2;
std::cout << "v1 * 2 = ";
v5.display();
std::cout << "\n";
// == 연산자
std::cout << "v1 == v2? " << (v1 == v2 ? "true" : "false") << "\n";
// [] 연산자
std::cout << "v1[0] = " << v1[0] << ", v1[1] = " << v1[1] << "\n";
// () 연산자 (크기)
std::cout << "Magnitude of v1: " << v1() << "\n";
return 0;
}
/* 출력:
v1 = (3, 4)
v2 = (1, 2)
v1 + v2 = (4, 6)
v1 - v2 = (2, 2)
v1 * 2 = (6, 8)
v1 == v2? false
v1[0] = 3, v1[1] = 4
Magnitude of v1: 5
*/
예제 3: 템플릿 (Templates)
#include <iostream>
#include <string>
// 템플릿 함수
template <typename T>
T findMax(T a, T b) {
return (a > b) ? a : b;
}
// 템플릿 클래스
template <typename T>
class Box {
private:
T value;
public:
Box(T val) : value(val) {}
T getValue() const {
return value;
}
void display() const {
std::cout << "Value: " << value << "\n";
}
};
int main() {
// 정수 비교
std::cout << "Max of 5 and 10: " << findMax(5, 10) << "\n";
// 실수 비교
std::cout << "Max of 3.5 and 2.8: " << findMax(3.5, 2.8) << "\n";
// 문자열 비교
std::string str1 = "apple";
std::string str2 = "zebra";
std::cout << "Max of \"apple\" and \"zebra\": "
<< findMax(str1, str2) << "\n";
// 템플릿 클래스
Box<int> intBox(42);
intBox.display(); // Value: 42
Box<double> doubleBox(3.14);
doubleBox.display(); // Value: 3.14
Box<std::string> stringBox("Hello");
stringBox.display(); // Value: Hello
return 0;
}
(2) 런타임 다형성 (Dynamic Polymorphism)
예제 1: 기본 가상 함수
#include <iostream>
#include <string>
// 기본 클래스
class Shape {
protected:
std::string name;
public:
Shape(const std::string& n) : name(n) {}
// 가상 함수
virtual void draw() {
std::cout << "Drawing a generic shape\n";
}
virtual void describe() {
std::cout << "This is a " << name << "\n";
}
virtual ~Shape() {}
};
// 파생 클래스들
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : Shape("Circle"), radius(r) {}
void draw() override {
std::cout << "Drawing a circle with radius " << radius << "\n";
}
void describe() override {
Shape::describe();
std::cout << "Radius: " << radius << "\n";
}
};
class Rectangle : public Shape {
private:
double width, height;
public:
Rectangle(double w, double h) : Shape("Rectangle"), width(w), height(h) {}
void draw() override {
std::cout << "Drawing a rectangle " << width << "x" << height << "\n";
}
void describe() override {
Shape::describe();
std::cout << "Dimensions: " << width << " x " << height << "\n";
}
};
class Triangle : public Shape {
private:
double side;
public:
Triangle(double s) : Shape("Triangle"), side(s) {}
void draw() override {
std::cout << "Drawing an equilateral triangle with side " << side << "\n";
}
void describe() override {
Shape::describe();
std::cout << "Side: " << side << "\n";
}
};
// 다형성 활용
void renderShape(Shape* shape) {
shape->draw();
shape->describe();
std::cout << "\n";
}
int main() {
Circle circle(5);
Rectangle rect(4, 6);
Triangle tri(3);
// 포인터 배열로 다양한 객체 처리
Shape* shapes[] = { &circle, &rect, &tri };
for (Shape* shape : shapes) {
renderShape(shape);
}
return 0;
}
/* 출력:
Drawing a circle with radius 5
This is a Circle
Radius: 5
Drawing a rectangle 4x6
This is a Rectangle
Dimensions: 4 x 6
Drawing an equilateral triangle with side 3
This is a Triangle
Side: 3
*/
예제 2: 지불 처리 시스템
#include <iostream>
#include <string>
#include <vector>
// 기본 클래스
class PaymentMethod {
protected:
std::string accountNumber;
public:
PaymentMethod(const std::string& account)
: accountNumber(account) {}
virtual bool processPayment(double amount) = 0;
virtual void displayDetails() = 0;
virtual ~PaymentMethod() {}
};
// 신용카드 결제
class CreditCard : public PaymentMethod {
private:
std::string cardholderName;
int cvv;
public:
CreditCard(const std::string& account, const std::string& name, int cvv)
: PaymentMethod(account), cardholderName(name), cvv(cvv) {}
bool processPayment(double amount) override {
std::cout << "Processing credit card payment of $" << amount << "\n";
std::cout << "Card number: " << accountNumber << "\n";
std::cout << "Cardholder: " << cardholderName << "\n";
std::cout << "Payment successful!\n";
return true;
}
void displayDetails() override {
std::cout << "Payment Method: Credit Card\n";
std::cout << "Card: " << accountNumber << "\n";
std::cout << "Holder: " << cardholderName << "\n";
}
};
// 은행 송금
class BankTransfer : public PaymentMethod {
private:
std::string bankName;
public:
BankTransfer(const std::string& account, const std::string& bank)
: PaymentMethod(account), bankName(bank) {}
bool processPayment(double amount) override {
std::cout << "Processing bank transfer of $" << amount << "\n";
std::cout << "From account: " << accountNumber << "\n";
std::cout << "Bank: " << bankName << "\n";
std::cout << "Transfer initiated!\n";
return true;
}
void displayDetails() override {
std::cout << "Payment Method: Bank Transfer\n";
std::cout << "Account: " << accountNumber << "\n";
std::cout << "Bank: " << bankName << "\n";
}
};
// 디지털 지갑
class DigitalWallet : public PaymentMethod {
private:
double balance;
std::string walletProvider;
public:
DigitalWallet(const std::string& account, double bal, const std::string& provider)
: PaymentMethod(account), balance(bal), walletProvider(provider) {}
bool processPayment(double amount) override {
if (balance < amount) {
std::cout << "Insufficient balance in " << walletProvider << "\n";
return false;
}
balance -= amount;
std::cout << "Processing " << walletProvider << " payment of $" << amount << "\n";
std::cout << "Wallet ID: " << accountNumber << "\n";
std::cout << "Remaining balance: $" << balance << "\n";
std::cout << "Payment successful!\n";
return true;
}
void displayDetails() override {
std::cout << "Payment Method: Digital Wallet\n";
std::cout << "Provider: " << walletProvider << "\n";
std::cout << "Wallet ID: " << accountNumber << "\n";
std::cout << "Balance: $" << balance << "\n";
}
};
// 주문 처리
class Order {
private:
double totalAmount;
std::vector<std::string> items;
public:
Order(double amount) : totalAmount(amount) {}
void addItem(const std::string& item) {
items.push_back(item);
}
bool checkout(PaymentMethod* payment) {
std::cout << "=== Order Details ===\n";
for (const auto& item : items) {
std::cout << "- " << item << "\n";
}
std::cout << "Total: $" << totalAmount << "\n\n";
std::cout << "=== Payment Processing ===\n";
payment->displayDetails();
std::cout << "\n";
return payment->processPayment(totalAmount);
}
};
int main() {
Order order(250.00);
order.addItem("Laptop");
order.addItem("Mouse");
order.addItem("Keyboard");
std::cout << "=== Payment Method 1: Credit Card ===\n";
CreditCard creditCard("1234-5678-9012-3456", "John Doe", 123);
order.checkout(&creditCard);
std::cout << "\n\n";
std::cout << "=== Payment Method 2: Bank Transfer ===\n";
BankTransfer transfer("123456789", "Chase Bank");
order.checkout(&transfer);
std::cout << "\n\n";
std::cout << "=== Payment Method 3: Digital Wallet ===\n";
DigitalWallet wallet("alice@paypal.com", 300.00, "PayPal");
order.checkout(&wallet);
return 0;
}
예제 3: 동물 사운드 시스템
#include <iostream>
#include <vector>
#include <memory>
// 기본 클래스
class Animal {
protected:
std::string name;
public:
Animal(const std::string& n) : name(n) {}
virtual void makeSound() = 0;
virtual void move() = 0;
virtual std::string getType() = 0;
virtual ~Animal() {}
};
// 개
class Dog : public Animal {
public:
Dog(const std::string& n) : Animal(n) {}
void makeSound() override {
std::cout << name << " says: Woof! Woof!\n";
}
void move() override {
std::cout << name << " runs on four legs\n";
}
std::string getType() override {
return "Dog";
}
};
// 고양이
class Cat : public Animal {
public:
Cat(const std::string& n) : Animal(n) {}
void makeSound() override {
std::cout << name << " says: Meow! Meow!\n";
}
void move() override {
std::cout << name << " walks gracefully\n";
}
std::string getType() override {
return "Cat";
}
};
// 새
class Bird : public Animal {
public:
Bird(const std::string& n) : Animal(n) {}
void makeSound() override {
std::cout << name << " says: Tweet! Tweet!\n";
}
void move() override {
std::cout << name << " flies in the air\n";
}
std::string getType() override {
return "Bird";
}
};
// 양
class Sheep : public Animal {
public:
Sheep(const std::string& n) : Animal(n) {}
void makeSound() override {
std::cout << name << " says: Baa! Baa!\n";
}
void move() override {
std::cout << name << " walks on grass\n";
}
std::string getType() override {
return "Sheep";
}
};
// Zoo 클래스
class Zoo {
private:
std::vector<std::shared_ptr<Animal>> animals;
public:
void addAnimal(std::shared_ptr<Animal> animal) {
animals.push_back(animal);
}
void performShow() {
std::cout << "=== Zoo Show ===\n";
for (auto& animal : animals) {
animal->makeSound();
animal->move();
std::cout << "Type: " << animal->getType() << "\n\n";
}
}
void countByType() {
std::cout << "=== Animal Count ===\n";
int dogs = 0, cats = 0, birds = 0, sheep = 0;
for (auto& animal : animals) {
if (animal->getType() == "Dog") dogs++;
else if (animal->getType() == "Cat") cats++;
else if (animal->getType() == "Bird") birds++;
else if (animal->getType() == "Sheep") sheep++;
}
std::cout << "Dogs: " << dogs << "\n";
std::cout << "Cats: " << cats << "\n";
std::cout << "Birds: " << birds << "\n";
std::cout << "Sheep: " << sheep << "\n";
}
};
int main() {
Zoo zoo;
zoo.addAnimal(std::make_shared<Dog>("Buddy"));
zoo.addAnimal(std::make_shared<Cat>("Whiskers"));
zoo.addAnimal(std::make_shared<Bird>("Chirpy"));
zoo.addAnimal(std::make_shared<Dog>("Max"));
zoo.addAnimal(std::make_shared<Sheep>("Fluffy"));
zoo.performShow();
std::cout << "\n";
zoo.countByType();
return 0;
}
/* 출력:
=== Zoo Show ===
Buddy says: Woof! Woof!
Buddy runs on four legs
Type: Dog
Whiskers says: Meow! Meow!
Whiskers walks gracefully
Type: Cat
Chirpy says: Tweet! Tweet!
Chirpy flies in the air
Type: Bird
Max says: Woof! Woof!
Max runs on four legs
Type: Dog
Fluffy says: Baa! Baa!
Fluffy walks on grass
Type: Sheep
=== Animal Count ===
Dogs: 2
Cats: 1
Birds: 1
Sheep: 1
*/
다형성의 작동방식
// 포인터나 레퍼런스를 통한 다형성
class Base {
public:
virtual void method() { std::cout << "Base\n"; }
};
class Derived : public Base {
public:
void method() override { std::cout << "Derived\n"; }
};
int main() {
Derived d;
Base& ref = d; // 부모 타입 레퍼런스
ref.method(); // "Derived" 출력 (동적 바인딩)
Base* ptr = &d; // 부모 타입 포인터
ptr->method(); // "Derived" 출력 (동적 바인딩)
Derived direct = d; // 직접 복사
direct.method(); // "Derived" 출력
return 0;
}
정적 vs 동적 다형성 비교
| 특성 | 정적 다형성 | 동적 다형성 |
| 결정 시기 | 컴파일 타임 | 런타임 |
| 방식 | 함수 오버로딩, 템플릿 | 가상 함수 |
| 성능 | 더 빠름 | 약간의 오버헤드 (vTable) |
| 유연성 | 낮음 | 높음 |
| 복잡도 | 간단 | 복잡할 수 있음 |
추상 클래스 (Abstract Class)
class AbstractShape {
public:
// 순수 가상 함수
virtual void draw() = 0;
virtual double getArea() = 0;
// 구현된 가상 함수
virtual void describe() {
std::cout << "I am a shape\n";
}
virtual ~AbstractShape() {}
};
class Circle : public AbstractShape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
void draw() override {
std::cout << "Drawing circle\n";
}
double getArea() override {
return 3.14159 * radius * radius;
}
};
int main() {
// AbstractShape shape; // ✗ 컴파일 에러: 추상 클래스 직접 인스턴스화 불가
Circle circle(5); // ✓ OK: 모든 순수 가상 함수 구현
circle.draw();
return 0;
}
다형성의 장점
| 장 점 | 설 명 |
| 유연성 | 새로운 타입 추가 시 기존 코드 수정 불필요 |
| 코드 재사용 | 부모 타입으로 다양한 객체 처리 |
| 유지보수 | 공통 인터페이스로 일관된 처리 |
| 확장성 | 런타임에 동적으로 동작 선택 |
| 테스트 | Mock 객체를 통한 테스트 용이 |
다형성의 위험요소
// ⚠️ 주의: 포인터 타입으로 직접 호출
Base* ptr = new Derived();
ptr->method(); // "Derived" 호출 (가상 함수)
// 문제: virtual 키워드 없으면 "Base" 호출됨
class Base {
public:
void method() { std::cout << "Base\n"; } // virtual 없음!
};
class Derived : public Base {
public:
void method() { std::cout << "Derived\n"; }
};
int main() {
Base* ptr = new Derived();
ptr->method(); // "Base" 출력 (예상과 다름)
}
결론
다형성의 핵심: "같은 인터페이스, 다른동작", "타입에 상관없이 같은 방식으로 처리 가능"
다형성은 OOP의 가장 강력한 특징으로, 확장 가능하고 유연한 코드를 작성할 수 있게 해줍니다.
2.4 추상화 (Abstraction in OOP)
추상화(Abstraction)는 OOP의 가장 중요한 개념 중 하나입니다. 복잡한 구현 세부사항을 숨기고, 필요한 것만 드러내기를 의미합니다.
핵심 아이디어
// 추상화 없이 (복잡함)
Car myCar;
myCar.ignitionSystem.turnOn();
myCar.fuelPump.pressurize();
myCar.sparkPlugs.ignite();
myCar.engine.start();
// 추상화를 통해 (간단함)
Car myCar;
myCar.start(); // 내부 구현은 몰라도 됨
예제 1: 은행 계좌 (추상화의 완벽한 예)
class BankAccount {
private: // ← 구현 세부사항 숨김
double balance;
std::vector<Transaction> transactionHistory;
void validateAmount(double amount) {
// 복잡한 검증 로직
}
void logTransaction(const Transaction& t) {
// 거래 기록 로직
}
public: // ← 필요한 인터페이스만 노출
void deposit(double amount) {
validateAmount(amount);
balance += amount;
logTransaction(Transaction("deposit", amount));
}
void withdraw(double amount) {
if (balance >= amount) {
balance -= amount;
logTransaction(Transaction("withdraw", amount));
}
}
double getBalance() {
return balance; // 잔액만 알려줌
}
};
// 사용자 입장
BankAccount acc;
acc.deposit(1000);
acc.withdraw(200);
double balance = acc.getBalance();
// 사용자는 내부가 어떻게 작동하는지 모르고, 신경 쓸 필요도 없음
// 복잡한 검증, 기록, 보안 등은 모두 캡슐화됨
예제 2: 데이터베이스 연결
class Database {
private:
// 복잡한 내부 구현
std::unique_ptr<Connection> connection;
ConnectionPool pool;
void handleConnectionError(const std::string& error) {
// 에러 처리 로직
}
void parseQueryResult(const std::string& raw) {
// 쿼리 결과 파싱
}
public:
// 간단한 인터페이스만 제공
bool connect(const std::string& host) {
// 복잡한 연결 로직
}
std::vector<Record> query(const std::string& sql) {
// SELECT 쿼리 실행
}
void disconnect() {
// 연결 종료
}
};
// 사용자는 이것만 알면 됨
Database db;
db.connect("localhost");
auto results = db.query("SELECT * FROM users");
db.disconnect();
// 내부 연결 풀, 타임아웃, 재연결 메커니즘 등은 모두 숨겨짐
예제 3: 자동차 시스템
class Car {
private:
// 숨겨진 복잡한 부분들
class Engine {
void ignite() { /* ... */ }
void runCycle() { /* ... */ }
};
class FuelSystem {
void pumpFuel() { /* ... */ }
double measureOctane() { /* ... */ }
};
class ElectricalSystem {
void activateBattery() { /* ... */ }
};
Engine engine;
FuelSystem fuel;
ElectricalSystem electrical;
public:
// 사용자가 실제로 사용하는 간단한 메서드들
void start() {
electrical.activateBattery();
fuel.pumpFuel();
engine.ignite();
engine.runCycle();
}
void accelerate(double speed) {
engine.runCycle(); // 엔진 사이클 조정
}
void stop() {
engine.stop();
fuel.stopPumping();
}
};
// 사용 방법
Car myCar;
myCar.start(); // 단순하고 직관적
myCar.accelerate(60);
myCar.stop();
// 운전자는 엔진 점화, 연료 분사, 전기 시스템 등을 신경 쓸 필요 없음
추상화의 핵심 원칙
// ❌ 나쁜 예: 구현이 노출됨
class Student {
public:
std::vector<double> grades;
int credits;
std::string major;
// 클라이언트가 직접 계산해야 함
double calculateGPA() {
double sum = 0;
for (auto g : grades) sum += g;
return sum / grades.size();
}
};
// ✓ 좋은 예: 인터페이스만 제공
class Student {
private:
std::vector<double> grades;
int credits;
std::string major;
// 내부 구현 세부사항
double calculateWeightedGPA() const {
// 복잡한 가중치 계산
}
public:
// 사용자는 이것만 호출
double getGPA() const {
return calculateWeightedGPA();
}
void addGrade(double grade) {
// 유효성 검사
if (grade >= 0 && grade <= 4.0) {
grades.push_back(grade);
}
}
};
추상화 vs 캡슐화 (헷갈리는 개념)
캡슐화 (Encapsulation):
→ private/public으로 데이터 보호
→ "어떻게" 관련
→ 접근 제어
추상화 (Abstraction):
→ 복잡한 구현을 간단한 인터페이스로 만들기
→ "무엇을" 제공하는가 관련
→ 세부사항 숨기기
추상화의 장점
| 장 점 | 설 명 |
| 유지보수 용이 | 내부 구현을 바꿔도 공개 인터페이스만 유지하면 됨 |
| 코드 재사용성 | 복잡한 로직을 단순한 메서드로 제공 |
| 오류 감소 | 사용자가 실수하기 어려운 구조 |
| 유연성 | 구현을 자유롭게 변경 가능 |
| 가독성 | 간단한 인터페이스로 의도가 명확 |
실제 C++ 예제: 파일 시스템
class FileSystem {
private:
// 복잡한 내부 구현
std::map<std::string, INode> inodes;
std::vector<Block> blocks;
void allocateBlock() { /* ... */ }
void updateINode(const std::string& path) { /* ... */ }
void fragmentationCheck() { /* ... */ }
public:
// 사용자에게 제공하는 간단한 인터페이스
bool createFile(const std::string& filename) {
// 내부에서 INode 생성, 블록 할당 등을 처리
}
bool deleteFile(const std::string& filename) {
// 내부에서 블록 해제, INode 제거 등을 처리
}
std::string readFile(const std::string& filename) {
// 복잡한 블록 읽기 과정을 숨김
}
bool writeFile(const std::string& filename, const std::string& data) {
// 복잡한 블록 쓰기 과정을 숨김
}
};
// 사용자는 이렇게 간단하게 사용
FileSystem fs;
fs.createFile("document.txt");
fs.writeFile("document.txt", "Hello, World!");
std::string content = fs.readFile("document.txt");
fs.deleteFile("document.txt");
결론
추상화의 핵심: "사용자는 무엇을 하는지만 알면 되고, 어떻게 하는지는 몰라도 된다"
이를 통해 복잡한 시스템을 관리 가능한 수준으로 단순화하고, 코드의 품질과 안정성을 높입니다.
'Fundamental of CS > : : C++' 카테고리의 다른 글
| [OOP] 객체(Object)의 메모리 구조 (0) | 2024.07.16 |
|---|---|
| [OOP] OOP, Class (0) | 2024.07.16 |
| [Resource Move] Return Value Optimization(RVO) (0) | 2024.07.16 |
| [Resource Move] C++의 포인터와 Rust의 소유권 (0) | 2024.07.16 |
| [Resource Move] std::move() (0) | 2024.07.16 |