면접에서 제대로 답변 못한 김에 정리하는 객체 지향 프로그래밍 4가지 특징!
객체 지향 프로그래밍 (OOP) 란?
Object-Oriented Promgramming의 약자로, 객체 지향 프로그래밍(OOP)은 데이터를 객체라는 단위로 나누고, 객체 간의 상호작용을 통해 프로그램을 설계하는 방식이다.
캡슐화, 상속, 다형성, 추상화 같은 개념을 활용하여 코드의 재사용성, 유지보수성, 확장성을 높인다.
즉, 현실 세계를 모델링하여 프로그래밍에 적용하는 방법론이다.
C++ 예제 코드로 각각의 특징에 대해서 알아보자
캡슐화 (Encapsulation)
캡슐화는 데이터와 메서드를 하나의 객체로 묶고 데이터 접근을 제어한다. 이를 통해 외부에서 직접 접근하지 못하게 한다.
- brand 와 speed는 private로 선언되어 외부에서 직접 접근할 수 없음
- getBrand와 SetSpeed() 같은 메서드를 통해 데이터를 간접적으로 조작
#include <iostream>
#include <string>
class Car
{
private: // 외부에서 직접 접근 불가
std::string brand;
int speed;
public:
// 생성자
Car(std::string b, int s) : brand(b), speed(s) {}
// getter와 setter로 데이터 접근 제어
std::string getBrand()
{
return brand;
}
void setBrand(std::string b)
{
brand = b;
}
int getSpeed()
{
return speed;
}
void setSpeed(int s)
{
if (s >= 0) // 유효성 검사
{
speed = s;
}
}
void display()
{
std::cout << "Brand: " << brand << "\n";
std::cout << "Speed: " << speed << "km/h\n";
}
};
int main()
{
Car myCar("Tesla", 120);
myCar.display();
myCar.setSpeed(150);
std::cout << "Updated speed: " << myCar.getSpeed() << "km/h\n";
return 0;
}
상속(Inheritance)
상속은 기존의 클래스의 속성과 동작을 새로운 클래스가 물려받는다.
- Dog 클래스는 Animal 클래스를 상속받아 eat() 메서드를 사용할 수 있음
- 상속을 통하여 코드 재사용성을 높일 수 있음
#include <iostream>
class Animal
{
public:
void eat()
{
std::cout << "This animal eats food.\n";
}
};
class Dog : public Animal // Animal class를 public 상속
{
public:
void bark()
{
std::cout << "This dog barks\n";
}
};
int main()
{
Dog myDog;
myDog.eat(); // 상속 받은 메서드
myDog.bark(); // Dog 클래스 고유의 메서드
return 0;
}
": public" 은 public 상속으로 아래와 같이 상속된다.
- 상위 클래스의 public 멤버는 하위 클래스에서 public으로 상속
- 상위 클래스의 protected 멤버는 하위 클래스에서 protected로 상속
- 상위 클래스의 private 멤버는 하위 클래스에서 접근 불가능하지만, 상위 클래스의 메서드를 통해 간접적으로 접근 가능
다형성 (Polymorphism)
다형성은 같은 인터페이스로 다양한 동작을 구현하는 것을 의미한다.
주로 가상 함수와 오버라이딩을 통하여 구현된다.
- draw() 메서드는 Shape 클래스의 포인터를 통해 호출되지만 실체 객체 타입 (Circle 또는 Rectangle)에 따라 동작이 달라짐
- 코드의 유연성과 확장성을 높일 수 있음
#include <iostream>
class Shape
{
public:
virtual void draw() // 가상함수
{
std::cout << "Drawing a shape.\n";
}
};
class Circle : public Shape
{
public:
void draw() override // 오버라이딩
{
std::cout << "Drawing a circle.\n";
}
};
class Rectangle : public Shape
{
public:
void draw() override // 오버라이딩
{
std::cout << "Drawing a rectangle.\n";
}
};
int main()
{
Shape* myShape; // 기본 클래스 포인터
Circle myCircle;
Rectangle myRectangle;
myCircle.draw();
myRectangle.draw();
myShape = &myCircle;
myShape->draw(); // Circle의 draw() 호출
myShape = &myRectangle;
myShape->draw(); // Rectangle의 draw() 호출
return 0;
}
가상 함수 (Virtual Fucntion)는 기본 클래스(Base Class)에서 선언되며, 하위 클래스(Derived Class)에서 재정의(Override)될 수 있는 함수이다. 재정의하지 않아도 기본 클래스의 구현을 사용할 수 있다.
virtual 키워드를 사용해 선언한다.
추상화 (Abstraction)
추상화는 객체의 복잡성을 숨기고 꼭 필요한 부분만 노출하는 것을 의미한다.
C++에는 추상클래스와 순수 가상 함수로 구현할 수 있다.
- Animal 클래스는 추상 클래스이며, sound()는 순수 가상 함수
- Dog와 Cat 클래스는 이를 구현하여 자신만의 동작을 정의
#include <iostream>
// 추상 클래스 (순수가상함수 사용)
class Animal
{
public:
virtual void sound() = 0; // 순수 가상 함수
};
class Dog : public Animal
{
public:
void sound() override
{
std::cout << "Woof! Woof!\n";
}
};
class Cat : public Animal
{
public:
void sound() override
{
std::cout << "Meow?\n";
}
};
int main()
{
Animal* myAnimal; // 추상 클래스 포인터
Dog myDog;
Cat myCat;
myAnimal = &myDog;
myAnimal->sound(); // Dog의 sound() 호출
myAnimal = &myCat;
myAnimal->sound(); // Cat의 sound() 호출
return 0;
}
순수 가상 함수 (Pure Virtual Function)는 C++의 추상 클래스(Abstract Class)에서 사용되는 함수로, 해당 클래스에서 반드시 하위 클래스에서 재정의해야 하는 메서드를 정의할 때 사용한다.
순수 가상 함수는 아래와 같은 형태로 선언된다.
virtual void functionName() = 0;
- 순수 가상 함수가 포함된 클래스는 추상 클래스가 됨
- 추상 클래스는 인스턴스화할 수 없음. 즉, 직접 객체를 생성할 수 없음
- 순수 가상 함수를 정의함으로써, 공통 인터페이스를 제공하고, 해당 인터페이스에서 반드시 구현하도록 강제할 수 있음
- 순수 가상 함수는 = 0을 사용하여 선언되며, 하위 클래스에서 반드시 재정의해야 함