본문 바로가기

Programming/C C++

[C, C++] Class

C와 C++의 차이중 가장 큰 것이 바로 Class개념입니다. 현재에도 Class는 java나 C#그리고 VB.NET에까지 다양한 언어에서 객체지향프로그래밍(OOP)으로 Class개념이 사용되고 있습니다.

C++에서 Class는 원래 C의 구조체를 좀더 효휼적으로 바꾸고자 해서 등장했다는 얘기가 있습니다. 실제로 정말 그런지는 확실하지 않지만 만일 C에서 구조체를 접해보신 경우라면 'C의 구조체에 변수 Member와 더불어 함수까지도 포함시킬 수 있게 한것이 Class다.' 라고 이해(꼭 그렇다는 것은 아니지만...)하시면 될듯 합니다.

다음은 C++에서 구조체를 활용한 예입니다.

#include <iostream.h>
#include <string.h>

main()
{
  struct exam{
    char name[15];
    int kuk;
    int mat;
    int eng;
  };
 
  exam student;
 
  strcpy(student.name, "kim young soo");
  student.kuk = 90;
  student.mat = 100;
  student.eng = 80;
 
  cout << "학생이름 : " << student.name << endl;
  cout << "국어점수 : " << student.kuk << endl;
  cout << "수학점수 : " << student.mat << endl;
  cout << "영어점수 : " << student.eng << endl;
 
  return 0;
}


exam구조체를 선언하고 각 변수 Member에 값을 설정한뒤 설정한 값을 확인하는 Program입니다.


Pogram에서 쓰인 구조체를 Class화 하려면 다음과 같이 바뀔 수 있습니다.

class exam{
  private:
    char name[15];
    int kuk;
    int mat;
    int eng;
 
  public:
    void name_input(char *nm);
    void memberprint();
   
    int kuk_input(int pt);
    int mat_input(int pt);
    int eng_input(int pt);
};

위 구조체를 class로 바꾼것으로서 exam을 Class로 정의하였습니다. 이 Class안에 name과 kuk, mat, eng라는 Member변수와 더불어 name_input, memberprint, kuk_input, mat_input, eng_input이라는 Member함수도 같이 존재하도록 하였습니다.

여기서 Member함수는 위 Member변수를 설정할 수 있도록 하기 위해 정의한 것입니다. (Memberprint()함수는 각 설정값을 확인하기 위한 용도로 사용하려고 합니다.)

위 Class에서 private와 public은 현재 Class에서 어떤 Member에 대해 접근 가능하게 할지 또 어떤 Member에 대해서 접근을 막을지에 대해 지정하는 것이라고 보시면 됩니다. 이때 private은 해당 Member에 대해 접근을 불가능하게 하는 Keyword이며 public는 접근을 허락하는 Keyword입니다.(protected 라는 Keyword가 있지만 이는 잠시 후 설명드리도록 하겠습니다.)

물론 Member변수에 public Keyword를 쓴다면 접근이 가능하게 할 수도 있습니다.

class exam{
  private:
    char name[15];
    int kuk;
    int mat;
    
  public:
    void name_input(char *nm);
    void memberprint();
   
    int kuk_input(int pt);
    int mat_input(int pt);
    int eng_input(int pt);

    int eng;
};


eng Member변수를 public하위에 정의하였습니다.

student.eng = 90;

public으로 정의된 eng는 직접접근이 가능합니다.

private의 경우에는 생략할 수도 있습니다. 이는 처음 Member는 private로 처리하는 것이 기본으로 되어 있기 때문에 가능한 것입니다.

class exam{
    char name[15];
    int kuk;
    int mat;
    int eng;
 
  public:
    void name_input(char *nm);
    void memberprint();
   
    int kuk_input(int pt);
    int mat_input(int pt);
    int eng_input(int pt);
};

이렇게 생략하면 다른 Keyword가 등장할때까지 모든 Member는 private로 처리됩니다.

이와 더불어 각 Keyword를 반복해서 쓸 수도 있습니다. 이는 해당 Member를 특성에 따라 Group으로 구분하고자 할때 유용하게 사용됩니다.

class exam{
  private:
    char name[15];
 
  public:
    void name_input(char *nm);
    void memberprint();
 
  public:
    int kuk_input(int pt);
    int mat_input(int pt);
    int eng_input(int pt);
   
