블로그 이미지
하루, 글. 그림. 영상매체. 표현을 도와주는 기기들. 도전 중. 동화다아아
    동화다아아

    카테고리

    분류 전체보기 (176)
    잡담 (1)
    IT 기기-리뷰&뉴스 (7)
    리뷰 - 도서 (1)
    리뷰 - 영상 (0)
    리뷰 - 그림/음악 (1)
    내장형 하드웨어 (163)
    Total
    Today
    Yesterday
    - class 포인터라는 것은 구조체 포인터와 같다.

    #ifdef DEBUG
            cout << "car class test()" << endl;
    #endif
     → g++ -o main main.cpp -DDEBUG
     → 디버거를 켜고 끔으로서 컴파일 단계에서 옵션으로 동작 구분이 가능하다.

    #if DEBUG
            cout << "car class test()" << endl;
    #endif
     → g++ -o main main.cpp -DDEBUG=0
     → 위와 같은 컴파일기법도 있다.

    - 구성(composition)
     → 클래스를 상속 안받고 내부에 선언해서 사용하는 것
    ※ 클래스 내 변수 초기화는 안된다.(메모리에 존재하지 않는다. 구조체와 같다.)
     class car
    {
          int K = 100;
    }
    struct car
    {
         int K = 100;
    }
     → 당연히 둘다 에러이다. 구조체와 마찬가지로 class도 메모리에 존재하지 않으므로 클래스 내 변수 초기화는 안된다.
     → 따라서 생성자 내에서 초기화 해야 한다.
     → 헷갈릴 때는 struct를 생각해 본다.
    #if 1
    #include<iostream.h>
    #include<stdlib.h>
    #else
    #include<stdio.h>
    #endif

    void test();

    // 자동차 클래스
    class car
    {  
      // 클래스 내에서만 사용할 수 있다.
      private:
        unsigned int uiNumber; // 자동차 일련 번호
        unsigned char* ucpVendor; // 제조사
        
        // 생성자 - 자동 호출 된다.(반환형이 없는 특수한 경우)
        // 객체를 생성할 때 만들어 진다.
        // 생성자는 여러개를 만들 수 있으나 소멸자는 하나만 만들 수 있다.
        car() // 인자가 없는 생성자를 디폴트 생성자라 한다.
        {
          uiNumber = 0;
          ucpVendor = 0;
    #ifdef DEBUG      
          cout << "Car class 생성자 호출" << endl;
    #endif
        }  

        car(unsigned int uiNumber) // 자동차 일련번호
        {
          ucpVendor = 0;
          (*this).uiNumber = uiNumber;
    #ifdef DEBUG      
          //cout << this->uiNumber << " Car class 생성자 호출" << endl;
    #endif
        }
      // 전체가 사용할 수 있다.
      public:
        int C;
        void test(char)
        {
    #ifdef DEBUG      
          cout << "car class test()" << endl;
    #endif
        }
      
      // 상속 받은 클래스만 사용할 수 있다.
      protected:
        unsigned int uiSpeed;
        // 제조사
        car(unsigned int uiNumber, char *cpVendor)
        {
          unsigned int uiLen; // 길이를 받기 위해
          
          uiLen = strlen(cpVendor);
          ucpVendor = (unsigned char*)malloc(uiLen+1); // NULL을 위해 +1
          strcpy((char *)ucpVendor,cpVendor);
          
          (*this).uiNumber = uiNumber;
          //cout << this->uiNumber << cpVendor<< " Car class 생성자 호출" << endl;
        }
        ~car() // 소멸자 - 앞에 틸드(~)를 붙인다. main함수가 종료되기 전에 호출된다.
        {      
          if(0 !=  ucpVendor)  
          {
            free(ucpVendor);
          }
          //cout << uiNumber << " CAR class 소멸자 호출" << endl;
        }    
    };
    // 오디오 클래스
    class audio
    {
      private:
        char *cpMaker;
        
      public:
        int A;
        audio() // 생성자
        {
          cpMaker = 0;
          //cout << "audio class" << endl;
        }
        // 인자의 타입만 쓸수도 있다.(C와의 차이점)
        audio(char *cpMaker) 
        {
          unsigned int uiLen; // 길이를 받기 위해

          uiLen = strlen(cpMaker);
          this->cpMaker = (char*)malloc(uiLen+1); // NULL을 위해 +1
          
          strcpy((*this).cpMaker, cpMaker);
    #ifdef DEBUG      
          cout << this->cpMaker << " Audio class 생성자 호출" << endl;
    #endif      
        }    
        ~audio() // 소멸자
        {
          if(0 != cpMaker)
          {
            free(cpMaker);
          }
    #ifdef DEBUG      
          cout << this->cpMaker << " Audio class 소멸자 호출" << endl;
    #endif
        }
        void test(float)
        {
    #ifdef DEBUG      
          cout << "audio class test()" << endl;
    #endif
        }
    };

    // public을 붙여주지 않으면 기본적으로 상속은 private이 된다.
    // 따라서 main()에서 호출하면 접근속성 제한이 되어 사용할 수 없다.
    // 즉, public car로 사용하므로써 main에서 int C; 에 접근 가능하다.
    class K5:public car
    {
      private:
         // 구성(composition), 상속이 아니므로 변수로 사용할 수 있다.
        audio myAudio;
        
      public:
        int K;
        K5() // 생성자
        :car(1234"KIA"), myAudio("JBL"// 다중 상속 받았을 때 생성자 구동
        {
          // 생성자 강제 호출로 객체에 객체가 대입된다.
          // 즉, audio::audio("a")는 대입을 위해 존재하고 소멸한다.(free)
          // 그런데 MyAudio는 audio::audio("a")가 소멸시킨 주소를 가리키고 있으므로
          // 실행하면 세그멘테이션 오류가 발생한다.
          // MyAudio = audio::audio("a"); - 오류코드
    #ifdef DEBUG      
          cout << "(생성자)K5가 생성되었습니다." << endl;
    #endif
          uiSpeed = 0;
          //uiNumber = 7; // private이므로 에러
        }
        ~K5(void// 소멸자
        {
    #ifdef DEBUG      
          cout << "(소멸자)K5가 소멸되었습니다." << endl;
    #endif
        }
        void speedUp(void)
        {
          uiSpeed = uiSpeed+10;
          if(100<uiSpeed)
          {
            uiSpeed = 100;
          }
    #ifdef DEBUG      
          cout << "지금 속도는 " << uiSpeed << "Km입니다." << endl;
    #endif
          return ;
        }
        void speedDown(void)
        {
          if(0==uiSpeed)
          {
            uiSpeed = 0;
          }
          else
          {
            uiSpeed = uiSpeed-10;
          }
    #ifdef DEBUG      
          cout << "지금 속도는 " << uiSpeed << "Km입니다." << endl;
    #endif
        }
        void test(int )
        {
    #ifdef DEBUG      
          cout << "K5 class test()" << endl;
    #endif
        }
    };

    int main()
    {
      K5 myCar;
        
      return 0;
    }

     → 
     → myAudio = audio::audio("aa"); 라고 생성자에 선언하면 오류가 발생한다.(세그멘테이션 오류)
     → 


     → 위의 소스는 생성자 강제 호출(audio::audio("aa"))로 객체에 객체가 대입된다.
     → 즉, audio::audio("aa")는 대입을 위해 존재하고 소멸한다.(free()가 존재)
     → 그런데 MyAudio는 audio::audio("aa")가 소멸시킨 주소를 가리키고 있으므로 실행하면 세그멘테이션 오류가 발생한다.
     → 이를 해결하려면     
     →  K5() // 생성자
       :car(12"KIA"), myAudio("aa"// 다중 상속 받았을 때 생성자 구동
       {
     → 와 같은 방법을 사용한다.

    - 자동 캐스팅
     → 자동 캐스팅은 상속된 타입 속성에 따라 가능한 것과 불가능 한 것이 구분된다.
     → 만약 A 클래스와 B 클래스가 있고 B 클래스가 A 클래스를 상속 받으면
    A AA;
    A *AP;
    B BB;
    B *BP;
    라고 선언했을 경우
    //////////////////////
    AP = &AA;
    BP = &BB; 의 경우는 문제가 없다.
    //////////////////////
    BP = &AA;
    AP = &BB; 의 경우에는 문제가 생긴다. AA의 경우는 A클래스 타입만 가지고 있고 BB의 경우는 A와 B타입을 모두 가지므로
                   BP = &AA;는 불가능 하고 AP = &BB; 의 경우는 가능하게 된다.

     → 위와 같은 자동 캐스팅은 남발하면 문제가 발생하게 된다.
    B BB[10];
    A *AP;
    AP = BB;
    AP = AP+1; // 만약에 A타입과 B타입의 크기가 다르다면 잘못된 곳을 가리키게 되므로 위험한 에러가 발생한다.
     → 따라서 사용에 주의가 필요하다.

     → 생성자와 소멸자를 클래스의 밖으로 빼서 사용하는 경우
    // 오디오 클래스
    class
     audio
    {
      private:
        char *cpMaker;
        
      public:
        int A;
        audio();
        audio(char *cpMaker);
        ~audio();
        void test(float)
        {
    #ifdef DEBUG      
          cout << "audio class test()" << endl;
    #endif
        }  
    };

    audio::audio() // 생성자
    {
      cpMaker = 0;
      //cout << "audio class" << endl;
    }
    // 인자의 타입만 쓸수도 있다.(C와의 차이점)
    audio::audio(char *cpMaker) 
    {
      unsigned int uiLen; // 길이를 받기 위해
      uiLen = strlen(cpMaker);
      this->cpMaker = (char*)malloc(uiLen+1); // NULL을 위해 +1
      
      strcpy((*this).cpMaker, cpMaker);
    #ifdef DEBUG      
      cout << this->cpMaker << " Audio class 생성자 호출" << endl;
    #endif      
    }    
    audio::~audio() // 소멸자
    {
      if(0 != cpMaker)
      {
        free(cpMaker);
      }
    #ifdef DEBUG      
      cout << this->cpMaker << " Audio class 소멸자 호출" << endl;
    #endif
    }

     → 위와 같이 생성자와 소멸자를 클래스의 밖으로 빼고 클래스 내에 선언해 준 다음
     → 밖으로 뺀 소멸자의 앞에다가 스코프 연산자(::)를 사용해 주면 사용이 가능하다.

    ※ C 복습
     → 함수의 정의는 끝에 세미콜론(;)이 없다.(중괄호로 끝을 표시) 변수의 정의에는 세미콜론(;)이 끝에 있다.
     → 따라서 main(), 생성자, 소멸자 등의 함수에는 세미콜론을 붙이지 않는다.
     → 반대로 struct, class 등에는 세미콜론을 붙인다.


    → 변수의 유효기간
    → for문안에서 새로 선언한 변수는 해당 for문이 끝나면 사라진다.
    → 확인해 보면
      // for문안에서의 객체 생성은 비효율적이다.
      // 생성되었다 삭제되었다를 반복하기 때문.
      // 따라서 객체를 미리 만들어서 값을 집어 넣는 쪽이 효율적이다.
      for(int iCnt=0;5>iCnt;++iCnt)
      {
        A temp(iCnt);
      }
      // for문안에 선언한 iCnt는 for문이 끝나면 stack에서 사라진다.
      // 자주 사용하지 않을 때는 이쪽이 코드가 심플하고 편리하나
      // 자주 사용한다면 그냥 위에 선언하고 사용하는 쪽이 낫다.
      for(iCnt=0;5>iCnt;++iCnt) - 에러코드
      {
        A test(iCnt);
      }



    → 다음으로 전역, 지역, 함수의 객체 생성 및 소멸의 순서를 확인해 본다.
    → 예제
    #include<iostream.h>

    class
     A
    {
      public:
        int i;
        int j;
        A();
        A(int i);
        A(int i, int j);
        ~A();
    };
    A::A()
    {
      cout << "A class 생성자 호출" << endl;    
    }
    A::A(int i)
    {
      (*this).i = i;
      cout << this-><< " : A class 생성자 호출" << endl;
    }
    A::A(int i, int j)
    {
      (*this).i = i;
      (*this).j = j;
      cout << this-><< " : A(int, int) class 생성자 호출" << endl;
      cout << this-><< " : A(int, int) class 생성자 호출" << endl;
    }
      
    A::~A()
    {
      cout << this-><< " : A class 소멸자 호출" << endl;    
    }

    void test()
    {
      // 처음 호출 될 때 객체를 호출한다.
      // static을 사용하면 처음에 한번만 생성되며 이것은 객체에도 유효하다.
      static A OBJ3(1000);
    }

    // main 함수 보다 전역이 먼저 실행됨을 결과로 확인할 수 있다.
    A OBJ(1);

    int main()
    {
      // 생성 순서 : 전역변수 -> 지역변수 or 함수
      // 소멸 순서 : 지역 -> 함수 -> 전역
      A OBJ2(2);
      // static으로 객체를 선언했으므로 한번만 객체가 만들어 진다.
      // 포인터를 사용할 때 문제가 있으므로 생성과 소멸 순서를
      // 제대로 알고 있어야 한다.
      test();
      test();
      test();
      test();
      test();
      return 0;
    }

     → 생성 순서 : 전역 -> 지역 or 함수
     → 소멸 순서 : 지역 -> 함수 -> 전역

     → JAVA와 C++의 차이(일반 자료형과 객체)
     → class A가 존재하고 20byte라고 가정할 때
    JAVA  C++ 
     int A;
     Z obj;( 포인터이다. 4byte)
     → java는 바로 생성하지 않는다.(모두 동적할당.)
     → 동적할당이므로 heap 영역
     obj = new z;
     → obj는 객체 참조(reference) 변수
     obj = new z; // 메모리 해제가 필요 없다. (자동)
    int A;
    Z obj;(클래스이다. 20byte) 
     → C++은 동적할당과 정적 할당을 선택할 수 있다.

    obj = new z;
    delete z;
    obj = new z;



     
    Posted by 동화다아아
    , |

    최근에 달린 댓글

    최근에 받은 트랙백

    글 보관함