'소멸자'에 해당되는 글 2건

8장정리

C++프로그래밍/C++ 2007. 7. 21. 21:52
1. 상속의 조건
사용자 삽입 이미지


<Is-a관계>
<내가돈주고산열혈강의C++ ppt자료에서 캡춰>

Is-a관계가 성립하는 방향이 상속을 의미하는 화살표 방향과 일치하고 있다.이것이 적절한 형태의 상속구조이다.
사용자 삽입 이미지

<has-a관계>
<내가돈주고산열혈강의C++ ppt자료에서 캡춰>

has-a관계는 소유의 개념이 강하다.상속은 소유를 표현하기 위해서도 사용된다.
#include <iostream>

using std::cout;
using std::endl;

class Cudgel{
public:
 void Swing(){cout<<"Swing a codgel"<<endl;}
};
class Police
{
 Cudgel cud;
public:
 void UseWeapon(){cud.Swing();}
};
int main()
{
 Police pol;
 pol.UseWeapon();
 return 0;
}

Cudgel클래스의 객체가 Police의 멤버객체로 포함되어있다..이러한 객체를 클래스 객체멤버
혹은 객체멤버라고 한다. 이러한 관계를 포함관계라고 한다.

상속과 포함관계 둘로 나타낼수 있을때에는 상속보다 포함관계를 쓰는편이 좋다.
왜냐면 그것은 결합도의 문제인데 결합도가 높아질수록 혹시모르는 변경을 할때
결합도가 높을수록 코드를 손봐야 할 부분이 많아진다.
상속관계는 결합도가 포함관계보다 높다.
이 두 예제를 잘 분석해봐라..

2. 상속된 객체와 포인터와의 관계

객체포인터 : 객체의 주소값을 저장할수 있는 포인터.
A클래스의 포인터 A*는 A객체의 주소값 뿐만 아니라 A클래스를 상속하는 Derived클래스 객체의 주소값도 저장이 가능하다. 여기서 A클래스의 포인터를 가리켜 객체포인터라고 한다

사용자 삽입 이미지

<내가돈주고산열혈강의C++ ppt자료에서 캡춰>

Student클래스는 Person클래스를 상속하고 PartTimeStd클래스는 Student클래스를 상속한다
이런 상속관계의 경우 PartTimeStd가 Student를 상속하고.Student가 Person을 상속하고
사용자 삽입 이미지
<js의 그림판 작품 ㅋ,객체포인터>

PartTimeStd객체가 객체안에 Student객체와 Person객체를 상속하고 있으므로
PartTimeStd객체는 Student객체이자 Person객체이고 Student객체는 Person객체이다.
그러므로 Person포인터 Student포인터 PartTimeStd포인터로 PartTimeStd객체를 가리키는
것은 아무런 문제가 없다..

그러나 가리키는 것만 문제가 없을뿐..각각의 포인터의 권한은 다르다..
사용자 삽입 이미지
<js의 그림판 작품 ㅋ,객체포인터의 권한>


그림에서 보면 Student나 Person포인터는 각각 상속하고 있는 객체를뚫고 가리키고있다
뚫고 지나간 객체는 뚫고지나는 포인터로 항상 가리킬수 있다.(이결론내느라 빡셌다)

그리고 위의 그림에서 보면 각 포인터의 권한은 포인터가 가리키고 있는 객체안에 상속된
객체까지만 접근이 가능하다. Person*로 Student나 PartTimeStd객체에 접근(여기서 접근이란 객체의 컨트롤)이 불가능 하다 Person*는 Person객체만 접근 가능하고
Student*는 Student와상속하는Person객체, PartTimeStd*는 상속하는Student와Person객체
모두 접근가능하다.

3. 객체레퍼런스
객체레퍼런스는 객체포인터의 내용과 일치한다.



4. Static Binding & Dynamic Binding
오버라이딩의 이해

#include <iostream>

using std::cout;
using std::endl;

class AAA{
public:
 void fct(){
  cout<<"AAA"<<endl;
 }
};

class BBB : public AAA{
public:
 void fct(){
  cout<<"BBB"<<endl;
 }
};

int main(){
 BBB b;
 b.fct();
 
 return 0;
}