  private:
    int kuk;
    int mat;
    int eng;
};

private과 public Keyword는 여러번 쓸 수 있습니다.

Class안의 Member변수를 private에 둘것인가 public에 둘것인가 또는 Member함수를 private에 둘것인가 public에 둘것인가 하는 문제는 어디까지나 개발자의 자유이므로 어떤형태로든지 작성될 수 있습니다.

다음은 위 Class를 실제 Program적용시킨 예입니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
    int mat;
    int eng;
 
  public:
    void name_input(char *nm);
    void memberprint();
   
    int kuk_input(int pt);
    int mat_input(int pt);
    int eng_input(int pt);
};

void exam::name_input(char *nm)
{
  strcpy(name, nm);
}

int exam::kuk_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  kuk = pt;
 
  return ptp;
}

int exam::mat_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  mat = pt;
 
  return ptp;
}

int exam::eng_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  eng = pt;
 
  return ptp;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl << endl;
  cout << "국어점수 : " << kuk << endl;
  cout << "수학점수 : " << mat << endl;
  cout << "영어점수 : " << eng << endl;
}

main()
{
  exam student;
 
  int kukp;
  int matp;
  int engp;
 
  student.name_input("youngsoo");
 
  kukp = student.kuk_input(80);
  matp = student.mat_input(91);
  engp = student.eng_input(94);
 
  student.memberprint();

  cout << "국어점수(가산) : " << kukp << endl;
  cout << "수학점수(가산) : " << matp << endl;
  cout << "영어점수(가산) : " << engp << endl;
 
  return 0;
}


먼저 Program상단에 exam이라는 Class를 정의하였습니다. 이렇게 선언된 Class를 실제 사용하기 위해서는 Class의 Object를 생성해야 하며 생성 방법은 구조체를 사용할때와 비슷합니다.

exam student;

exam Class에서 student의 Object를 생성합니다.

보시는 바와 같이 Class는 Object생성해야 사용할 수 있습니다. Class에 바로 접근할 수 없다는 얘기인데 그 이유는 어느 한쪽에서 Class Member의 값을 수정하면 다른쪽에도 영향을 받을 수 있기 때문으로 Class를 사용할때는 Class자체를 특정이름으로 복사해서 사용해야할 필요성이 있는 것입니다. 이 Class의 복사본을 생성하는 과정을 흔히 Instance라고 하며 Instance에 의해 생성된 것(여기서는 student)을 Object(객체)라고 합니다.

위 Class는 Member변수와 Member함수를 가지도록 하였고 모든 Member변수는 private을 모든 Member함수는 public Keyword를 사용하였습니다. 따라서 Class의 Member변수에는 직접적으로 접근할 수 없지만 Member함수는 직접 접근이 가능합니다.

앞서 말씀드렸듯이 Class의 Member에는 접근이 불가능하므로 구조체를 사용할때 처럼 직접 값을 설정하는 것은 불가능합니다.

student.kuk = 90;

kuk Member변수는 private에 속하므로 위와 같이 값을 설정할 수 없습니다.

Class를 제대로 사용하기 위해서는 어떻게든 값을 설정해야할 필요가 있습니다. 그래서 public하위에 값을 설정할 수 있는 함수를 정의한 것입니다.

name_input, memberprint, kuk_input, mat_input, eng_input 함수는 public로 선언되었으므로 private로 선언된 Member변수와는 달리 직접 접근이 가능합니다. 이렇게 선언된 Member함수는 실제 이 함수가 어떤 기능을 수행할지 그 본체를 구현해야 하며 이때 구현은 Class밖에서 다음과 같은 방식으로 정의됩니다.

void exam::name_input(char *nm)
{
  strcpy(name, nm);
}

int exam::kuk_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  kuk = pt;
 
  return ptp;
}


Class안에 선언된 함수는 위와 같이 그 본체를 구현합니다.

위와 같은 방식은 Class의 특정 Member변수를 설정할때 꽤 유용하게 사용될 수 있는 부분입니다. 예를 들어 국어시험에서 모든 문제를 틀리면 0점이 됩니다. 시험을 아무리 못봐도 0이하(-값)은 될 수 없는데 만일 kuk이라는 Member변수에 -100을 설정하게 된다면 이것 자체가 오류가 됩니다.

이러한 상황이 생기는 것을 막으려면 kuk_input()함수를 다음과 같이 수정해야 합니다.

