본문 바로가기

C++ 일기

19. 클래스(Class)

클래스의 개념을 이해하기 위해 다양한 비유들이 있다.

이는 그만큼 이 클래스라는 개념이 정말 중요하고, 앞서 말한 객체지향 프로그래밍을 다룰 때 없어선 안 될 존재라는 것을

증명하는 지표인 것 같다.

 

클래스를 비유하는 말에는 여러가지가 있다. 대표적인 예로는 설계도라고 말 할 수 있을 것이다.

설계도를 통해서 물건을 만들면, 그 물건은 몇번을 만들어내도 설계도와 똑같이 만들어지기 때문이다.

 

우리가 일상생활 속에서, 가족과 굉장히 닮은 사람을 보면 붕어빵이라고들 말 한다.

어쩌면 클래스는 이러한 이유에서 설계도 보다는 붕어빵을 만드는 붕어빵 틀이라고 하는 것이 더 친숙한 표현일 수 있겠다.

 

클래스는 붕어빵 틀이다. 붕어빵 틀을 통해 만들어진 붕어빵은 모두 붕어빵틀의 모양대로 생겼다.

하지만 붕어빵 틀이 있더라도 재료가 없거나, 불을 붙일 수 없다면 붕어빵이 제대로 만들어지지 못 할 것이다.

그렇기에 우리는 붕어빵 틀에 여러 기능과 재료를 더해서, 맛있는 붕어빵을 만들 수 있도록 해야한다.

 

아래는 클래스의 기본 틀이다.

#include<iostream>

class 붕어빵틀
{
public:


private:

}

먼저 class라고 적은 후, class의 이름을 입력한다. 그 후엔 중괄호로 class의 구역을 정한다.

사실, class는 위에 있는 것들 말고도 생성자와 소멸자라는 추가적인 요소들이 있는데, 이에 대해선 다음에 설명하도록 하겠다.

public: 과 private: 라는 처음보는 문구가 적혀있다. 우선은 private부터 설명하겠다.

private:는, 클래스 내부의 값을 다른 곳에서 접근하지 못하게 하고,

class 스스로만 사용할 수 있는 기능이나 요소를 저장하는 곳이다.

class는 정보의 은닉을 중요시 여기므로, public도, private도 아닌 곳에 변수나 함수를 선언하여도,

기본 값으로 private로 설정된다.

private에는 대체적으로 class의 멤버변수들이 선언된다.

 

다음으로는 public이다. public은 public 안의 있는 값이나 기능들을 외부 함수에서 접근할 수 있도록 만들어준다.

아무리 정보의 은닉이 중요하더라도,

아무것도 외부에 공개되어있지 않으면 그 클래스는 고립된 외딴 섬이 되어

아무런 기능도 수행할 수 없기 때문이다.

설령, 내부에서 어떤 기능을 수행하더라도, 그 기능을 밖으로 전달하지 않기 때문에 무용지물이다.

 

그럼, 이제 class를 직접 사용해보도록 하겠다.

#include<iostream>

class 붕어빵틀
{
public:
   void 불올리기() { std::cout << "화로에 불을 붙인다" << std::cout ;}
   void 붕어빵뒤집기() { std::cout << "붕어빵이 타기 전에 붕어빵을 뒤집는다." <<std::cout; }

private:
   int 팥;
   int 밀가루;
   int 물;
}

int main()
{
   붕어빵틀 boong;
   boong.불올리기();
   boong.붕어빵뒤집기();
   
   boong.팥 = 10;
   // error
   
   // 출력값 : 화로에 불을 붙인다.	붕어빵이 타기 전에 붕어빵을 뒤집는다.
}

위의 붕어빵 예시를 좀 더 이해할 수 있도록 위와같이 편집해보았다.

주로 함수를 public에, 변수를 private에 선언하지만, 예외는 있다.

먼저, 구조체를 선언할 때 처럼 클래스의 이름과, 클래스로 찍어낼 붕어빵의 이름을 정하여

붕어빵을 만들어 낸다.

그렇게 만들어진 boong에, . 점을 찍어 클래스 내부에 있는 값을 참조할 수 있다.

그래서 위 처럼 boong에 접근하여 public에 있는 함수인 불올리기와 붕어빵뒤집기를 호출할 수 있다.

앞서 설명한 것 처럼 public에만 접근이 가능하며, private에 있는 값은 점을 찍어도 참조되지 않는다.

따라서 위처럼 boong.팥으로 접근하여 해당 값에 10을 대입하려고 하면 붉은줄이 생기며 에러가 출력된다.

 

그러면 모든 값이 다 public으로 있어서 모두 접근할 수 있게 되면 어디서든 간편하게 사용할텐데,

왜 public과 private이 나누어져 있는 것일까?

 

이에대한 해답은 앞서 설명한 바 있는 정보의 은닉 때문이다.

물론 사용자의 입장에서는 사용하기 편하지만, 만약 이렇게 공개되어있는 값에 접근을 잘못하여

구하고자 하는 값이 엉뚱한 값으로 바뀌어 버린다거나,

에러가 발생하게 되었는데, 어디서 에러가 났는지 찾기가 굉장히 어려워질 수 있기 때문에 주의해야한다.

무분별한 전역변수 및 public의 사용은 심각한 에러를 초래할 수 있기 때문에,

우리는 public과 private를 나누었으며, 기본값을 private로 설정한 이유도 바로 이것이다.

 

