객체지향프로그래밍(3) - 객체, 클래스, 인스턴스

객체지향프로그래밍(OOP)의 기본 단위, 객체, 클래스, 인스턴스 개념 도입

Featured image

🔚 짧게 하는 복습

✅ 1. 객체지향 프로그래밍의 도입 배경을 이해한다.

✅ 2. 절차지향 프로그래밍과 객체지향 프로그래밍의 정의를 이해한다.

혹시 기억이 안 난다면, 다시 돌아가자


우리는 저번 강의에서 절차지향 프로그래밍과 객체지향 프로그래밍에 대해 다루었다.

다시 한번 강조해도 부족하지 않으니, 절차지향 프로그래밍과 객체지향 프로그래밍은 대립적인 관계가 아니다.

절차지향 프로그래밍함수 단위로 일련의 순서에 맞게 프로그래밍하는 방법이었고, 객체지향 프로그래밍은 마치 실생활에서의 개념처럼 상태와 행동을 가진 하나의 단위로 프로그래밍하는 방법이었다.

즉 오히려, 객체지향 프로그래밍은 절차지향 프로그래밍의 확장 같은 느낌이지 절대 세간의 오해처럼 대립 관계가 아니다.

이 강의에서는 이 객체지향 프로그래밍의 정확한 개념과, 객체가 무엇인지에 대해 다루어보겠다.


객체란?

객체상태와 행동을 가진 최소의 코딩 단위이다.

우리가 이때까지 다루어온 코딩 방식은, 자료를 함수로 다룬다는 개념이 지배적이었다.

예를 들어, 저번 시간의 이 함수를 보자.

void attack(pokemon src, pokemon* dest) {
	if (make_random() <= src.skill.accuracy) {
		dest->hp -= (src.attack * src.skill.power) * check_type(src.type, dest->type) - dest->defend;
	}
}

pokemon이라는 자료를 함수로 다룬다는 느낌이 강하고, pokemon이라는 자료가 attack이라는 행동을 가진다고 느껴지지 않는다.

그런데 실제 게임에서, 우리가 받아들이는 포켓몬이라는 것은 어떤가?

포켓몬은 그들의 특성을 가지고, 공격이라는 행동을 가진다고 생각하기 마련이다.

이러한 인간의 사고방식에서 착안해, 기존의 구조체에서 확장되어 상태(변수)뿐만 아니라 행동을 모두 가지는 최소 단위를 객체(Instance)라고 한다.

여기서 객체의 행동을 메소드(Method)라고 한다.

메소드의 구현은 어렵지 않고, 구조체에 멤버로 정의해주면 된다.

아래의 예를 보자.

#include <iostream>
#include <string>

enum { Fire, Water, Grass };

struct skill_info {
	int power;
	int accuracy;
};

struct pokemon {
	std::string name;
	int type;
	double hp;
	int attack;
	int defend;
	int mobility;
	struct skill_info skill;

	void do_attack() {
		std::cout << attack * skill.power << "의 데미지를 주었습니다." << std::endl;
	}
};

int main() {
	pokemon pomon = { "브케인", Fire, 450, 45, 10, 20, {8, 50} };
	pomon.do_attack();
}

이전 강의의 프로그램과 다르게, 위 코드는 포켓몬이라는 구조체가 공격(do_attack)이라는 행동(메소드)를 가지는 것을 알 수 있다.


클래스, 인스턴스란?

그런데 이렇게 하면 용어의 모호성이 생긴다.

main함수 위에서 선언한 pokemon도 구조체고, 아래에서 선언한 pomon도 pokemon 구조체이다.

전자는 객체의 설계도와 같은 역할이고, 아래는 실제로 만들어진 객체인데 구분의 필요가 생겼다.

그래서 객체지향 프로그래밍의 패러다임을 따라, 저런 데이터와 기능을 함께 묶어 놓은 사용자 정의 데이터 형식클래스(Class), 그 클래스를 통해 만들어진 실제 객체인스턴스(Instance)라고 부르기로 했다.

그리고 이를 분리하기 위해, C++에서는 클래스를 정의할 때 class라는 이름으로 정의를 하는 것으로 했다.

또, 위에서 언급한 객체를 더 엄밀히 정의하자면, 클래스를 통해 만들어진 인스턴스를 뜻한다.

즉, 아래처럼 코드를 수정할 수 있다.