int exam::kuk_input(int pt)
{
  if (pt < 0){
    cout << "점수설정에 오류가 있습니다." << endl;
    return 0;
  }
 
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  kuk = pt;
 
  return ptp;
}

pt값이 0이하이면 kuk member변수에 값을 설정할 수 없도록 합니다. 즉, 특정값에 따라 원하는 처리를 수행할 수 있도록 할 수 있는 것입니다.

또한 Class구현시 Class에서 선언된 각 Member함수와 실제 함수의 본체를 별도로 하는것이 불편하다면 다음과 같이 아예 Class안에서 모든것을 해결할 수도 있습니다.

class exam{
  private:
    char name[15];
    int kuk;
    int mat;
    int eng;
 
  public:
    void name_input(char *nm){
      strcpy(name, nm);
    }
   
    void memberprint(){
      cout << "학생이름 : " << name << endl << endl;
      cout << "국어점수 : " << kuk << endl;
      cout << "수학점수 : " << mat << endl;
      cout << "영어점수 : " << eng << endl;
    }
   
    int kuk_input(int pt){
      int ptp = pt;
 
      if (pt >= 90)
        ptp = pt + 3;
 
      kuk = pt;
 
      return ptp;
    }
   
    int mat_input(int pt){
      int ptp = pt;
 
      if (pt >= 90)
        ptp = pt + 3;
 
      mat = pt;
 
      return ptp;
    }
   
    int eng_input(int pt){
      int ptp = pt;
 
      if (pt >= 90)
        ptp = pt + 3;
 
      eng = pt;
 
      return ptp;
    }
};

Class안에서 함수구현까지 모두 처리하고 있습니다.

위 함수에서 쓰인 exam:: 부분은 exam Class의 함수를 구현하겠다는 의미가 됩니다. 이렇게 함수를 구현한뒤 구조체처럼 함수를 호출하면 실제 위와 같이 구현된 동작을 수행하게 되는 것입니다.

참고 :
int exam::kuk_input(int pt)에서 ::문자를 Scope연산자라고 합니다. Scope연산자는 다양한 역활을 수행하는데 여기에서는 Class의 Member에 접근하도록 하는 역활을 수행하고 있습니다. 이때 Scope연산자는 exam의 kuk_input member 처럼 ~의 ~ 로 해석될 수 있습니다.

student.name_input("youngsoo");
 
  kukp = student.kuk_input(80);
  matp = student.mat_input(91);
  engp = student.eng_input(94);
 
student.memberprint();

Class의 함수를 호출합니다.

Class안의 각 함수는 Class안의 Member변수에 값을 설정할 수 있도록 구현되었습니다.(memberprint()함수는 값을 확인하도록 하였습니다.) 즉, Class안의 Member함수를 통해 Member변수에 접근하고 있는 것입니다.(이는 위에서 말씀드린 Scope연산자의 의해 가능해 집니다.)

이렇게 값을 설정하고 memberprint()함수를 호출하면 각 Member변수의 값을 확인할 수 있습니다.

위 Member함수에서 name이 아닌 kuk, mat, eng member변수를 설정하는 함수를 보면

int exam::kuk_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  kuk = pt;
 
  return ptp;
}

int exam::mat_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  mat = pt;
 
  return ptp;
}

int exam::eng_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  eng = pt;
 
  return ptp;
}


pt값이 90이상일때 3을 더하도록 구현되어 있음을 알 수 있습니다. 점수로 치면 90점 이상일때 가산점 3을 더하도록 하기 위한 의도입니다. 각 함수에서는 가산점이 붙은 값을 return하고 있으며 main()은 위 세개의 함수를 호출할때 계산된 가산점을 받도록 하고 있습니다.

kukp = student.kuk_input(80);
matp = student.mat_input(91);
engp = student.eng_input(94);

이처럼 Class의 Member함수도 일반 함수처럼 void로 할 수 있고 필요하다면 특정 값을 받도록 할 수도 있습니다. Class안의 함수라고 해서 일반함수와 특별히 다를 것은 없습니다.


만일 같은 Class에서 파생된 Object가 2개상이 있다면 상호간의 Object대입을 통해서 값을 초기화 할 수도 있습니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
    int mat;
    int eng;
 
  public:
    void name_input(char *nm);
    void memberprint();
   
    int kuk_input(int pt);
    int mat_input(int pt);
    int eng_input(int pt);
};

