포털:컴퓨터공학/C++/class: UPGRADE struct

위키배움터

struct[편집]

C에서와 마찬가지로, C++에서 struct를 사용할 수 있습니다. 다음 소스를 봅시다.

7.01 struct 사용 C++ 소스
#include <iostream>
#include <cstring>

using namespace std;

struct people{
 char name[20];
 int age;
 char phoneNumber[20];
};

void view(struct a)
{
 cout<<"["<<a.name<<"] age : "<<a.age<<", phone number : "<<a.phoneNumber<<endl;
}

int main(void)
{
 char input[20]={};
 int inputAge;
 struct people man;

 cin>>input;
 strcpy(man.name, input);

 cin>>inputAge;
 man.age=inputAge;
 
 cin>>input;
 strcpy(man.phoneNumber, input);
 view(man);

 return 0;
}
YangHyeonSik
21
010-1234-5678
[YangHyeonSik] age : 21, phone number : 010-1234-5678

잘 작동되는 것 같습니다. 하지만 만약 다음과 같은 소스 실수를 저지르면 어떻게 될까요(소스 7.01과 비슷하고 다른 부분엔 주석처리 하였습니다.)?

7.02 잘못된 함수의 호출
#include <iostream>
#include <cstring>

using namespace std;

struct people{
 char name[20];
 int age;
 char phoneNumber[20];
};

void view(struct a)
{
 cout<<"["<<a.name<<"] age : "<<a.age<<", phone number : "<<a.phoneNumber<<endl;
}

int main(void)
{
 char input[20]={};
 int inputAge;
 int woman; // 다른 부분
 struct people man;

 cin>>input;
 strcpy(man.name, input);

 cin>>inputAge;
 man.age=inputAge;
 
 cin>>input;
 strcpy(man.phoneNumber, input);
 view(woman); // 다른 부분

 return 0;
}

함수의 호출을 잘못하였군요. struct people 변수만 전달받아야 할 함수에 int형 변수를 전달했기 때문입니다. 이런 경우가 실제 코딩 시 발생할까요?

혹시 모릅니다. 소스 코드가 길어지면 충분히 발생할 수 있는 실수입니다. 그럼 함수를 struct안에 넣어 버려서 struct 안의 변수를 쓰듯 struct 안의 함수를 쓰면 안될까요?

우리가 첫번째로 배울 class의 주요 아이디어가 바로 이것입니다. 함수도 struct안에 집어 넣기!

struct안에는 함수를 집어넣을 수 없고, C++의 새로운 기능은 class는 마치 함수마저 묶은 struct와 같은 기능을 해줍니다. 다음 소스를 봅시다.

7.03 class 맛보기
#include <iostream>
#include <cstring>

using namespace std;

class people{
public : //아직 public에 대해서는 배우지 않았습니다. 나중에 배울태니 넘어가세요.
 char name[20];
 int age;
 char phoneNumber[20];

 void view(struct a)
 {
  cout<<"["<<a.name<<"] age : "<<a.age<<", phone number : "<<a.phoneNumber<<endl;
 }
};



int main(void)
{
 char input[20]={};
 int inputAge;
 people man;

 cin>>input;
 strcpy(man.name, input);

 cin>>inputAge;
 man.age=inputAge;
 
 cin>>input;
 strcpy(man.phoneNumber, input);
 man.view();

 return 0;
}
YangHyeonSik
21
010-1234-5678
[YangHyeonSik] age : 21, phone number : 010-1234-5678

마치 struct의 변수에 접근하듯, man.view()처럼 적어 함수를 호출했습니다.

 T I P 
  public은 접근 지시자 또는 접근 제어 지시자 또는 접근 제한자등으로 불리웁니다.
  이 키워드에 대해서는 곧 배울 것이니 지금은 습관처럼 class 블록의 상단에 적어 주시기 바랍니다.
 예제 7.01번 
  2개의 (x,y)좌표를 입력 받아서 직선의 함수를 출력해줬던 문제가 기억나시나요?
  직선의 함수는 다음과 같이 구합니다.

  전달받지 못하며 0으로 초기화합니다.   상수 함수는 전달받지 않는다고 가정합니다.
  이 문제를 class를 사용하여(변수와 함수 모두 한 class 안에 위치하도록 하여) 해결해 보세요.

struct를 함수까지 포함하도록 upgrade한 것! 그것이 class입니다!

 T I P 
  class는 struct와 다르게, struct people man처럼 변수를 선언하지 않습니다. 오직 people man  !!



Access Control Specifiers[편집]

