정보은닉 


우리는 정보은닉에 대한 이야기를 진행하고자 한다. 


class Point

{

public :

int x ;

int y ;

};


위의 클래스에서 멤버변수 x와 y의 범위는 0이상 100 이하이고, 좌 상단좌표가 [ 0 , 0 ] 

우 하단의 좌표가 [ 100 , 100 ]이라고 가정하자. 그리고 다음의 예를 관찰하자.

이 예제는 Point 클래스의 멤버변수가 public으로 선언되었을때 발생할 수 있는 문제점을 보인다.


  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class Point  
  5. {  
  6. public:  
  7.     int x;  
  8.     int y;  
  9. };  
  10.   
  11. class Rectangle  
  12. {  
  13. public:  
  14.     Point upLeft;  
  15.     Point lowRight;  
  16.   
  17. public:  
  18.     void ShowRecInfo()  
  19.     {  
  20.         cout << "up Left   : " << '[' << upLeft.x << ", ";  
  21.         cout << upLeft.y << ']' << endl;  
  22.         cout << "low Right : " << '[' << lowRight.x << ", ";  
  23.         cout << lowRight.y << ']' << endl << endl;  
  24.     }  
  25. };  
  26.   
  27. int main()  
  28. {  
  29.     Point pos1 = { -2,4 };  
  30.     Point pos2 = { 5,9 };  
  31.     Rectangle rec = { pos2,pos1 };  
  32.     rec.ShowRecInfo();  
  33.     return 0;  
  34. }  



[ 결과 ]


  • 4행: 점을 표현한 Point 클래스의 멤버변수 x, y는 public으로 선언되어 있어서 어디서든 접근이 가능하다.
  • 11행: Rectangle 클래스는 직사각형을 표현한 것이다. 그런데 직사각형은 두 개의 점으로 표현이 가능하므로, 두 개의 Point 객체를 멤버로 두었다. 이렇듯 클래스의 멤버로 객체를 둘 수 있다.
  • 29~30행: 직사각형을 표현할 두 개의 Point 객체를 생성하였다. 이렇듯 멤버변수가 public으로 선언되면, 구조체 변수를 초기화하듯이 초기화가 가능하다.
  • 31행: 위에서 생성한 Point 객체를 이용해서 Rectangle 객체를 생성 및 초기화하고있다.


위의 예제에서 어떠한 문제가 있는지 지적해보자.

  • 점의 좌표는 0이상 100이하가 되어야 하는데, 그렇지 못한 Point 객체가 있다.
  • 직사각형을 의미하는 Rectangle 객체의 좌 상단 좌표 값이 우 하단 좌표 값보다 크다.

점의 좌표는 0이상 100이하가 되어야 유효하다. 하지만 위의 예제는 이를 지키지 않았다. 이러한 문제는 프로그래머의 실수에서 발생하는 경우가 대부분이다. 뿐만 아니라, 좌 상단의 좌표 값이 우 하단의 좌표 값보다 큰 것으로 보아 두 좌표값이 서로 바뀐듯 하다. 에러가 나지 않은 이유는 문법적으로 문제가 없기 때문에 컴파일러는 위의 코드를 문제삼지 않기 때문이다. 때문에 제한된 방법으로의 접근만 허용을 해서 잘못된 값이 저장되지 않도록 도와야 한다.


다음의 변경된 Point 클래스를 보자.


point.h


  1. #ifndef __POINT_H_  
  2. #define __POINT_H_  
  3.   
  4. class Point  
  5. {  
  6. private:  
  7.     int x;  
  8.     int y;  
  9.   
  10. public:  
  11.     bool InitMembers(int xpos, int ypos);  
  12.     int Getx() const;  
  13.     int Gety() const;  
  14.     bool SetX(int xpos);  
  15.     bool SetY(int ypos);  
  16. };  
  17. #endif  


먼저 멤버변수 x와 y를 private으로 선언하여 임의로 값이 저장되는 것을 막아놓았다.

즉, x와 y라는 정보를 은닉한 상황이다. 대신에 값의 저장 및 참조를 위한 함수를 추가로 정의하였다.

따라서 이 함수 내에서 맴버변수에 저장되는 값을 제한할 수 있다.