void exam::name_input(char *nm)
{
  strcpy(name, nm);
}

int exam::kuk_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  kuk = pt;
 
  return ptp;
}

int exam::mat_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  mat = pt;
 
  return ptp;
}

int exam::eng_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  eng = pt;
 
  return ptp;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl << endl;
  cout << "국어점수 : " << kuk << endl;
  cout << "수학점수 : " << mat << endl;
  cout << "영어점수 : " << eng << endl;
}

main()
{
  exam student, student2;
 
  int kukp;
  int matp;
  int engp;
 
  student.name_input("youngsoo");
 
  kukp = student.kuk_input(80);
  matp = student.mat_input(91);
  engp = student.eng_input(94);
 
  student.memberprint();
  student2 = student;
  student2.memberprint();
 
  return 0;
}


exam Class로부터 student와 student2라는 Object를 생성하고 student object에서 값을 설정한 후 student2 Object에 student Object를 대입하였습니다.

student.memberprint();

이 후 student2 Object의 값을 확인해 보면 student Object와 값이 일치함을 알 수 있습니다.


위에서는 단일 Object를 설정하여 해당 Class에 값을 설정하였지만 Class를 통해 생성한 Object를 배열로 하면 해당 Class의 복사본을 배열길이만큼 확보할 수 있게 됩니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
    int mat;
    int eng;
 
  public:
    void name_input(char *nm);
    void memberprint();
   
    int kuk_input(int pt);
    int mat_input(int pt);
    int eng_input(int pt);
};

void exam::name_input(char *nm)
{
  strcpy(name, nm);
}

int exam::kuk_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  kuk = pt;
 
  return ptp;
}

int exam::mat_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  mat = pt;
 
  return ptp;
}

int exam::eng_input(int pt)
{
  int ptp = pt;
 
  if (pt >= 90)
    ptp = pt + 3;
 
  eng = pt;
 
  return ptp;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
  cout << "수학점수 : " << mat << endl;
  cout << "영어점수 : " << eng << endl << endl;
}

main()
{
  exam student[2];
 
  student[0].name_input("youngsoo");
  student[0].kuk_input(80);
  student[0].mat_input(91);
  student[0].eng_input(94);
 
  student[1].name_input("sooyoung");
  student[1].kuk_input(70);
  student[1].mat_input(81);
  student[1].eng_input(84);
 
  student[0].memberprint();
  student[1].memberprint();
 
  return 0;
}


exam student를 exam student[2]로 하여 2개의 배열길이만큼 Object를 생성합니다.

배열도 단일 Object와 똑같이 값을 설정하면 됩니다. [0]이나 [1]을 붙여 몇번째 배열에 Member를 호출하는지만 지정해 주면 되는 것입니다.


다만 일일이 첨자를 붙여 초기화 해주는 것이 불편하다면 문자배열을 초기화 하듯 {}를 써서 한꺼번에 초기화 할 수도 있습니다. 이는 생성자를 통해서 가능한데 자세한 내용은 생성자와 소멸자 부분을 참고해 주시기 바랍니다.

[Develop/C, C++] - [C, C++] Class구현시 생성자와 소멸자 활용

Class를 활용한 다음 Program을 봐주십시오.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
   
    int kuk_input(int pt);
};

void exam::name_input(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

main()
{
  exam student;
 
  student.name_input("youngsoo", 90);
   student.memberprint();
 
  return 0;
}



위 Program에서 Class의 name과 kuk member에 접근하기 위해 Class내에서 memberprint()함수를 public으로 선언하였습니다.

따라서 이 함수외에 다른 외부함수에서는 name과 kuk member에 접근할 수 없게 되었습니다. 왜냐하면 name과 kuk member는 class안에서 private으로 선언되었기 때문입니다.

하지만 Program안에서 private Member에 접근할 수 있는 방법이 있는데 그것은 Class안에 friend함수를 선언하는 것입니다.

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
   
    int kuk_input(int pt);
   
    friend void kukinout(exam ea);
};

위와 같이 Class안에서 friend함수를 선언하면 해당 함수는 private나 public의 적용을 받지 않습니다. 왜냐하면 friend함수는 Class의 Member가 아니기 때문입니다. 이것은 단지 Program내에서 kukinout함수가 있으면 이 함수안에서 만큼은 Class의 private Member에 접근을 허용한다라는 의미만 가지고 있을 뿐입니다. 즉, 외부함수와 Class간의 통로역활만 하고 있는 것입니다.