#include <iostream>
#include <string>

enum { Fire, Water, Grass };

struct skill_info {
	int power;
	int accuracy;
};

class pokemon {
	// pokemon의 변수
    std::string name;
	int type;
	double hp;
	int attack;
	int defend;
	int mobility;
	struct skill_info skill;

    // pokemon의 메소드
	void do_attack() {
		std::cout << attack * skill.power << "의 데미지를 주었습니다." << std::endl;
	}
};// pokemon 클래스

int main() {
	pokemon pomon = { "브케인", Fire, 450, 45, 10, 20, {8, 50} };
	// pokemon 클래스의 인스턴스
    pomon.do_attack();
}

주석을 달아놓은 것처럼, main 상단은 말 그대로 클래스이고, main 함수 안에서 호출된 pomon은 그 클래스를 통해 실제로 만들어진 인스턴스, 즉 객체이다.

그런데 따라왔다면 알겠지만, 이 코드는 실행이 되지 않는다.

그 이유는 구조체와 다르게 클래스는 특수한 성질이 있기 때문이다.


public, private란?

클래스는 구조체와 달리 절차 지향적 성격을 제한하기 위해, 기본적으로 클래스 내의 변수는 외부에서 접근이, 메소드호출이 불가능하게 되어있다.

그런데 만약 외부 접근을 제어하고 싶다면 private와 public으로 설정할 수 있다.

또한, 이를 접근 지정자라고 한다.

외부 접근을 막고 싶다면 접근 지정자를 private:, 외부 접근을 허가하고 싶다면 public:이라고 작성하고 그 아래에 변수와 메소드를 선언하면 된다.

아래의 예제를 보자.

class pokemon {
private :
    // pokemon 클래스의 private 변수
	std::string name;
	int type;
	double hp;
	int defend;
	int mobility;
	struct skill_info skill;

    // pokemon 클래스의 private 메소드
	void get_hp() {
		std::cout << hp << " 의 체력을 가집니다." << std::endl;
	}

public:
    // pokemon 클래스의 public 변수
	int attack;

    // pokemon 클래스의 public 메소드
	void get_attack() {
		std::cout << attack<<" 의 공격력을 가집니다." << std::endl;
	}

}; //pokemon 클래스

int main() {
	pokemon pomon; //pokemon 클래스의 인스턴스
	pomon.attack = 30;
	pomon.get_attack();
	//위에는 됨

	pomon.hp = 40;
	pomon.get_hp();
	//이건 안됨
}

참고로 대부분 메소드는 외부 호출이 가능하게 하고, 변수는 외부 참조를 불가능하게 한다.

이는 조금만 생각해보면 이해가 가능한 게, 포켓몬의 행동은 외부에서 호출을 시킬 수 있어야 하지만, 속성, 공격력, 방어력 등을 고려하는 작동원리에 대해서는 굳이 사용자가 알 필요가 없기 때문이다.

조금 더 극단적으로 이야기하면, 우리가 카메라로 사진을 찍을 때 카메라 내부에서 어떤 동작이 일어나서 어떤 상태가 변화하는지 알아야 한다면 어떨까?

전문가를 제외하고는 아무도 카메라를 사용하지 못할 것이다.

이렇게 객체지향 프로그래밍에서는 철저하게 public과 private 속성으로 행동과 상태를 나누어서, 작동원리를 하나도 모르는 사람도 행동을 실행하는데 문제가 없게 한다.

다음 강의에서는 이와 관련된 객체지향의 대표적 성질 중 캡슐화추상화를 배워보도록 하겠다.


📖 오늘의 핵심(다 알기 전까지는 넘어가지 말자❗)

✅ 1. 객체가 무엇인지 안다.

✅ 2. 클래스와 구조체가 무엇이 다른지 안다.

✅ 3. 클래스와 인스턴스에 대해 안다.

✅ 4. public과 private 속성에 대해 안다.

⚠️ 대부분은 메소드는 외부 호출이 가능하게 하고, 변수는 외부 참조를 불가능하게 한다.

💣 과제,

  1. +, -, /, *와 숫자 2개를 입력받아 값을 계산하는 계산기를 클래스로 구현해보자(난이도 中) (무엇이 private로 가야 할지, 무엇이 public으로 가야 할지 생각해보자.)

  2. C++ BST 클래스를 구현해보자(난이도 中) (BST가 무엇인지 모르겠다면?)