Base클래스에서 선언한 멤버함수를 Derived클래스에서 또다시 선언하면?
이것의 형태를 가리켜 오버아리딩(overriding)이라고 한다. 오버라이딩은 이전에 정의된
함수를 가리는 특성이 있다.
위의 예제에서 BBB클래스의 객체b를 생성하려고 들어갔더니 어.AAA를 상속하네
AAA로 ㄱㄱ 갔더니 fct함수가 있네.어?근데BBB에도 fct함수가 있네..그럼 AAA멤버함수 실행
안하고 바로 BBB멤버함수 실행한다.객체생성완료..
그런데 오버라이딩된 함수는 호출이 불가능할까? 가능하다.대한민국에불가능한게어딨어.


int main(){
 BBB* b =  new BBB;
 b->fct();

 AAA* a=b;
 a->fct();

 delete b;
 
 return 0;
}


이렇게 포인터로 접근을 하면 오버라이딩된 함수에도 접근이 가능하다.
이 예문은 위에 js의 그림판 그림들을 보면서 이해하도록. ㅋㅋㅋㅋㅋㅋㅋ저그림예술이야.

멤버함수 virtual 으로 선언하기

class AAA{
public:
 virtual void fct(){
  cout<<"AAA"<<endl;
 }
};

class BBB : public AAA{
public:
 void fct(){
  cout<<"BBB"<<endl;
 }
};



멤버함수에 virtual 키워드를 붙이면 이것은 가상의 함수..즉 없는 함수가 된다.
있지만 없는 함수-_-;

int main(){
 BBB* b =  new BBB;
 b->fct();

 AAA* a=b;
 a->fct();

 delete b;
 
 return 0;
}

이렇게 할경우 둘다 BBB의 함수만 호출한다.왜? 없는 함수니까 AAA의 멤버함수는.
여기서 주의할점.

함수 이름이 같아야 Virtual 선언이 먹힌다
virtual 키워드가 붙은 상태의 오버라이딩은 재정의 이지만
virtual 키워드가 붙지 않은 상태의 오버라이딩은 은닉이다.
오버라이딩이라고 무조건 재정의가 아님.


가상함수또한 상속된다.

class AAA{
public:
 virtual void fct(){
  cout<<"AAA"<<endl;
 }
};

class BBB : public AAA{
public:
 virtual void fct(){
  cout<<"BBB"<<endl;
 }
};

class CCC : public BBB{
public:
 virtual void fct(){
  cout<<"CCC"<<endl;
 }
};


최종적으로 오버라이딩한 함수를 제외한 나머지 함수는 가려지게 된다.

오버라이딩된 함수 호출하기

#include <iostream>

using std::cout;
using std::endl;

class AAA{
public:
 virtual void fct(){
  cout<<"AAA"<<endl;
 }
};

class BBB : public AAA{
public:
 void fct(){
  AAA::fct();
  cout<<"BBB"<<endl;
 }
};

int main(){
 AAA* a=new BBB;
 cout<<"첫번째 시도"<<endl;
 a->fct();

 cout<<"두번째 시도"<<endl;
 a->AAA::fct();    //거의 사용안함..쓰지마
 return 0;
}

오버라이딩 된 함수도 범위지정연산자 ( :: )를 이용하여 호출이 가능하다.
그러나 두번째껀 쓰지말래..쓰고싶지도않아 ㅎ;;

순수가상함수와 추상클래스

class AAA{
public:
 virtual void fct(){
  cout<<"AAA"<<endl;
 }
};

class BBB : public AAA{
public:
 virtual void fct(){
  cout<<"BBB"<<endl;
 }
};

class CCC : public AAA{
public:
 virtual void fct(){
  cout<<"CCC"<<endl;
 }
};

위의 예제에서 AAA클래스의 멤버함수 fct는 아무런 기능을 하지 않는다 가상이니까
그때는 순수가상함수로 만들어줄수있다 바로..
class AAA{
public:
 virtual void fct()=0;
};

요딴식으로 말이지..이런식의 함수를 가리켜 순수 가상함수라고 한다.
이건 가상함수와 달라서 아예 없는함수다 가상함수는 있으면서 없는 함수지만 이건 아예 없는 함수이다.

저렇게 하나이상의 멤버함수가 순수가상함수인 클래스를 가리켜 추상클래스 라고한다.
추상클래스는 객체화 하지못한다.함수의 정의가 생략되었기때문에 완전한 클래스가 아니므로 객체를 생성할수 없다.