또한 friend함수에서 함수의 인수로 exam형의 인수(exam ea)를 지정하고 있습니다. 이는 Program안에서 kukinout함수를 작성할때 Class의 Member에 접근하기 위해 Class로 부터 파생된 Object를 전달해 줄 필요가 있기 때문입니다.

Class안에서 작성된 friend함수의 특성을 활용하기 위해서는 Program안에서 kukinout라는 똑같은 이름의 함수를 작성해야 합니다.

void kukinout(exam ea)
{
  ea.kuk = 20;
  cout << "국어점수 : " << ea.kuk << endl;
}

인수로 부터 전달받은 Object를 통해 Private Member인 kuk member의 값을 설정하고 그 값을 확인하고 있습니다.

kukinout함수는 Class가 아닌 Program안에서 쓰인 독자적 함수입니다. 따라서 함수의 원형도 선언할 수 있습니다.

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
   
    int kuk_input(int pt);
   
    friend void kukinout(exam ea);
};

void kukinout(exam ea);


함수의 원형을 Class밑에 선언하는 이유는 이 원형이 Class선언 이전에 위치하게 되면 exam이 정의되지 않았다는 오류를 일으키기 때문입니다.

이제 지금까지 말씀드린 모든 Code를 모두 조합하여 Program을 작성하면 다음과 같이 될 것입니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
   
    int kuk_input(int pt);
   
    friend void kukinout(exam ea);
};

void kukinout(exam ea);
void exam::name_input(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

main()
{
  exam student;
 
  student.name_input("youngsoo", 90);
  student.memberprint();
 
  kukinout(student);
 
  return 0;
}

void kukinout(exam ea)
{
  ea.kuk = 20;
  cout << "국어점수 : " << ea.kuk << endl;
}


kukinout(student); 로 kukinout함수를 호출합니다.


friend 함수는 Program안에서 쓰인 특정 함수에 대해서만 Class의 Member접근을 허용하는 것입니다. 하지만 Class자체를 friend화 하면 해당 Class전체에 대해서 Member접근을 허용할 수 있게 됩니다.

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
   
    int kuk_input(int pt);
   
    friend class exam2;
};

class exam2 : public exam{
  private:
    char name[15];
 
  public:
    void name_input(char *nm);
    void memberprint();
};


위와 같이 Class자체를 friend로 선언하면 friend Class는 본래 Class의 모든 Member에 접근할 수 있게 됩니다. 단, Class exam2 : public exam 처럼 본래 Class로 부터 상속받는 Class인 경우에만 가능합니다.

Class의 상속에 대한 내용은 다음 글을 참고하여 주십시오.

[Develop/C, C++] - [C, C++] Class 상속

이렇게 되면 exam2 Class에서는 본래 Class Member를 원하는 대로 다룰 수 있게 됩니다.

void exam2::memberprint()
{
  kuk = 20;
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

exam2에서 exam Class의 kuk member에 접근하여 값을 설정하고 그 값을 확인하고 있습니다.(exam2 Class에서 Member변수는 name뿐입니다.)

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
   
    int kuk_input(int pt);
   
    friend class exam2;
};

class exam2 : public exam{
  private:
    char name[15];
 
  public:
    void name_input(char *nm);
    void memberprint();
};

