1. 클래스와 객체
1.1 클래스란?
클래스는 객체를 생성하기 위한 설계도 또는 청사진입니다. 클래스는 속성(데이터)과 메서드(함수)를 정의하며, 이를 통해 여러 객체를 생성할 수 있습니다. 클래스는 현실 세계의 개념을 프로그램으로 모델링하는 데 사용됩니다. 예를 들어, 자동차, 은행 계좌, 학생 등과 같은 개념을 클래스로 표현할 수 있습니다.
class Car {
public:
string color; // 속성
int year; // 속성
void drive() { // 메서드
cout << "The car is driving." << endl;
}
};
위 예제에서 Car
클래스는 color
와 year
라는 속성과 drive()
라는 메서드를 가지고 있습니다. 이 클래스를 사용하면 다양한 자동차 객체를 생성할 수 있습니다.
1.2 객체란?
객체는 클래스로부터 생성된 실체입니다. 클래스에 정의된 속성과 메서드를 가진 구체적인 인스턴스입니다. 객체는 클래스의 설계도를 바탕으로 만들어지며, 각 객체는 독립적인 상태를 가질 수 있습니다.
int main() {
Car myCar; // Car 클래스의 인스턴스인 myCar 생성
myCar.color = "Red"; // 색상 설정
myCar.year = 2020; // 연도 설정
cout << "My car color: " << myCar.color << endl;
cout << "My car year: " << myCar.year << endl;
myCar.drive(); // 메서드 호출
return 0;
}
이 코드에서는 myCar
라는 객체를 생성하고, 그 속성을 설정한 후 drive()
메서드를 호출합니다. 객체는 클래스의 설계도를 바탕으로 만들어지며, 각 객체는 독립적인 상태를 가질 수 있습니다.
2. 생성자와 소멸자
2.1 생성자란?
생성자는 객체가 생성될 때 자동으로 호출되는 특별한 함수로, 주로 객체의 초기 상태를 설정하는 데 사용됩니다. 생성자는 클래스 이름과 동일하며, 반환형이 없습니다. 생성자를 통해 객체의 초기값을 설정하거나 필요한 리소스를 초기화할 수 있습니다.
class Car {
public:
string model;
int year;
// 생성자 정의
Car(string m, int y) {
model = m;
year = y;
cout << "차량이 만들어졌습니다: " << model << ", 연식: " << year << endl;
}
};
int main() {
Car myCar("현대 아반떼", 2022); // 객체를 만들면서 자동으로 생성자가 호출됨
return 0;
}
위 예제에서 Car
클래스의 생성자는 차량 모델과 연식을 초기화하고, 객체가 생성될 때 메시지를 출력합니다. 생성자를 통해 객체의 초기 상태를 안전하게 설정할 수 있습니다.
2.2 소멸자란?
소멸자는 객체가 메모리에서 제거될 때 자동으로 호출되는 함수로, 주로 리소스를 정리하는 데 사용됩니다. 소멸자는 클래스 이름 앞에 ~
를 붙여 정의하며, 반환형이 없습니다. 소멸자를 통해 동적 메모리 해제, 파일 닫기 등의 정리 작업을 수행할 수 있습니다.
class Book {
public:
string title;
// 생성자 정의
Book(string t) : title(t) {
cout << "책 제목 '" << title << "' 이(가) 만들어졌습니다." << endl;
}
// 소멸자 정의
~Book() {
cout << "책 제목 '" << title << "' 이(가) 삭제되었습니다." << endl;
}
};
int main() {
Book myBook("C++ Programming"); // 여기서 myBook이라는 책이 만들어짐
} // main 블록 종료 시 myBook은 범위를 벗어나고 소멸자가 자동으로 호출됨
위 예제에서는 Book
클래스의 소멸자가 객체가 소멸될 때 메시지를 출력합니다. 이를 통해 객체의 생명주기를 확인할 수 있습니다.
3. 상속
3.1 상속의 개념
상속은 기존 클래스의 속성과 메서드를 새로운 클래스에 전달하여 확장하는 방법입니다. 이를 통해 코드 중복을 줄이고, 더 구조화된 프로그램을 만들 수 있습니다. 상속은 "is-a" 관계를 표현하는 데 적합합니다. 예를 들어, "개는 동물이다"와 같은 관계를 상속으로 표현할 수 있습니다.
class Animal {
public:
void eat() {
std::cout << "Eating..." << std::endl;
}
};
class Dog : public Animal {
public:
void bark() {
std::cout << "Woof!" << std::endl;
}
};
int main() {
Dog myDog;
myDog.eat(); // Output: Eating...
myDog.bark(); // Output: Woof!
}
위 예제에서 Dog
클래스는 Animal
클래스로부터 eat()
메서드를 상속받습니다. 이를 통해 Dog
클래스는 Animal
클래스의 기능을 재사용하면서도 자신만의 추가 기능(bark()
)을 정의할 수 있습니다.
3.2 상속의 종류
C++에서는 단일 상속, 다중 상속, 계층적 상속 등 다양한 형태의 상속이 가능합니다. 단일 상속은 하나의 부모 클래스로부터 상속받는 것이고, 다중 상속은 두 개 이상의 부모 클래스로부터 상속받는 것입니다.
class Pet {
public:
void play() {
cout << "Playing with pet." << endl;
}
};
class WorkingAnimal {
public:
void work() {
cout << "Working hard." << endl;
}
};
class Dog : public Pet, public WorkingAnimal {
public:
void bark() {
cout << "Woof!" << endl;
}
};
int main() {
Dog myDog;
myDog.play(); // Output: Playing with pet.
myDog.work(); // Output: Working hard.
myDog.bark(); // Output: Woof!
}
4. 다형성
4.1 다형성이란?
다형성은 같은 이름의 메서드가 다양한 방식으로 동작할 수 있는 능력을 의미합니다. C++에서는 함수 오버로딩과 가상 함수를 통해 다형성을 구현합니다.
4.2 함수 오버로딩
함수 오버로딩은 동일한 이름의 함수를 여러 번 정의하되, 각 함수는 서로 다른 매개변수 목록을 가집니다. 이를 통해 개발자는 명확하고 직관적인 코드를 작성할 수 있습니다.
class Math {
public:
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
};
int main() {
Math math;
cout << "정수 덧셈: " << math.add(5, 3) << endl; // Output: 8
cout << "실수 덧셈: " << math.add(5.5, 3.2) << endl; // Output: 8.7
}
4.3 가상 함수
가상 함수는 상속 관계에서 부모 클래스의 메서드를 자식 클래스에서 재정의할 수 있게 해줍니다. 이를 통해 런타임 다형성을 구현할 수 있습니다.
class Animal {
public:
virtual void sound() {
cout << "Animal makes a sound" << endl;
}
};
class Dog : public Animal {
public:
void sound() override {
cout << "Dog barks" << endl;
}
};
class Cat : public Animal {
public:
void sound() override {
cout << "Cat meows" << endl;
}
};
void makeSound(Animal* animal) {
animal->sound(); // 런타임에 어떤 소리가 출력될지 결정됨
}
int main() {
Dog dog;
Cat cat;
makeSound(&dog); // Output: Dog barks
makeSound(&cat); // Output: Cat meows
return 0;
}
5. 캡슐화
5.1 캡슐화란?
캡슐화는 데이터와 메서드를 하나의 단위인 객체로 묶어 외부에서 접근을 제한하는 방법입니다. 이를 통해 데이터 무결성을 유지하고 코드의 안정성을 높입니다. 캡슐화는 OOP의 핵심 원칙 중 하나로, 데이터를 보호하고 인터페이스를 제공하여 사용자가 내부 구현을 알 필요 없이 객체를 사용할 수 있게 합니다.
class BankAccount {
private:
double balance;
public:
BankAccount(double initialBalance) : balance(initialBalance) {}
void deposit(double amount) {
if (amount > 0) {
balance += amount;
cout << amount << "원이 입금되었습니다." << endl;
} else {
cout << "입금 금액은 0보다 커야 합니다." << endl;
}
}
void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
cout << amount << "원이 출금되었습니다." << endl;
} else {
cout << "출금 금액이 유효하지 않습니다." << endl;
}
}
double getBalance() const {
return balance;
}
};
int main() {
BankAccount account(100); // 초기 잔액 100원으로 계좌 생성
account.deposit(50); // 입금 시도
account.withdraw(30); // 출금 시도
cout << "현재 잔액: " << account.getBalance() << "원" << endl;
return 0;
}
6. 추상화
6.1 추상화란?
추상화는 복잡한 시스템을 단순하게 표현하고 이해할 수 있도록 돕는 과정입니다. C++에서는 순수 가상 함수를 통해 추상화를 구현합니다. 추상화는 사용자가 세부 구현을 알 필요 없이 객체의 핵심 기능에 집중할 수 있게 합니다.
class Vehicle {
protected:
string brand;
int wheels;
public:
Vehicle(string b, int w) : brand(b), wheels(w) {}
virtual void startEngine() = 0; // 순수 가상 함수
};
class Car : public Vehicle {
public:
Car(string b) : Vehicle(b, 4) {}
void startEngine() override {
cout << brand << " 자동차의 엔진이 시작되었습니다." << endl;
}
};
int main() {
Car myCar("현대");
myCar.startEngine(); // Output: 현대 자동차의 엔진이 시작되었습니다.
return 0;
}
결론
객체 지향 프로그래밍은 소프트웨어 개발에서 매우 중요한 개념입니다. 클래스와 객체, 상속, 다형성, 캡슐화, 추상화와 같은 OOP의 핵심 개념을 이해하고 활용하면 더 구조화되고 유지보수하기 쉬운 코드를 작성할 수 있습니다. C++는 이러한 OOP 개념을 강력하게 지원하므로, 이를 잘 활용하면 복잡한 시스템도 효율적으로 설계하고 구현할 수 있습니다.
이러한 개념들은 단순히 문법을 익히는 것을 넘어, 실제 프로그래밍에서 어떻게 활용할지 고민하는 과정에서 더욱 깊이 있게 이해될 수 있습니다. 예를 들어, 캡슐화를 통해 데이터를 보호하고, 상속을 통해 코드 재사용성을 높이며, 다형성을 통해 유연한 프로그램을 설계할 수 있습니다. 이러한 원칙들을 잘 이해하고 적용한다면, 더 나은 소프트웨어를 개발하는 데 큰 도움이 될 것입니다.
또한, OOP는 단순히 코드를 작성하는 데 그치는 것이 아니라, 프로그램의 구조를 설계하고 문제를 해결하는 방법론으로도 활용됩니다. 따라서 OOP의 원칙을 이해하고 적용하는 것은 프로그래머로서의 역량을 한 단계 끌어올리는 데 중요한 역할을 합니다. 이러한 개념들을 실습하고 프로젝트에 적용해보는 과정에서 더욱 깊이 있는 이해를 얻을 수 있을 것입니다.
'프로그래밍 > C++' 카테고리의 다른 글
C++ 메모리 관리: 동적 메모리 할당과 해제의 중요성 (0) | 2025.02.01 |
---|---|
C++에서 포인터와 참조자의 이해와 활용 (0) | 2025.02.01 |
C++ 함수: 정의, 호출, 매개변수, 반환값, 그리고 오버로딩 (0) | 2025.01.31 |
C++ 기본 문법: 데이터 타입, 변수, 상수, 연산자, 조건문 및 반복문 (0) | 2025.01.31 |
C++ 프로그래밍 입문: 역사, 특징, 설치 및 첫 번째 프로그램 (0) | 2025.01.31 |