Access Control Specifiers는 접근자, 접근 제어 지시자, 접근 한정자와 같이 번역할 수 있습니다. 이 강의에서는 접근 지시자라는 말을 사용하겠습니다. 접근 지시자는 다음 3가지가 존재합니다.

  1. public
  2. protected
  3. private

우선 필요성에 대해서 C++이 지향하는 바에 대해서 생각해 보아야 합니다. C++는 class를 만들때 다음과 같은 사항을 고려하라고 프로그래머에게 요청합니다.


information hiding
정보은닉이라고 합니다.


정보은닉에 대해서 더 자세히 알아봅시다. 만약 학습자 당신이 노트북의 'A'버튼을 눌렀다고 합시다. 다음과 같은 일이 발생하겠지요.

키보드가 눌립니다 → 키보드 내부에서 전기적 신호를 받아들입니다 → 인코딩이 되어 USB로 전송됩니다 → USB를 통해 OS가 신호를 받아들입니다 →OS가 디코딩을 합니다 → stdin에 0x41이 입력됩니다 → ....

너무나도 복잡합니다! 우리가 키보드를 다룰 때 이러한 것들을 생각하고 하나요? 아니요! 우리는 단지 "A를 눌러야지"라고 생각할 뿐입니다.

왜냐하면 복잡한 작업에 대해서 알지 못해도, 노트북의 소프트웨어와 하드웨어가 자동으로 일을 처리해 줄테니까요!


이것이 정보은닉의 기본적인 개념입니다. "난 과정을 몰라. 그냥 내가 원하는 걸 하고 싶어. 복잡한 작업은 알아서 해줘"

다음 소스를 보면서 이러한 개념을 C++에 접목시켜 봅시다.


7.04 정보 은닉의 쉬운 예
#include <iostream>
#include <cstring>

using namespace std;

class complex{
public : // 무엇인지 알지 못하지만, 우선 사용해 봅시다!
 int age;
 int height;
 int weight;

 void init(void) {age=10; height=120; weight=40;}
 void ageUp(void){
  age += 1;
  height += 10;
  weight += 5;}
 void view(){cout<<"age : "<<age<<", height : "<<height<<", weight :"<<weight<<endl;}
};


int main(void)
{
 complex Frank;
 Frank.init();
 
 Frank.ageUp();
 Frank.ageUp();
 Frank.ageUp();

 Frank.view();

 Frank.ageUp();
 Frank.ageUp();

 Frank.view();


 return 0;
}
age : 13, height : 150, weight : 55
age : 15, height : 170, weight : 65

Frank라는 class complex의 변수를 만들었습니다.


 T I P 
  이제부터 class 변수를 객체라고 부르겠습니다

네, Frank라는 complex 객체를 만들었습니다. Frank는 ageUp을 통해서 나이를 먹어 갑니다. 나이, 키, 몸무게가 증가하는 군요!

하지만 우리가 직접 Frank.height+=10;처럼 쓰지 않았습니다. 직접 올려줄 필요가 없었습니다. ageUP()이라는 함수가 자동으로 올려주었기 때문이죠.

지금은 겨우 나이에 따라서 키와 몸무게만 변경되었지만, 만약 실제 몸처럼, 머리카락 길이, 손톱 길이, 팔 두께, 허리 사이즈, 눈 시력 등등 엄청나게 많은 사항이 바뀐다고 하면,

Frank.hair += 150; Frank.nail+= 20; Frank.arm.....하며 그 모든 것들에 대해 써주어야 하겠죠. 하지만 ageUP이라는 함수를 적절히 작성하면, 우리는 ageUP()이라는 함수를 사용함으로서 그 모든 작업을 끝낼 수 있습니다.

이것이 바로 정보 은닉입니다. 내부에서 정확히 어떻게 돌아가는 지는 모르겠지만, 나이를 올려준다(여러가지 작업은 알아서 해줘!)

정보 은닉을 중요시 여긴다면 보면 다음 소스는 잘못된 것입니다. 7.04 소스와 다음 소스는 거의 동일하고, 달라진 부분은 주석을 달았습니다.

7.05 정보 은닉이 중요하다면, 잘못된 소스!
#include <iostream>
#include <cstring>

using namespace std;

class complex{
public :
 int age;
 int height;
 int weight;

 void init(void) {age=10; height=120; weight=40;}
 void ageUp(void){
  age += 1;
  height += 10;
  weight += 5;}
 void view(){cout<<"age : "<<age<<", height : "<<height<<", weight :"<<weight<<endl;}
};


