레슨 8 / 10·2개 토픽
클래스와 객체지향
클래스 정의와 접근 지정자
C++에서 클래스는 데이터(멤버 변수)와 동작(멤버 함수)을 하나로 묶는 사용자 정의 타입입니다. public, private, protected 접근 지정자로 외부에서의 접근을 제어합니다.
cpp
#include <iostream>
#include <string>
using namespace std;
class BankAccount {
private: // 외부 접근 불가
string owner;
double balance;
public: // 외부 접근 가능
// 생성자
BankAccount(const string& name, double initial)
: owner(name), balance(initial) {}
// 소멸자
~BankAccount() {
cout << owner << " 계좌 소멸" << endl;
}
// 복사 생성자
BankAccount(const BankAccount& other)
: owner(other.owner + " (복사)"), balance(other.balance) {}
void deposit(double amount) {
if (amount > 0) balance += amount;
}
bool withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}
void showInfo() const { // const 멤버 함수
cout << owner << " | 잔액: " << balance << "원" << endl;
}
protected: // 파생 클래스에서 접근 가능
double getBalance() const { return balance; }
};
int main() {
BankAccount acc("김철수", 10000);
acc.deposit(5000);
acc.withdraw(3000);
acc.showInfo(); // 김철수 | 잔액: 12000원
BankAccount copy(acc); // 복사 생성자 호출
copy.showInfo(); // 김철수 (복사) | 잔액: 12000원
return 0;
}상속과 가상 함수
상속은 기존 클래스(기반 클래스)를 확장하여 새로운 클래스(파생 클래스)를 만드는 메커니즘입니다. virtual 키워드로 선언한 함수는 파생 클래스에서 재정의(override)할 수 있으며, 기반 클래스 포인터를 통해 호출해도 실제 객체의 함수가 실행됩니다(다형성).
cpp
#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;
// 추상 기반 클래스
class Shape {
protected:
string name;
public:
Shape(const string& n) : name(n) {}
virtual ~Shape() = default; // 가상 소멸자 필수
// 순수 가상 함수 — 파생 클래스에서 반드시 구현
virtual double area() const = 0;
virtual double perimeter() const = 0;
virtual void print() const {
cout << name << " | 넓이: " << area()
<< " | 둘레: " << perimeter() << endl;
}
};
class Circle : public Shape {
double radius;
public:
Circle(double r) : Shape("원"), radius(r) {}
double area() const override { return 3.14159 * radius * radius; }
double perimeter() const override { return 2 * 3.14159 * radius; }
};
class Rectangle : public Shape {
double width, height;
public:
Rectangle(double w, double h)
: Shape("직사각형"), width(w), height(h) {}
double area() const override { return width * height; }
double perimeter() const override { return 2 * (width + height); }
};
int main() {
// 다형성: 기반 클래스 포인터로 파생 객체 사용
vector<unique_ptr<Shape>> shapes;
shapes.push_back(make_unique<Circle>(5.0));
shapes.push_back(make_unique<Rectangle>(4.0, 6.0));
for (const auto& s : shapes) {
s->print(); // 각 도형의 실제 타입에 맞는 함수 호출
}
// 원 | 넓이: 78.5398 | 둘레: 31.4159
// 직사각형 | 넓이: 24 | 둘레: 20
return 0;
}- •
public— 어디서든 접근 가능 - •
private— 해당 클래스 내부에서만 접근 가능 (기본값) - •
protected— 해당 클래스와 파생 클래스에서 접근 가능 - •생성자 — 객체 생성 시 자동 호출, 초기화 리스트(
:)로 멤버 초기화 - •소멸자(
~) — 객체 소멸 시 자동 호출, 리소스 해제 담당 - •
virtual— 파생 클래스에서 재정의 가능한 함수 선언 - •
= 0— 순수 가상 함수, 해당 클래스를 추상 클래스로 만듦 - •
override— 기반 클래스의 가상 함수를 재정의함을 명시
💡
상속 계층에서 기반 클래스의 소멸자는 반드시 virtual로 선언하세요. 그렇지 않으면 기반 클래스 포인터로 파생 객체를 삭제할 때 파생 클래스의 소멸자가 호출되지 않아 메모리 누수가 발생합니다.