5. virtual 소멸자의 필요성

#include <iostream>

using std::cout;
using std::endl;

class AAA{
 char* str1;
public:
 AAA(char* _str1){
  str1 = new char[strlen(_str1)+1];
  strcpy(str1,_str1);
 }
 ~AAA(){
  cout<<"~AAA() call"<<endl;
  delete []str1;
 }
 virtual void ShowString(){
  cout<<str1<<endl;
 }
};

class BBB : public AAA{
 char* str2;
public:
 BBB(char* _str1,char* _str2) : AAA(_str1){
  str2 = new char[strlen(_str2)+1];
  strcpy(str2,_str2);
 }
 ~BBB(){
  cout<<"~BBB() call"<<endl;
  delete []str2;
 }
 virtual void ShowString(){
  AAA::ShowString();
  cout<<str2<<endl;
 }
};

int main(){
 AAA* a=new BBB("Good","evening");
 BBB* b=new BBB("Good","morning");


 a->ShowString();
 b->ShowString();

 cout<<"객체소멸직전"<<endl;
 delete a;
 delete b;

 return 0;
}


사용자 삽입 이미지
<내가돈주고산열혈강의C++ ppt자료에서 캡춰>

위의 예제의 문제점..
위의 예제 AAA객체의 소멸자에
virtual ~AAA(){
  cout<<"~AAA() call"<<endl;
  delete []str1;
 }
virtual키워드를 붙여주자 그럼 어떻게 될것인가?
사용자 삽입 이미지
<내가돈주고산열혈강의C++ ppt자료에서 캡춰>

짜잔..저렇게 된다.근데 좀 이상해..두개의 소멸자는 이름이 다른데 AAA소멸자가 virtual선언이 되어있다고 해서 BBB의 객체 소멸자를 대신 호출하냐고..음..저거는 이름은 다르지만
둘다 소멸자인 관계로 virtual함수를 오버라이딩 했을때의 현상이 그대로 적용이된다.
(근데 저거 무한루프아니여?ㅋㅋ)

블로그 이미지

百見 이 不如一打 요 , 百打 가 不如一作 이다.

,

4장정리

C++프로그래밍/C++ 2007. 7. 16. 10:59

1. 정보은닉
객체의 외부에서 객체내에 존재하는 멤버변수에 직접 접근하는것을 허용하지 않는것.

#include <iostream>

using std::cout;
using std::cin;
using std::endl;

class Point
{
public:
 int x;
 int y;
};

int main()
{
 int x,y;
 cout<<"좌표입력 : ";
 cin>>x>>y;

 Point p;
 p.x=x;
 p.y=y;

 cout<<"입력된 데이터를 이용해서 그림을 그림"<<endl;
 return 0;
}

 설명: 클래스 외부에서 내부의 변수에 직접 접근을 하였다.
         클래스 객체의 정보가 노출이 된 상태.

#include <iostream>

using std::cout;
using std::endl;
using std::cin;

class Point
{
 int x;
 int y;

public:
 int GetX()
 {
  return x;
 }
 int GetY()
 {
  return y;
 }
 void SetX(int _x);
 void SetY(int _y);
};
void Point::SetX(int _x)
{
 if(_x<0 || _x>100)
 {
  cout<<"x좌표 입력오류 ,확인요망"<<endl;
  return ;
 }
 x=_x;
}
void Point::SetY(int _y)
{
 if(_y<0 || _y>100)
 {
  cout<<"y좌료 입력오류 ,확인요망"<<endl;
  return ;
 }
 y=_y;
}

int main()
{
 int x,y;

 cout<<"좌표입력 : ";
 cin>>x>>y;

 Point p;
 p.SetX(x);
 p.SetY(y);

 cout<<"입력된 데이터를 이용해서 그림을 그림"<<endl;
 return 0;

}

설명 : 클래스 객체 외부에서 클래스 객체 내부로 직접 접근이 불가능한 상태.
         GetX,GetY라는 함수로 변수에 접근을 한다음 반환되는 값으로
         SetX,SetY라는 함수에서 연산을 하고 있다. 직접접근이 아니므로
         정보가 은닉이 된 상태. 또한 SetX,Y함수에서 클래스 객체내의 변수에 대한
         경계검사가 이루어졌다..