point.cpp


  1. #include <iostream>  
  2. #include "Point.h"  
  3. using namespace std;  
  4.   
  5. bool Point::InitMembers(int xpos, int ypos)  
  6. {  
  7.     if (xpos < 0 || ypos < 0)  
  8.     {  
  9.         cout << "Pass out of range values" << endl;  
  10.         return false;  
  11.     }  
  12.   
  13.     x = xpos;  
  14.     y = ypos;  
  15.     return true;  
  16. }  
  17.   
  18. int Point::Getx() const  
  19. {  
  20.     return x;  
  21. }  
  22.   
  23. int Point::Gety() const  
  24. {  
  25.     return y;  
  26. }  
  27.   
  28. bool Point::SetX(int xpos)  
  29. {  
  30.     if (0 > xpos || xpos > 100)  
  31.     {  
  32.         cout << "Pass out of range values" << endl;  
  33.         return false;  
  34.     }  
  35.     x = xpos;  
  36.     return true;  
  37. }  
  38. bool Point::SetY(int ypos)   
  39. {  
  40.     if (0 > ypos || ypos > 100)  
  41.     {  
  42.         cout << "Pass out of range values" << endl;  
  43.         return false;  
  44.     }  
  45.     y = ypos;  
  46.     return true;  
  47. }  


먼저, 멤버변수에 값을 저장하는 함수 InitMembers, SetX, SetY는 0이상 100 이하의 값이 전달되지 않으면, 에러 메세지를 출력하면서 값의 저장을 허용하지 않는 형태로 정의되었다. 따라서 잘못된 값이 저장되지 않을뿐더러, 값이 잘못 전달되는 경우 출력된 메세지를 통해서 문제가 있음을 확인할 수 있다. 


이들을 바탕으로 다음과 같은 결론을 내릴 수 있다.


멤버변수를 private로 선언하고, 해당 변수에 접근하는 함수를 별도로 정의해서, 안전한 형태로 멤버변수의 접근을 유도하는 것이 바로 '정보은닉'이며, 이는 좋은 클래스가 되기 위한 기본조건이다.


위의 코드를 보면 변수의 이름이 XXX 일때

다음과 같이 함수의 이름이 GetXXX, SetXXX로 정의된 함수들을 볼 수 있다.


int GetX( ) const ;

bool SetX( int xpos ) ;


int GetY( ) const ;

bool SetY( int ypos ) ;


이들을 가리켜 '엑세스 함수(access function)'라 하는데, 이들은 멤버변수를 

private으로 선언하면서 클래스 외부에서의 멤버변수 접근을 목적으로 정의되는 함수들이다.


그럼 이어서 정보가 은닉된 Rectangle 클래스를 다음과 같이 제시할 수 있다.


Rectangle.h


  1. #ifndef __RECTANGLE_H_  
  2. #define __RECTANGLE_H_  
  3.   
  4. #include "Point.h"  
  5.   
  6. class Rectangle  
  7. {  
  8. private:  
  9.     Point upLeft;  
  10.     Point lowRight;  
  11.   
  12. public:  
  13.     bool InitMembers(const Point &ul, const Point &lr);  
  14.     void ShowRecInfo() const;  
  15. };  
  16. #endif  


Rectangle클래스도 멤버변수를 private으로 선언하고, 멤버의 초기화를 위한 별도의 함수를 추가하였다. 이 함수 내에서는 좌 상단과 우 하단의 좌표가 뒤바뀌는 것을 검사하는 내용이 담겨있다. 


그럼 어떻게 검사를 진행하는지 직접 확인해보자.


Rectangle.cpp


  1. #include <iostream>  
  2. #include "Rectangle.h"  
  3. using namespace std;  
  4.   
  5. bool Rectangle::InitMembers(const Point &ul, const Point &lr)  
  6. {  
  7.     if (ul.GetX() > lr.GetY || ul.GetY() > lr.GetX())  
  8.     {  
  9.         cout << "Pass out of range values" << endl;  
  10.         return false;  
  11.     }  
  12.     upLeft = ul;  
  13.     lowRight = lr;  
  14.     return true;  
  15. }  
  16.   
  17. void Rectangle::ShowRecInfo() const  
  18. {  
  19.     cout << "up Left   : " << '[' << upLeft.GetX() << ", ";  
  20.     cout << upLeft.GetY() << ']' << endl;  
  21.     cout << "low Right : " << '[' << lowRight.GetX() << ", ";  
  22.     cout << lowRight.GetY() << ']' << endl << endl;  
  23. }  