int main(void)
{
 complex Frank;
 Frank.init();
 
 Frank.ageUp();
 Frank.ageUp();
 Frank.ageUp();

 Frank.view();

 Frank.ageUp();
 Frank.ageUp();
 Frank.age = -50; //잘못 된 부분

 Frank.view();


 return 0;
}
age : 13, height : 150, weight : 55
age : -50, height : 170, weight : 65

Frank.age = -50이라는 문장 때문에 나이가 마이너스 50살이라는 결과가 나왔습니다. 프로그래머가 소스를 작성하는 도중에 실수한 것이 분명해 보입니다.

학습자 여러분은 이 소스에서 잘못된 부분이 어딘지 금방 알아차릴 것이고, 몇 분 안에 제대로 작동하는 소스로 고칠 수 있을 것입니다.

하지만 만약 소스가 아주아주 길어진다면, 이러한 프로그래머의 실수는 알아차리기 힘들어질 수도 있습니다.

따라서 프로그래머들은 이런 생각을 했죠. Frank의 age는 ageUP()으로 충분히 올릴 수 있다. Frank의 age를 ageUP으로만 올릴 수 있게 하면 안될까?

바로 이 것을 지원해 주는 것이 Access Control Specifiers 접근 지시자입니다. 접근 지시자는 위에서 보았듯이 3개입니다.

그중 protected는 나중에 배우는 "상속"을 배워야 알 수 있는 내용이기 때문에, 우리는 2개의 접근 지시자만 여기서 배우고 가겠습니다.

public : 외부에서 접근할 수 있습니다!

private : 내부에서만 접근할 수 있습니다!

이것만 보고서는 감이 오지 않습니다. 소스를 보면서 이해합시다. 이 소스는 "7.05 정보 은닉이 중요하다면, 잘못된 소스!"에서의 문제점 방지한 소스입니다.

7.06 접근 지시자를 사용하기
#include <iostream>
#include <cstring>

using namespace std;

class complex{
private :
 int age;
 int height;
 int weight;
public :
 void init(void) {age=10; height=120; weight=40;}
 void ageUp(void){
  age += 1;
  height += 10;
  weight += 5;}
 void view(){cout<<"age : "<<age<<", height : "<<height<<", weight :"<<weight<<endl;}
};


int main(void)
{
 complex Frank;
 Frank.init();
 
 Frank.ageUp();
 Frank.ageUp();
 Frank.ageUp();

 Frank.view();

 Frank.ageUp();
 Frank.ageUp();
 Frank.age = -50; // error 발생!

 Frank.view();


 return 0;
}

Frank 객체의 변수인 age, height, weight가 private로 설정되어 있습니다. private는 내부에서만 접근 가능! 한 접근 지시자였죠.

main함수에서 접근이 불가능한 상태인 것이죠. Frank의 public으로 설정된 다른 함수들을 통해서 간접적으로 접근해야 합니다.

변수만 private로 설정할 수 있는 것은 아닙니다. 때에 따라서는 함수도 private로 설정할 수 있습니다.

이 경우 다음 소스처럼 다른 public 함수를 사용해서 그 함수를 호출해야 겠지요.

7.07 접근 지시자, 함수에서 사용하기
#include <iostream>
#include <cstring>

using namespace std;

class complex{
private :
 int age;
 int height;
 int weight;
public :
 void init(void) {age=10; height=120; weight=40;}
 void ageUp(void){
  age += 1;
  height += 10;
  weight += 5;}
private :
 void view(){cout<<"age : "<<age<<", height : "<<height<<", weight :"<<weight<<endl;}
public :
 void Real_view() {view();}
};


int main(void)
{
 complex Frank;
 Frank.init();
 
 Frank.ageUp();
 Frank.ageUp();
 Frank.ageUp();

 Frank.Real_view(); 

 Frank.ageUp();
 Frank.ageUp();


 Frank.view(); // error 발생!


 return 0;
}

view()함수가 private이기 때문에 main함수에서 접근 할 수 없습니다.

private는 클래스 내부에서만 접근 가능! public은 클래수 외부에서도 접근 가능! 이것을 기억해 두세요.

정리[편집]

복습은, 최고의 공부법!

  1. struct
  2. class가 왜 필요하지?
  3. class, 약간 다르게 사용했어.
  4. 정보 은닉의 예, ageUP()함수와 프로그래머가 실수한 소스의 예!
  5. 이를 방지하기 위해 등장한 접근 지시자
  6. public, private
  7. 누구는 어디에서도 접근할 수 있었고, 누구는 어디에서만 접근할 수 있었지!