클래스에서 아무런 선언이 없으면 접근 제어의 default값으로 private가 적용이된다.
그러나 C의 구조체에서는 default값은 public 이다


2. 캡슐화 (Encapsulation)
캡슐화란 관련있는 데이터와 함수를 하나의 단위로 묶는것이다.

#include <iostream>

using std::cout;
using std::endl;
using std::cin;

class Point
{
 int x;
 int y;
public:
 int GetX(){return x;}
 int GetY(){return y;}
 void SetX(int _x);
 void SetY(int _y);
}

class PointShow
{
public:
 void ShowData(Point p)
 {
  cout<<"x좌표 : "<<p.GetX()<<endl;
  cout<<"y좌표 : "<<p.GetY()<<endl;
 }
};
int main()
{
 int x,y;
 cout<<"좌표입력 :";
 cin>>x>>y;
 Point p;
 p.SetX(x);
 p.SetY(y);
 PointShow show;
 show.ShowData(p);
 return 0;
}

설명: 현재 x,y와 관련된 데이터와 함수가 두개의 클래스로 나뉘어져있다..
        적절한 캡슐화가 필요하다.
#include <iostream>
using std::cout;
using std::endl;
using std::cin;
class Point
{
 int x;
 int y;
public:
 int GetX(){return x;}
 int GetY(){return y;}
 void SetX(int _x);
 void SetY(int _y);
 void ShowData();
};
int main()
{
 int x,y;
 cout<<"좌표입력 :";
 cin>>x>>y;
 Point p;
 p.SetX(x);
 p.SetY(y);
 p.ShowData();
 return 0;
}
설명: 위의 예제에서 두개의 클래스로 나뉘어졌던 것을 하나로 캡슐화 시켰다.
        캡슐화의 개념에 대해서 확실히 알아두자.

캡슐화와 정보은닉 두가지 특성을 가진 클래스는 좋은 클래스라고 할수있다



3. 생성자와 소멸자

생성자
객체 생성시 반드시 호출된다.
객체를 생성과 동시에 초기화 할수있는 메커니즘.
함수이며 클래스의 이름과 같은 이름을 지니고 리턴타입이 없으며 리턴하지도 않는다.

모든 객체는 1)메모리할당 , 2)생성자 호출 의 과정을 반드시 거치게 되어있다
.

#include <iostream>

using std::cout;
using std::endl;
const int SIZE=20;

class AAA
{
 int i,j;
public:
 AAA(int _i,int _j)      //생성자
 {
  i=_i,j=_j;
 }
 void ShowData()
 {
  cout<<i<<' '<<j<<endl;
 }
};
int main()
{
 AAA aaa(111,222);  // (설명)
 aaa.ShowData();
 return 0;
}

 설명: AAA클래스의 객체를 생성하되 이름은 aaa이고 생성자호출시 111,222를
         인자로 전달받을수 있는 생성자를 호출하라.

Default생성자
생성자를 하나도 정의하지 않으면 Default 생성자가 자동삽입된다.
생성자도 함수이다 .그러므로 오버로딩도 가능하고 디폴트 매개변수의 설정도 가능하다.
class Point
{
   int x,y;
public:
   Point(){}
}
디폴트 생성자의 예
하는일은 아무것도 없다. 생성자를 정의하지 않으면 컴파일러에 의해 화면에는 보이지 않지
만 자동적으로 삽입이 된다.

#include <iostream>

using std::cout;
using std::endl;

class Point
{
 int x,y;
public:
 Point()  
 {
  x=y=0;
 }
 Point(int _x,int _y)
 {
  x=_x,y=_y;
 }
 void ShowData()
 {
  cout<<x<<' '<<y<<endl;
 }
};
int main()
{
 Point p1(10,20);
 p1.ShowData();
 Point p2; // void생성자 호출 하는것
 Point p2(); // 이건 생성자에게 void로 매개변수를 전달하는것이 아니라 p라는 함수를
                //  호출 하는것이다.
                //  class의 객체생성시 void매개변수를 전달할때는 Point p2; 이렇게
               //   해야한다는 일종의 약속.
 p2.ShowData();
 return 0;
}