마지막으로 위의 클래스들을 대상으로 정의된 main 함수를 정의하여 코드를 완성해보자.


  1. #include <iostream>  
  2. #include "Point.h"  
  3. #include "Rectangle.h"  
  4. using namespace std;  
  5.   
  6. bool Point::InitMembers(int xpos, int ypos)  
  7. {  
  8.     if (xpos < 0 || ypos < 0)  
  9.     {  
  10.         cout << "Pass out of range values" << endl;  
  11.         return false;  
  12.     }  
  13.   
  14.     x = xpos;  
  15.     y = ypos;  
  16.     return true;  
  17. }  
  18.   
  19. int Point::GetX() const  
  20. {  
  21.     return x;  
  22. }  
  23.   
  24. int Point::GetY() const  
  25. {  
  26.     return y;  
  27. }  
  28.   
  29. bool Point::SetX(int xpos)  
  30. {  
  31.     if (0 > xpos || xpos > 100)  
  32.     {  
  33.         cout << "Pass out of range values" << endl;  
  34.         return false;  
  35.     }  
  36.     x = xpos;  
  37.     return true;  
  38. }  
  39. bool Point::SetY(int ypos)   
  40. {  
  41.     if (0 > ypos || ypos > 100)  
  42.     {  
  43.         cout << "Pass out of range values" << endl;  
  44.         return false;  
  45.     }  
  46.     y = ypos;  
  47.     return true;  
  48. }  
  49. bool Rectangle::InitMembers(const Point &ul, const Point &lr)  
  50. {  
  51.     if (ul.GetX() > lr.GetX() || ul.GetY() > lr.GetY())  
  52.     {  
  53.         cout << "Pass out of range values" << endl;  
  54.         return false;  
  55.     }  
  56.     upLeft = ul;  
  57.     lowRight = lr;  
  58.     return true;  
  59. }  
  60.   
  61. void Rectangle::ShowRecInfo() const  
  62. {  
  63.     cout << "up Left   : " << '[' << upLeft.GetX() << ", ";  
  64.     cout << upLeft.GetY() << ']' << endl;  
  65.     cout << "low Right : " << '[' << lowRight.GetX() << ", ";  
  66.     cout << lowRight.GetY() << ']' << endl << endl;  
  67. }  
  68.   
  69. int main()  
  70. {  
  71.     Point pos1;  
  72.     if (!pos1.InitMembers(-2, 4))  
  73.         cout << "Init fail" << endl;  
  74.       
  75.     if (!pos1.InitMembers(2, 4))  
  76.         cout << "Init fail" << endl;  
  77.   
  78.     Point pos2;  
  79.     if (!pos2.InitMembers(5, 9))  
  80.         cout << "Init fail" << endl;  
  81.   
  82.     Rectangle rec;  
  83.     if (!rec.InitMembers(pos2, pos1))  
  84.         cout << "Rectangle Init fail" << endl;  
  85.   
  86.     if (!rec.InitMembers(pos1, pos2))  
  87.         cout << "Rectangle Init fail" << endl;  
  88.   
  89.     rec.ShowRecInfo();  
  90.     return 0;  
  91. }  


[ 결과 ]




const 함수


앞서 보인 예제 Point.h와 Rectangle.h에 선언된 다음 함수들에는 const 선언이 추가되어 있다.


  1. int GetX() const;  
  2. int GetY() const;  
  3. void ShowRecInfo() const;  


이 const는 다음 내용을 선언하는 것이다.


"이 함수 내에서는 멤버변수에 저장된 값을 변경하지 않겠다!"


매개변수도 아니고, 지역변수도 아닌, 멤버변수에 저장된 값을 변경하지 않겠다는 선언이다. 따라서 const 선언이 추가된 멤버함수 내에서 멤버변수의 값을 변경하는 코드가 삽입되면, 컴파일 에러가 발생한다. 이렇게 함수를 const로 선언하면, 실수로 자신의 의도와 다르게 멤버변수의 값을 변경했을 때, 컴파일 에러를 통해서 확인할 수 있다. 따라서 프로그래머의 실수를 최소화 할 수있다. 다음의 예제를 통해 특징들을 알아보자.


  1. class SimpleClass  
  2. {  
  3. private:  
  4.     int numl  
  5.   
  6. public:  
  7.     void InitNum(int n)  
  8.     {  
  9.         num = n;  
  10.     }  
  11.     int GetNum()  
  12.     {  
  13.         return num;  
  14.     }  
  15.     void ShowNum() const  
  16.     {  
  17.         cout << GetNum() << endl;  
  18.     }  
  19. };  


위의 클래스 정의에서 ShowNum 함수는 const 함수로 선언되었다. 그리고 실제로 함수 내에서는 멤버변수 num의 값을 변경하지 않는다. 그럼에도 불구하고 GetNum 함수를 호출하는 문장에서 컴파일 에러가 발생한다. 그 이유는 const 함수 내에서 const가 아닌 함수의 호출이 제한되기 때문이다. const로 선언되지 않은 함수는 아무리 멤버변수에 저장된 값을 변경하지 않더라도 변경할 수 있는 능력을 지닌 함수이다.



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

C++ 기본(4) 객체지향 프로그래밍의 이해  (0) 2017.01.03
C++ 기본(3) 클래스(Class)와 객체(Object)  (0) 2016.12.28
C++ 기본(2) - 구조체  (0) 2016.12.23
8. C++ 기본(8)  (0) 2016.12.14
7. C++ 기본(7)  (0) 2016.12.13

+ Recent posts