5 minute read

정보 은닉


정보은닉의 이해

class Point
{
    // 정보 은닉 실패
    public :
        int x; //x좌표의 범위는 0 이상 100이하
        int y; // y좌표의 범위는 0 이상 100 이하
};

class Rectangle
{
    public :
        Point upLeft;
        Point lowRight;
    public :
        void ShowRecInfo()
        {
            cout<<"up left :"<< upLeft.x << upLeft.y<<endl;
            cout<<"up left :"<< lowRight.x << lowRight.y<<endl;
        }
}

int main (void)
{
    Point pos1={-2, 4};
    Point pos2={5,9};

    Rectangle rec = {pos2, pos2};

    rec.ShowRecInfo();

    return 0;
}
  • 예제에서 보이듯이 멤버 변수의 외부 접근을 허용하면 잘못된 값이 저장되는 문제 발생
  • 따라서 멤버변수의 외부 접근 방어, 정보 은닉
  • Point 멤버 변수에는 0~100 이외의 값이 들어오는 것을 막는 장치가 없다.
  • Rectangle의 멤버 변수에는 좌우 정보가 뒤 바뀌어 저장되는 것을 막을 장치가 없다.

Point 클래스의 정보 은닉 결과

  • 클래스의 멤버변수를 private으로 선언하고, 해당 변수에 접근하는 함수를 별도로 정의해서, 안전한 형태로 멤버 변수의 접근을 유도.
class Point
{
    private :
        int x;
        int y;
    public :
        bool InitMembers(int xpos, int ypos);
        int GetX() const;
        int GetY() const;
        bool SetX(int xpos);
        bool SetY(int ypos);

};

bool Point::SetX(int xpos)
{
    if(0 > xpos || xpos > 100)
    {
        cout<<"벗어난 범위의 값 전달"<<endl;
        return false;
    }
    x = xpos;
    return true;
}

Rectangle 클래스의 정보 은닉 결과

class Rectangle
{
    private :
        Point upLeft;
        Point lowRight;

    public :
        bool InitMembers(const Point &ul, const Point &lr);
        void showRecInfo() const;
};

bool Rectangle::InitMembers(const Point &ul, const Point &lr)
{
    if(ul.GetX() > lr.GetX() || ul.GetY()>lr.GetY())
    {
        cout<<"잘못된 위치정보 전달"<<endl;
        return false;
    }

    upleft = ul;
    lowRight = lr;
    return true;   
}

const 함수

  • 멤버함수의 const 선언
// const 함수 내에서는 동일 클래스에 선언된 멤버변수의 값을 변경하지 못한다!
int GetX() const;
int GetY() const;
void ShowRecInfo() const;

// 이둘은 멤버 함수 입니다.

int GetNum()
{
    return num;
}
void ShowNum()
{
    cout<<GetNum()<<endl; // 컴파일 에러 발생
    // const 함수는 const가 아닌 함수를 호출하지못한다!
}

void InitNum(const EasyClass &easy)
{
    num = easy.GetNum(); // 컴파일 에러 발생
    // const로 상수화된 객체를 대상으로는 const 멤버함수만 호출이 가능하다

}

캡슐화


콘택 600과 캡슐화

  • 약의 복용 순서가 정해져 있다고 한다면, 캡슐화가 매우 필요한 상황
  • 캡슐화란! 관련 있는 모든 것을 하나의 클래스 안에 묶어두는 것!
class SinivelCap // 콧물 처치용 캡슐
{
    public :
        void Take() const {cout<<"콧물이 삭~ 납니다" <<endl;}
    
};

class SneezeCap // 재채기 처치용 캡슐
{
    public :
        void Take() const {cout<<"재채기가 맞습니다"<<endl;}
};

class SnuffleCap //코막힘 처치용 캡슐
{
    public :
        void Take() const {cout<<"코가 뻥 뚤립니다"<<endl;}
};

캡슐화된 콘택600

class CONTAC600
{
    private :
        // 코감기와 관련 있는 것을 하나의 클래스로 묶었다.
        SinivelCap sin;
        SneezeCap sne;
        SnuffleCap snu;
    public :
        void Take() const
        {
            sin.Take();
            sne.Take();
            snu.Take();
        }


생성자의 이해

  • 클래스의 이름과 동일한 이름의 함수이면서 반환형이 선언되지 않았고 실제로 반환하지 않는 함수를 가리켜 생성자라고한다.
class SimpleClass
{
    private :
        int num;
    public :
        SimpleClass(int n) // 생성자(constructor)
        {
            num = n;
            // 생성자는 객체 생성시 딱 한번만 호출 된다. 따라서 멤버 변수의 초기화에 사용할 수 있다.
        }
        int GetNum() const
        {
            return num;
        }
};

SimpleClass sc(20);

생성자의 함수적 특성

  • 생성자도 함수의 일종이므로 오버로딩이 가능하다.
  • 오버로딩 : 같은 이름의 메서드 여러개를 가지면서 매개변수의 유형과 개수가 다르도록 하는 기술
SimpleClass()
{
    num1 = 0;
    num2 = 0;
}

SimpleClass(int n)
{
    num1 = 0;
    num2 = 0;
}

SimpleClass(int n1, int n2)
{
    num1 = n1;
    num2 = n2;
}

SimpleClass(int n1 = 0, int n2 = 0)
{
    num1 = n1;
    num2 = n2;
}

Point, Rectangle 클래스에 생성자 적용

class Point
{
    private :
        int x;
        int y;
    