소멸자
소멸자도 생성자와 같이 객체를 생성하게 될때 반드시 호출된다.
모든 객체는 반드지 1)소멸자 호출 , 2)메모리반환 의 과정을 거쳐서 소멸된다.
소멸자도 함수이며 클래스의 이름앞에 ~가 붙은 형태의 이름을 지닌다.
리턴하지도 않고 리턴타입도 선언되지 않으며 매개변수를 받을수가 없다
따라서 오버로딩도 할수가 없고 디폴트 매개변수의 선언이 불가능하다

#include <iostream>

using std::cout;
using std::endl;

class AAA
{
public:
 AAA()
 {
  cout<<"생성자호출"<<endl;
 }
 ~AAA()  // 소멸자의 형태
 {
  cout<<"소멸자호출"<<endl;
 }
};
int main()
{
 AAA aaa1;
 AAA aaa2;
 return 0;
}

소멸자도 정의를 해주지 않으면 Default 소멸자가 호출이 된다.
즉. 클래스 객체 내에서 생성자와 소멸자를 정의해주지 않으면
Default 생성자와 소멸자가 자동으로 호출이 된다.하지만 하는일은 없다.
class Point
{
   int x,y;

public:
   Point(){}
   ~Point(){}

   void Print(){...}
}

4. 클래스와 배열


두가지 코드를 보고이해해라. 이건 어떻게 설명을..음.

#include <iostream>

using std::cout;
using std::endl;

class Point
{
 int x;
 int y;
public:
 Point(){
  cout<<"Point() call"<<endl;
  x=y=0;
 }

 Point(int _x, int _y)
 {
  x=_x;
  y=_y;
 }

 int GetX(){return x;}
 int GetY(){return y;}
 void SetX(int _x){x=_x;}
 void SetY(int _y){y=_y;}
};

int main()
{
 Point arr[5]; //객체배열

 for(int i=0; i<5; i++)
 {
  arr[i].SetX(i*2);
  arr[i].SetY(i*3);
 }
 for(int j=0; j<5; j++)
 {
  cout<<"x: "<<arr[j].GetX()<<' ';
  cout<<"y: "<<arr[j].GetY()<<endl;
 }
 return 0;
}


Point arr[5];
객체배열, 즉, Point객체 5개의 배열이 생성된것.배열안에 객체가 존재.

int main()
{
 Point* arr[5]; //객체포인터배열

 for(int i=0; i<5; i++)
 {
  arr[i]=new Point(i*2,i*3);
 }
 for(int j=0; j<5; j++)
 {
  cout<<"x: "<<arr[j]->GetX()<<' ';
  cout<<"y: "<<arr[j]->GetY()<<endl;
 }
 for(int k=0; k<5; k++)
 {
  delete arr[k];
 }
 return 0;
}


Point* arr[5];
Point객체 5개를 가리킬수 있는 포인터배열.


5. this 포인터(자기 참조 포인터)

#include <iostream>

using std::cout;
using std::endl;

class Person
{
public:
 Person(){}

 Person* GetThis()
 {
  return this;
 }
};

int main()
{
 cout<<"*****p1의정보*****"<<endl;
 Person* p1 = new Person();
 cout<<"포인터p1:"<<p1<<endl;
 cout<<"p1의 this:"<<p1->GetThis()<<endl;

 return 0;

}



사용자 삽입 이미지

this포인터의 용도

#include <iostream>

using std::cout;
using std::endl;

class Data
{
 int aaa;
 int bbb;
public:
 Data(int aaa, int bbb)
 {
  //aaa=aaa;
  this->aaa=aaa;
  //bbb=bbb;
  this->bbb=bbb;
 }
 void printAll()
 {
  cout<<aaa<<" "<<bbb<<endl;
 }
};

int main()
{
 Data d(100,200);
 d.printAll();
 return 0;
}


생성자 Data를 보면 매개변수로 aaa,bbb로 받고 있다.
매개변수도 지역변수이므로 주석 처리되있는 부분은 전부다 생성자의 매개변수에 해당한다.
그러나 this포인터를 사용하면 private로설정되어있는 변수에 접근이 가능하다.

6. friend선언
friend선언을 통해 private로 선언된 멤버변수에 접근이 가능하다.

클래스에 대한 friend선언과 전역함수에 대한 friend선언이 가능한데..
사용하지 말래..ㅋㅋ
일단 소스코드만 첨부


friend선언은 정보은닉에 위배된다.

블로그 이미지

百見 이 不如一打 요 , 百打 가 不如一作 이다.

,