어쨌거나 우리는 class 내부에 있는 꽁꽁 숨겨진 private값에 접근하고, 값도 바꾸어 자유자재로 사용하고싶다.

이를 위해서는, 위의 원칙, private는 외부에 공개되지 않는다, public은 공개된다. 를 이용하여 

private의 값을 조절할 것이다.

 

#include<iostream>

class Bank
{
public:
   int GetBal() { return balance; }
   int SetBal(int num) { balance = num; }
private:
   int balance;
}

int main()
{
   Bank me;
   
   me.SetBal(1000);
   int myBalance = me.GetBal();
   
   std::cout << "내 통장 잔고 : " << myBalance;
   
   // 출력값 : 내 통장 잔고 : 1000
}

예시를 위해 이번엔 은행과 잔고로 예를 들었다.

위의 GetBalance는 외부에서 호출이 가능하고, 이 함수는 클래스 내부의 멤버이므로 멤버에게만 허용된 private값에

접근할 수 있다. 따라서 GetBalance는 private의 balance변수에 접근하여, 해당값을 반환한다.

SetBalance도 마찬가지다. 이 함수 역시 클래스의 멤버이기 때문에 private값에 접근하여 해당 값을 바꾼다.

이렇게, 외부에 호출되어 클래스 내부의 값에 접근하게 하는 함수를 Getter,

외부에 호출된 후 클래스 내부의 값에 접근하여 해당 값을 설정하게 하는 함수를 Setter라고 부른다.

 

클래스의 독특한 구조 때문에, 접근방식이 조금 특이하고 이해하기 어려울 수 있으니 또 예시를 들겠다.

클래스가 하나의 은행이라고 생각해보자.

나(me)는 은행을 통해 내 통장 잔고를 확인하고 싶고, 내 잔고를 입금 혹은 출금하려고 한다.

그런데 내 돈은 은행의 금고에 들어있어 외부인인 나는 접근할 수 없다.

그렇기 때문에 은행 직원을 통해 금고에 접근하여 간접적으로 내 돈을 받거나 넣어야 한다.

은행이 클래스라면, 금고에서 내 잔고를 확인하여 알려주는 직원이 Getter인 GetBal이고,

금고에 접근하여 내 돈을 꺼내거나, 내 돈을 받아 금고에 넣어주는 직원이 Setter인 SetBal이다.

public에 있는 멤버들은 외부와 내부를 오갈 수 있는 관계자. 여기서는 은행의 직원 혹은 경비원일 것이고,

private에 있는 멤버들은 외부와는 상호작용을 하지 않고, 오직 내부 멤버들에게만 접근이 허용된 금고와 같은 존재이다.

 

 

 

 

또, 위에서 말했던 붕어빵의 예시를 다시 가져오자면,

위에서 설명한 Getter와 Setter를 통해 서로 다른종류의 붕어빵을 만들고, 불러올 수 있게 된다.

#include<iostream>

class 붕어빵
{
public:
   void 붕어빵앙금넣기(std::string 속재료) { 앙금 = 속재료; }
   void 붕어빵가져오기() { std::cout << 앙금 << "붕어빵 "; }

private:
   std::string 앙금;
   
   int 밀가루;
   int 물;
}

int main()
{
   붕어빵 팥붕;
   붕어빵 슈붕;
   팥붕.붕어빵앙금넣기(팥);
   슈붕.붕어빵앙금넣기(슈크림);
   
   팥붕.붕어빵가져오기();
   슈붕.붕어빵가져오기();
   
   // 출력값 : 팥붕어빵 슈크림붕어빵
}

클래스를 붕어빵으로 비유했을 떄의 완성된 예시이다.

 

위의 붕어빵 클래스가 붕어빵이라면 공통으로 지녀야 할 모든 요소들을 가지고있고,

이를 통해서 다양한 붕어빵을 만들 수 있는 것 처럼,

 

클래스도 마찬가지로 내가 동일한 특성을 가지고있는 객체를 여럿 만들고 싶다면,

그들이 공통적으로 가져야 할 특징들을 멤버변수와 멤버함수로 만들어두고, 

이를 통해 유사한 객체들을 여럿 만들 수 있다.

 

이렇게 클래스를 통해 여러 객체들을 만들고, 만들어진 여러 객체들이 서로 상호작용을 하며

프로그램을 완성시키는 것이 객체지향 프로그래밍이다.

 

 

 

 

 

클래스는 객체지향 프로그래밍에 있어서 굉장히 중요한 개념이며,

객체지향 프로그래밍의 특성상 서로 다른 다양한 객체들과 상호작용을 활발히 하기 때문에 일어날 수 있는

문제점들. 예를 들면, 데이터가 다른 함수나 클래스에 유출되어 해당 값이 바뀌거나, 이로 인해 버그가 일어나도

쉽게 찾을 수 없는 문제들을 해결하기 위해 철저한 보안능력, 정보 은닉능력을 가졌다.

 

클래스는 굉장히 중요한 개념이고, 더 다룰 것들이 많은 개념이지만,

C++에서 다루는 클래스는 여기까지 다루도록 하고, 최종 목표인 유니티 프로그래밍을 위해

C#을 주력으로 다룰 예정이기에 C#의 클래스와 객체지향을 더욱 공부해나가고,

이를 위주로 포스팅 하게 될 것이다.