    public :
        Point(const int &xpos, const int &ypos); // 생성자
        int GetX() const;
        int GetY() const;
        bool SetX(int xpos);
        bool SetY(int ypos);
};

class Rectangle
{
    private :
        Point upLeft;
        Point lowRight;
    public :
        Rectangle(const int &x1, const int &y1, const int &x2, const int &y2);
        void ShowRecInfo() const;
};

Point::Point(const int &xpos, const int &ypos)
{
    x = xpos;
    y = ypos;
}


Rectangle::Rectangle(const int &x1, const int &y1, const int &x2, const int &y2)
            :upLeft(x1,y1), lowRight(x2,y2)
{} //객체 upLeft/lowRight 의 생성과정에서 x1/x2와 y1/y2를 인자로 전달 받는 생성자를 호출 하라.

// 이런식으로 초기화할 경우, 선언과 동시에 초기화되는 형태로 바이너리가 구성된다.
    
  • 1단계 : 메모리 공간의 할당
  • 2단계 : 이니셜라이저를 이용한 멤버변수(객체)의 초기화
  • 3단계 : 생성자의 몸체부분 실행

디폴트 생성자

  • 생성자를 정의하지 않으면 인자를 받지 않고, 하는 일이 없는 디폴트 생성자라는 것이 컴파일러에 의해서 추가된다.

  • 따라서 모든 객체는 무조건 생성자의 호출 과정을 거쳐서 완성된다.

class AAA
{
    private :
        int num;
    public :
        int GetNum {return num;}
};

-------->

class AAA
{
    private :
        int num;
    public:
        AAA(){} // 디폴트 생성자
        int GetNum {return num;}
};

소멸자의 이해


class AAA
{
    // empty class
};

---> 
class AAA
{
    public :
        AAA() {}
        ~AAA() {} // 생성자와 마찬가지로 소멸자도 정의하지 않으면 디폴트 소멸자가 삽입된다.
};

소멸자의 활용

  • 생성자에서 할당한 메모리 공간을 소멸시키기 좋은 위치가 소멸자이다.

class Person
{
    private :
        char * name;
        int age;
    public :
        Person(char * myname, int myage)
        {
            int len = strlen(myname)+1;
            name= new char[len];
            strcpy(name, myname);
            age = myage;
        }
        void ShowPersonInfo() const
        {
            cout<<"name : "<<name<<endl;
            cout<<"age : " <<age<< endl;
        }
        ~Person()
        {
            delete []name;
            cout<<"called destructor~"<<endl;
        }
};

클래스와 배열 그리고 this 포인터


객체 배열과 객체 포인터 배열

// 객체 배열, 객체로 이뤄진 배열
// 배열 생성시 객체가 함께 생성
// 이 경우 호출되는 생성자는 void 생성자

Person arr[3];
Person * parr = new Person[3];

// 객체 포인터 배열! 객체를 저장할 수 있는 포인터 변수로 이뤄진 배열
// 별도의 객체 생성 과정을 거쳐야한다.

Person * arr[3];
arr[0] = new Person(name, age);
arr[1] = new Person(name, age);
arr[2] = new Person(name, age);

this 포인터의 이해

  • this 포인터는 그 값이 결정되어 있지 않은 포인터
  • this 포인터는 this가 사용된 객체 자신의 주소값을 정보로 담고 있는 포인터 이기 때문 ```cpp class SoSimple { private : int num; public : SoSimple(int n) : num(n) { cout«num«this«endl; } void ShowSimpleData() { cout«num«endl; } SoSimple * GetThisPointer() { return this; } };

int main(void) { SoSimple sim1(100); SoSimple * ptr1 = sim1.GetThisPointer(); cout«ptr1«”, “; ptr1 -> ShowSimpleData();

SoSimple sim2(200);
SoSimple * ptr2 = sim2.GetThisPointer();
cout<<ptr2<<", ";
ptr -> ShowSimpleData();
return 0; }

// output

// num = 100, addr = 0012FF60 // 0012FF60 , 100

// num = 200, addr = 0012FF48 // 0012FF48, 100



### This 포인터의 활용

- this -> num1 은 멤버변수 num1을 의미한다.
- 객체의 주소값으로 접근할 수 있는 대상은 멤버 변수이지 지역변수가 아니기 때문이다.

```cpp
class TwoNumber
{
    private :
        int num1;
        int num2;
    public :
        TwoNumber(int num1, int num2)
        {
            this -> num1 = num2;
            this -> num2 = num2;
        }
        /// same
        TwoNumber(int num1, int num2)
            : num1(num1), num2(num2)
            {
                //empty
            }
};

Self-reference 의 변환

class SelfRef
{
    private:
        int num;
    public :
        SelfRef(int n) : num(n)
        {
            cout<<"객체 생성"<<endl;
        }
        SelfRef& Addr(int n)
        {
            num += n;
            return *this;
        }
        SelfRef& ShowTwoNumber()
        {
            cout<<num<<endl;
            return *this;
        }
};

int main(void)
{
    SelfRef obj(3);
    SelfRef &ref=obj.Adder(2);
    obj.ShowTwoNumber();
    ref.ShowTwoNumber();
    ref.Adder(1).ShowTwoNumber().Adder(2).ShowTwoNumber();
    return 0;
}


//output

// 5
// 5
// 6
// 8

Categories:

Updated: