프로그래밍/C++

객체 지향 프로그래밍(OOP)의 핵심 개념: 클래스, 객체, 상속, 다형성, 캡슐화, 추상화

shimdh 2025. 1. 31. 21:59
728x90

1. 클래스와 객체

1.1 클래스란?

클래스는 객체를 생성하기 위한 설계도 또는 청사진입니다. 클래스는 속성(데이터)과 메서드(함수)를 정의하며, 이를 통해 여러 객체를 생성할 수 있습니다. 클래스는 현실 세계의 개념을 프로그램으로 모델링하는 데 사용됩니다. 예를 들어, 자동차, 은행 계좌, 학생 등과 같은 개념을 클래스로 표현할 수 있습니다.

class Car {
public:
    string color; // 속성
    int year;     // 속성

    void drive() { // 메서드
        cout << "The car is driving." << endl;
    }
};

위 예제에서 Car 클래스는 coloryear라는 속성과 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의 원칙을 이해하고 적용하는 것은 프로그래머로서의 역량을 한 단계 끌어올리는 데 중요한 역할을 합니다. 이러한 개념들을 실습하고 프로젝트에 적용해보는 과정에서 더욱 깊이 있는 이해를 얻을 수 있을 것입니다.

728x90