void exam::name_input(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

void exam2::name_input(char *nm)
{
  strcpy(name, nm);
}

void exam2::memberprint()
{
  kuk = 20;
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

main()
{
  exam student;
  exam2 student2;
 
  student.name_input("youngsoo", 90);
  student.memberprint();
 
  student2.name_input("sooyoung");
  student2.memberprint();
 
  return 0;
}



private는 Member로의 접근방지, public는 Member의 접근허용 용도로 사용됩니다. C++에서는 이와 더불어 protected라는 Keyword가 있습니다. 이것은 일반적으로는 private의 성격을 가지고 있으나 다른 Class가 해당 Class로터 상속될때에는 public성격을 가지는 특징이 있습니다.

Class의 Public는 다음과 같이 Program내에서 접근이 가능하지만

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
};

void exam::name_input(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

main()
{
  exam student;
 
  student.name_input("youngsoo", 90);
  student.memberprint();

  return 0;
}



이것을 protected로 고치면 상황은 달라집니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  protected:
    void name_input(char *nm, int i);
    void memberprint();
};

void exam::name_input(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

main()
{
  exam student;
 
  student.name_input("youngsoo", 90);
  student.memberprint();

  return 0;
}



오류발생(접근할 수 없음) Error가 출력되는데 이는 protected Member로의 접근이 오로지 상속된 Class에서만 가능하기 때문입니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  protected:
    void name_input(char *nm, int i);
    void memberprint();
};

class exam2 : public exam{
  public:
    void input();
    void print();
};

void exam::name_input(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

void exam2::input()
{
  name_input("youngsoo", 90);
}

void exam2::print()
{
  memberprint();
}

main()
{
  exam2 student;
 
  student.input();
  student.print();
 
  return 0;
}


exam Class로 부터 상속하여 exam2 Class를 생성합니다.

상속된 Class는 기존 Class에서 protected된 Member에 다음의 형태처럼 접근하는 것이 가능해 집니다.

void exam2::input()
{
  name_input("youngsoo", 90);
}

void exam2::print()
{
  memberprint();
}


상속된 exam2 Class에서 exam Class의 protected Member인 name_input과 memprint Member에 접근하고 있습니다.


위의 friend설명부분에서 쓰인 memberprint()함수를 다시 한번 살펴보겠습니다.

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl << endl;
  cout << "국어점수 : " << kuk << endl;
  cout << "수학점수 : " << mat << endl;
  cout << "영어점수 : " << eng << endl;
}

이 함수에서는 exam Class의 name, kuk, mat, eng member변수의 값을 확인하도록 하고 있습니다. 그런데 만일 이 함수안에 어떠한 이유로 인하여 kuk이라는 변수가 새로 선언되었을때 결과는 어떻게 될까요?

void exam::memberprint()
{
  int kuk = 0;
 
  cout << "학생이름 : " << name << endl << endl;
  cout << "국어점수 : " << kuk << endl;
  cout << "수학점수 : " << mat << endl;
  cout << "영어점수 : " << eng << endl;
}

memberprint()함수안에 kuk이라는 변수를 선언하고 초기값을 0으로 합니다.


Program실행결과가 0으로 나왔습니다. 이는 Class의 kuk Member가 아니라 함수안에서 쓰인 kuk변수값을 가져오고 있기 때문입니다.

이렇게 혼동되는 문제를 해결하는 방법에는 두가지가 있는데 하나는 해당 Member에 Class를 지정해 주는 것이며

cout << "국어점수 : " << exam::kuk << endl;

다른 하나는 This Pointer를 지정하는 것입니다.

cout << "국어점수 : " << this->kuk << endl;

이 This Pointer는 이름에서도 알 수 있듯이 주소값을 갖는 Pointer로서 해당 주소는 다름아닌 Class에서 생성된 Object의 주소를 갖고 있는 것입니다.

결국

exam student;

student.memberprint();


이와같이 하였을때 This에는 student Object의 주소가 담겨져 전달되기 때문에 Class의 kuk Member값을 가져오게 됩니다.

방금전 This Pointer를 사용해 보았는데 이것으로 알 수 있는것은 Class의 Member에 접근하는데 Object를 통해서만 아니라 Pointer를 통해 접근할 수도 있다는 것입니다.

아래 예제는 위에서 다룬 Program에서 점수부분은 빼고 이름만 설정/확인할 수 있도록 간소화한 예제입니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
 
  public:
    void name_input(char *nm);
    void memberprint();
};

void exam::name_input(char *nm)
{
  strcpy(name, nm);
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
}

main()
{
  exam student;
 
  exam *p = &student;
 
  student.name_input("youngsoo");
  student.memberprint();
 
  p->memberprint();
 
  return 0;
}


student의 Object주소값을 exam형 Class의 Pointer에 전달하고 이 Pointer를 통하여 Class의 memberprint() Member함수에 접근하고 있습니다.

Object의 주소값을 Pointer에 넘기려면 해당 Pointer가 Object가 생성된 Class형으로 선언되어야 하며 이 Pointer에 & 연산자를 통하여 Object의 주소값만 넘겨주기만 하면 됩니다.

이 후 Object의 주소를 전달받은 Pointer는 -> 연산자를 통해 Class의 Member에 접근하도록 합니다.


