객체지향 프로그래밍(OOP, Object-Oriented Programming)
객체지향 프로그래밍이란?
- 프로그램을 객체 단위로 구성하여, 코드의 재사용성과 유지보수성을 높이는 프로그래밍 패러다임
- 객체는 데이터(속성)와 이를 조작하는 함수(메서드)를 포함하는 독립적인 단위로 정의
객체지향 프로그래밍의 원리
추상화(Abstraction)
- 복잡한 시스템을 단순화하여 필요한 부분만을 표현하는 개념
- 예를 들어, 자동차를 운전할 때 내부 엔진 작동 방식까지 알 필요 없이 핸들과 페달만 조작하면 되는 개념
캡슐화(Encapsulation)
- 데이터와 메서드를 하나로 묶어 외부에서 직접 접근하지 못하도록 보호하는 개념
- 예를 들어, 자동차 내부 엔진은 직접 조작할 수 없으며, 운전자가 조작 가능한 인터페이스만 노출되는 개념
상속(Inheritance)
- 기존 클래스(부모 클래스)의 속성과 기능을 새로운 클래스(자식 클래스)가 물려받는 개념
- 예를 들어, Vehicle 클래스에서 Car와 Bike 클래스를 상속받아 공통 기능을 공유할 수 있는 개념
다형성(Polymorphism)
- 동일한 인터페이스를 가진 여러 개체가 다른 방식으로 동작할 수 있는 개념
- 예를 들어, Animal 클래스의 makeSound() 메서드가 Dog 클래스에서는 "멍멍"을, Cat 클래스에서는 "야옹"을 출력하는 개념
접근 제한자(Access Specifier)
- 클래스 멤버에 대한 접근 권한을 설정하는 키워드
- public : 외부에서도 접근 가능
- private : 클래스 내부에서만 접근 가능
- protected : 클래스 내부와 상속받은 클래스에서만 접근 가능
class Example {
private:
int privateVar;
protected:
int protectedVar;
public:
int publicVar;
};
클래스 vs 오브젝트
- 클래스(Class) : 객체를 정의하는 틀(템플릿)
- 객체(Object) : 클래스를 기반으로 생성된 실제 인스턴스
class Car { // 클래스
public:
string brand;
int year;
void display() {
cout << "Brand: " << brand << ", Year: " << year << endl;
}
};
int main() {
Car myCar; // 객체 생성
myCar.brand = "Tesla";
myCar.year = 2023;
myCar.display();
}
클래스 생성 - 데이터, 메서드
- 클래스는 속성(데이터)와 메서드(함수)로 구성된다
class Person {
private:
string name;
int age;
public:
void setName(string n) { name = n; }
string getName() { return name; }
void setAge(int a) { age = a; }
int getAge() { return age; }
};
객체에 포인터를 주는 방법
Person p1;
Person* pPtr = &p1; // 객체 포인터
pPtr->setName("Alice");
cout << pPtr->getName();
객체에 대한 포인터
- 객체의 주소를 저장하는 포인터를 이용하여 간접적으로 객체를 조작할 수 있다
Person obj;
Person* ptr = &obj;
ptr->setName("John");
cout << ptr->getName();
Stack, Heap에서 객체를 생성하는 방법
- Stack : 일반적인 객체 선언 시 스택에 할당
- Heap : new 연산자로 동적으로 객체를 생성할 경우 할당
Person* pHeap = new Person(); // Heap에 객체 생성
pHeap->setName("Bob");
cout << pHeap->getName();
delete pHeap; // 동적 할당 해제
데이터 은폐와 Property Function
- 데이터 은폐는 객체 내부의 데이터가 외부에서 직접 접근하지 못하도록 보호하는 개념
- 이를 위해 접근자 메서드를 사용
class BankAccount {
private:
double balance;
public:
double getBalance() { return balance; } // Accessor
void setBalance(double b) { balance = b; } // Mutator
void deposit(double amount) { balance += amount; } // Facilitator
bool isEmpty() { return balance == 0; } // Enquiry
};
생성자(Contructors)
- C++에서 생성자는 객체가 생성될 때 자동으로 호출되는 특수한 멤버 함수이다
기본 생성자(Default Constructor)
class Car {
public:
string brand;
Car() { brand = "Unknown"; } // 기본 생성자
};
파라미터 생성자(Parameter Constructor)
class Car {
public:
string brand;
Car(string b) { brand = b; } // 파라미터 생성자
};
복사 생성자(Copy Constructor)
- 기존 객체를 복사하여 새 객체를 생성하는 생성자
class Car {
public:
string brand;
Car(const Car &c) { brand = c.brand; } // 복사 생성자
};
복사 생성자의 문제점
- 객체가 직접 메모리를 할당할 경우 복사 생성자는 같은 메모리를 가리켜 문제가 발생할 수 있다
class Test {
private:
int* data;
public:
Test(int val) { data = new int(val); }
Test(const Test& t) { data = t.data; } // 문제 발생 (같은 메모리 공유)
};
- 해결 방법은 깊은 복사(Deep Copy)를 구현하는 것이다
Test(const Test& t) {
data = new int(*t.data); // 새로운 메모리 생성
}
Scope Resolution Operator(범위 지정 연산자 ::)
- 클래스의 멤버 함수를 클래스 외부에서 정의할 때 사용
class Sample {
public:
void show();
};
void Sample::show() { // 범위 지정 연산자 사용
cout << "Hello!";
}
인라인 함수(Inline Function)
- 짧은 함수의 경우 컴파일러가 함수를 호출하지 않고 직접 코드에 삽입하여 실행 속도를 높인다
inline int add(int a, int b) { return a + b; }
this 포인터
class Example {
public:
void display() { cout << this; } // 현재 객체의 주소 출력
};
Struct vs Class
- Struct : 기본 접근 제한자가 public
- Class : 기본 접근 제한자가 private