ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • C# 캡슐화, 정보 은닉, 상속, 클래스 형변환 (교육 11일차) 메모리 주소확인
    C# 2020. 6. 1. 18:32

    생성자

    Person이라는 생성자가 3개가 있는데, person1이 new를 통해 생성될 때, 가장 먼저 스태틱 생성자가 실행 도중 가장 먼저 단 한 번만 호출되고, 디폴트 생성자가 실행된다. 다음으로 person2가 실행될 때 오버 로딩을 통해서 Person("")으로 문자열 매개변수를 줬기 때문에 일반 생성자로 생성된다.

     

    캡슐화

    관련성 있는 데이터와 그 데이터를 다루는 메서드를 객체 안에 구현하는 것이 일반적인 통념이고, 더 나아가서는 객체의 밖에서 알아야 할 필요가 없는 내부 멤버를 숨기기도 하는데, 이를 두고 캡슐화(encapsulation)라는 용어를 사용한다.

     

    책에는 원의 넓이를 구하는 예제를 통해서 캡슐화를 설명하는데, 원의 넓이를 구하는 클래스가 있다고 가정할 때, 접근제한자의 pulic을 명시하지 않음으로써 클래스 외부의 사용자가 원주율을 바꾼다거나, 원의 넓이를 구하는 공식을 마음대로 수정하는 일을 방지하고, public으로 접근 가능한 출력 메서드로 사용자가 접근해서 원의 넓이를 구하는 메서드를 접근하는 2중 구조를 하고 있다.

     

    class Circle

    {

       double pi = 3.14;

       double GetArea(double radius)

       {

            return radius * radius * pi

       }

     

       public void Print(double value)

       {

           Console.WriteLine(GetArea(value));   //여기서 위의 원의 넓이를 구하는 공식에 접근 

        }                                                 

    }

     

    Circle o = new Circle();

    o.pi=6.28; //public이 아니므로 수정불가

    o.GetArea(10); // public 이 아니므로 메서드에 접근 불가

    o.Print(10); //value=10에 접근해서 그 안의 내부 메서드에 다시 접근해서 결과를 출력한다.

     

    정보 은닉

    객체지향에서 캡슐화를 다룰 때면 언제나 정보 은닉(information hiding)이라는 개념이 함께 등장한다. 클래스 입장에서 "정보"라고 불리는 것은 멤버 변수를 일컫는데, 외부에서 이 멤버 변수를 직접 접근할 수 없게 만드는 것이 정보 은닉에 해당한다. 외부에서 그 변수에 접근할 때는 Get, Set이라는 단어를 각각 사용한다. get/set 기능을 하는 메서드를 특별히 접근자 메서드, 설정자 메서드라고 한다. 

     

    그렇다면 pi를 그냥 public으로 해주면 되지 않을까 하는 의문이 생긴다. 필드의 값을 읽고 쓰기만 하면 아무 지장이 없지만, 위의 예제와 같이 프로그래밍한다면 유지보수에서 오류를 더 쉽게 찾을 수 있다. 만약 pi의 값이 유효한 범위에서 벗어나는 경우 일반적으로 public을 사용해서 변수를 설정해야 한다면 그 필드를 사용하는 곳을 전부 찾아야 하지만 반대로 정보 은닉을 통한다면 조건문을 사용해서 오류를 발견할 수 있다. 

     

    public double SetPi(double value)

    {

        if (value>5 || value<0)

        {

          Console.WriteLine("오류 발생");

        }

        else

         {

           pi=value;

         }

    }

     

    위의 코드와 같이 바꿔주면 만약 5보다 크거나 0보다 작다면 오류가 발생함으로써 쉽게 발견 및 수정이 가능하다.

     

    정보 은닉의 원칙

    - 특별한 이유를 제외하고는 필드를 절대 public으로 선언하지 않는다.

    - 접근이 필요할 때는 접근자. 설정자 메서드를 만들어 외부에서 접근하는 경로를 클래스 개발자의 관리하에 둔다.

     

    프로퍼티

    get set 메서드를 일일이 작성하기 어려우므로 C#에서만 제공하는 간단한 문법이 있다.

    위 예제를 보면 프로퍼티를 사용해 Get과 Set 프로퍼티를 사용해서 Get과 Set 역할을 하는 메서드를 더욱 간편하게 표현했다. 여기서 주의할 점은 value는 예약어라는 것이다.  프로퍼티에 대입될 값을 가리키는 변수가 없으므로 도입되었다. Set 내부에서만 사용이 가능하다. 우리의 눈에는 편리하게 보이는데 컴파일을 하게 될 경우, 앞서 보았던 Get Set메서드로 변환된다. 결과적으로 컴파일해 나오는 코드는 동일하다.

     

    class 클래스명

    {

        접근_제한자 타입 프로퍼티명

        {

          접근 제한자  get

           {

           // 코드

           //return 프로퍼티의_타입과_일치하는_유형의_표현식

           }

           접근_제한자 set

            {

             //코드

             //value라는 문맥 키워드를 사용해 설정하려는 값을 표현

            }

         }

     }

     

    상속

    노트북, 데스크톱, 넷북이라는 클래스가 있을 때 3개의 클래스가 공통적으로 가지는 기능들이 있다. 예를 들어 컴퓨터 켜기, 컴퓨터 끄기, 컴퓨터 재부팅 등등이 있을 것이다. 각각의 클래스의 공통적으로 쓰이는 부분들을 하나의 클래스로 정의해서 노트북 데스크톱 넷북에 각각 상속시켜준다면 일일이 클래스 안에 컴퓨터 끄기, 컴퓨터 켜기 등등을 안 적어도 될 것이다. 이러한 공통적인 특징을 정의하는 부모 클래스(Parent Class)를 두고 자식(Child Class)에서 부모의 기능을 물려받는 식으로 처리할 수 있다.

    간단하게 말하면 기존 클래스의 기능을 당겨오기이다. 기존의 클래스에서 물려받은 기능을 더 발전시킬 수 있다. 코드의 재사용률이 높아진다.

     

    결과

    어차피 같은 메서드를 굳이 모든 클래스에 적을 필요가 없다. 따라서 상속을 이용해보자.

    상속은 기호 " : "을 사용해서 상속 받는다. C++에서와 같다. 상속받고 싶은 클래스 옆에 " : " 을 적고 오른쪽에 상속하고 싶은 클래스를 명시한다. 가지는 장점으로 부모 클래스의 기능은 전부 유전된다. 코드의 중복을 방지한다.

    class 나 : 엄마

    {

    }

     

    밑의 방법이 결과는 똑같고 훨씬 간단하다. 만약 공통적으로 사용되는 항목 외에 추가적으로 필요한 항목이 있다면 추가로 적어주면 된다. 만약 class 안에서 상속받은 메서드를 사용하면, 먼저 class 내부에서 찾고 없을 경우 부모 클래스로 가서 찾는다.  "아버지 뭐하시니" 

     

    여기서 접근 제한자가 다시 나온다. 접근 제한자를 아무것도 안 적을 경우 기본값으로 Private을 가지고 있다. private 접근 제한자가 적용된 멤버는 오직 그것을 소유한 클래스에서만 접근할 수 있다. 따라서 자식 클래스는 부모 클래스가 private 접근 제한자를 가질 경우 접근할 수 없다.

     

    위의 예제에서 Notebook 클래스를 바꿔보자.

     

    public class Notebook : Computer

    {

          bool fingerScan;

          public bool HasFingerScanDevice() { return fingerScan;}

     

          public void CloseLid()

          {

                if (PowerOn == true) //컴파일 오류 발생 : 접근 불가(inaccessible)

                {

                        Shutdown();

                }

            }

     }

     

    PowerOn은 부모 클래스에서 정의된 Private의 접근 제한자를 가진 변수이므로, 자식 클래스에서는 접근 불가 메세지가 뜬다. 여기서 사용될 목적으로 추가된 접근 제한자가 protected이다. 위의 코드에서 부모 클래스에서 PowerOn 변수의 접근 제한자를 protected로 명시한다면 자식클래스에서 접근하는 것이 가능해진다, 자식 클래스에서 에러 뜨면 부모 클래스 가서 변수를 protected 적어보자.

     

     상속을 의도적으로 막고 싶으면 클래스 앞에 sealed를 붙여주면 된다. C#에만 있다. 다른 언어는 생성자를 private으로 생성한다던지 하는 방법을 사용한다.

     

    sealed class Computer

    {

     

    }

    class notebook : Computer //에러발생(상속 불가)

    {

     

    }

    상속은 단일 상속만 가능하다. 예를 들어 notebook클래스에 Computer와 netbook을 상속하고 싶을 때는 Computer를 notebook에 상속하고 그다음 notebook 클래스에 상속해야 한다.

     

    형 변환

    지금 까지는 기본 자료형의 형 변환이라면, 지금은 객체의 형변환이다. 위의 예제에서 Computer는 가장 일반적인 개념으로 그 범위는 Computer를 상속받은 Notebook도 포함한다. Notebook은 Computer의 특수화 타입인 것이다. 따라서 Notebook(특수화 타입) 인스턴스를 Computer(일반화 타입)의 변수로 대입하는 경우에는 암시적 형변환이 가능하지만, 반대로 부모 클래스(일반화 타입)의 인스턴스를 자식 클래스(특수화 타입)의 변수로 대입하는 것인 암시적 변환이 불가능하다. 

    (Computer Type)
    -> (Notebook Type)
    -> Mynotebook
    -------------------------------------
                                              |

                                              |

                                              | ->

    ---------------------------------------->


    Computer
         ↓
    NoteBook
      NoteBook
    (Computer Type)
    -> MyComputer
    Computer
       

    Notebook noteBook = new Notebook();

    Computer pc1=notebook;                    //암시적 형변환

     

    Computer pc = new Computer();

    Notebook notebook = (Notebook)pc;    // 명시적 형 변환 컴파일만 가능

     

    컴파일은 가능하지만 Notebook 안에 있는 기능을 쓰려고 할 때, 문제가 생긴다. Notebook에는 Computer가 정의하지 않은 멤버가 있기 때문에.

     

    컴퓨터를 노트북으로 (ㅆㄱㄴ)

    노트북을 컴퓨터로  (ㅂㄱㄴ)

    ->다른 컴퓨터 종류는 없고 노트북만이 가지는 기능(터치패드)이 있다

    ->컴파일은 되지만 실행을 하면 에러.

                Notebook MyNoteBook = new Notebook(); //
                Computer MyComputer = MyNoteBook;

                Computer pc1 = new Computer();
                Notebook nb1 = (Notebook)pc1;

    에 대한 그림이다. 각각의 변수에 들어가는 것은 주소이다. 

     

                Notebook notebook = new NoteBook();

                Computer pc1          = new pc1();

     

                NoteBook note2 = (NoteBooK)pc1;

    아무 문제 없다.

                Computer pc1 = new NoteBook();

                NoteBook note2 = (NoteBook)pc1;

    역시 아무 문제 없다

     

    주로 사용 용도는, 인간을 중국, 미국, 한국 인간이란 변수로 z를 만들면 모든 나라를 다 가리킬 수 있다. 인간만 있으면 객체란 객체는 다 가리킬 수 있다. 상속받고 받고 받고 하면 최상위 즉 조상 참조 변수만 있으면 조상급으로 z를 만들면 2번째 조상 3번째 조상 전부 가리킬 수 있고 조상으로부터 상속된 다른 조상도 가리킬 수 있다. 그러면 만능 객체가 된다.  상위만 거느리면 하위를 다 거느릴 수 있다. 자바에서는 Object 클래스라고 부른다. 

     

    상위 개념을 이용한 유연한 프로그래밍

             

               Computer[] machines;
                machines = new Computer[3];
                machines[0] = new Notebook();
                machines[1] = new Notebook();
                machines[2] = new Notebook();

    이렇게 해도 똑같다, 배열 객체를 만들려면 힘들다. 

     

    foreach(Computer Temp in machines) machines 배열에서 하나를 추출해서 Temp에 넣어준다. 차례대로.
    {

         manage.TrunOff(device)

    }

     

     

     

    (1) Computer type으로 배열 선언 이름은 machines 안에 값은 null  : 배열 객체를 가르키는 참조변수

    (2) ->> new Computer[] 객체 생성 (new 개수만큼) 3개                 : Computer 타입의 배열 객체

    (3) ->> 노트북 객체 생성 [0] , 데스크톱 객체 생성 [1] , 넷북 객체 생성 [2]

    ********** machines[0].Reset()  으로 호출 가능 : Notebook.Reset();

    ********** machines[1].Reset()  으로 호출 가능 : Desktop.Reset();

    ********** machines[2].Reset()  으로 호출 가능 : Netbook.Reset();

    각 자식 클래스의 인스턴스를 부모 객체의 배열에 담을 수 있는 것도 암시적 형 변환 덕분이다.

     

     

    as, is 연산자

     

    해당 변수의 자료형이 is 뒤에 적힌 자료형과 일치하느냐 안하느냐에 따라서 boolean형으로 반환한다. n is string은 거짓이므로 위의 조건문에 해당한다.

     

     

     

    ※ 실행할 때 메모리 주소 확인하기

    중단점 설정 (줄 클릭) F9 -> 한 줄 컴파일 F11 -> iNum복사 -> 조사식 1에 iNum 앞에 & 입력 -> 값 항목에 문자를 복사

    ->

    이름값 형식

    &iNum 0x00d9f278 int*

    위와 같은 형식에서 0x00d9f278 복사 (컴퓨터마다 다름) -> 디버그 -> 창 -> 메모리 -> 메모리 1(1) -> 주소 창에 복사한 것 붙여 넣기 -> 한 줄 컴파일 F11 -> 메모리에 값이 들어가는 것을 볼 수 있다. 이때 메모리에 16진수가 뒤집혀서 들어가는데 연산을 많이 할 경우 뒤집어서 넣는다. 원래 숫자대로 들어가면 대소 비교가 느리다. 

    -

    위의 사진에서 메모리 주소가           Little Endian 방식

    12 : 38                                               ef : 38

    ab : 39                                              cd : 39

    cd : 3A                                              ab : 3A

    ef : 3B                                               12 : 3B

    슈퍼컴퓨터, Big Endian 방식            Intel CPU, Little Endian 방식

    대소 비교가 빠르다                           연산이 빠르다.

     

Designed by Tistory.