Class로부터 생성된 Object를 Const로 선언하면 해당 Object에서는 각 Member에 접근할 수 없는 상황이 발생합니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    void name_input(char *nm, int i);
    void memberprint();
};

void exam::name_input(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint()
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

main()
{
  const exam student;

  student.name_input("youngsoo", 90);
  student.memberprint();
 
  return 0;
}


exam Class의 student Object를 const로 선언하였습니다.


잘 아시는 바와 같이 const는 상수입니다. 즉, 값을 바꿀 수 없는 것이기 때문에 Member접근 자체가 차단되는 것입니다.

값을 설정하는 name_input member함수는 그렇다 치고 단순히 값을 조회하는 memberprint()함수까지 차단되는 이유는 뭘까요? 이는 C++ Compiler가 어떤 함수에서 값을 변경하고 또 어떤 함수에서 값을 변경하지 않는지까지는 판단하지 않기 때문입니다.

즉, 값을 변경하는 부분과 값을 변경하지 않는 부분을 구분하지 않기 때문에 아예 모두 틀어막아 버릴 수 밖에 없게 되는 것이죠.

이렇게 되면 값을 확인할 수도 없어서 const로 설정하는 것 자체가 거의 무의하게 되지만 값을 바꾸지 않는 Class의 Member함수에 한하여 const로 선언하게 되면 해당 함수는 사용을 가능하게 할 수도 있습니다.

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    exam(char *nm, int i);
    void memberprint() const;
};

Class에서 memberprint() Member함수에 const선언하였습니다. memberprint()는 값을 설정하는 부분이 없고 오로지 값을 확인하는 부분만 존재합니다.

또한 해당 Class의 각 Member에 초기값을 설정하기 위해 exam생성자를 선언하였습니다. 만일 생성자가 아닌 기존에서 처럼

void name_input(char *nm, int i);

하여 Member함수로 값을 설정하려고 하면 해당 Object생성시 const로 했을때 값을 설정하는 Member로의 접근이 차단 되므로 초기값을 설정하는 것 자체가 불가능하게 됩니다.

생성자에 대해서는 다음글을 참고하여 주십시오.

[Develop/C, C++] - [C, C++] Class구현시 생성자와 소멸자 활용

Class에서 memberprint()함수에 const를 선언하고 나면 해당 함수의 정의부분에서도 const를 추가해야 합니다.
void exam::memberprint() const
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

함수 정의 마지막 부분에 const구문을 추가하였습니다.

만일 Class안에서 선언과 정의가 동시에 이루어 지는 경우라면 다음과 작성할 수도 있습니다.

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    exam(char *nm, int i);
    void memberprint() const {
      cout << "학생이름 : " << name << endl;
      cout << "국어점수 : " << kuk << endl;
    }
};

마지막으로 선언된 생성자에 대한 정의를 작성한 후

exam::exam(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

초기화를 수행하는 부분을 추가하면 다음과 같이 될것입니다.

main()
{
  const exam student("youngsoo", 90);
  student.memberprint();
 
  return 0;
}

student Object생성과 동시에 초기화 합니다. 이때 const선언도 같이 이루어 지도록 합니다.

위와 같은 내용으로 Program을 완성시켜 보겠습니다.

#include <iostream.h>
#include <string.h>

class exam{
  private:
    char name[15];
    int kuk;
 
  public:
    exam(char *nm, int i);
    void memberprint() const;
};

exam::exam(char *nm, int i)
{
  strcpy(name, nm);
  kuk = i;
}

void exam::memberprint() const
{
  cout << "학생이름 : " << name << endl;
  cout << "국어점수 : " << kuk << endl;
}

main()
{
  const exam student("youngsoo", 90);
  student.memberprint();
 
  return 0;
}


const에 대한 Member함수 접근


memberprint()는 const선언되었으므로 정상적인 처리 가능

'Programming > C C++' 카테고리의 다른 글

표준 Library 함수 - Memory 관련 함수  (0) 2011.06.16
[C, C++] Class구현시 생성자와 소멸자 활용  (0) 2011.06.01
[C, C++] Class  (0) 2011.05.25
[C, C++] cout 서식지정  (0) 2011.04.14
[C, C++] 구조체  (1) 2011.04.13
[C, C++] 변수선언및 초기화  (0) 2011.04.07