'C++프로그래밍'에 해당되는 글 45건

9장정리

C++프로그래밍/C++ 2007. 7. 22. 19:48

1. 클래스 멤버함수는 사실 어디에?

#include <iostream>

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

class Data{
 int data;
public:
 Data(int num){
  data=num;
 }
 void ShowData(){
  cout<<"Data : "<<data<<endl;
 }
 void add(int num){
  data+=num;
 }
};

int main()
{
 Data ddd(10);
 ddd.add(10);
 ddd.ShowData();

 Data eee(20);
 eee.add(20);
 eee.ShowData();

 return 0;
}

2개의 객체를 생성할 경우 멤버함수도 2번 호출하는데 이는 생성되는 객체마다 멤버함수가
존재하는 것이 아니다. 함수이름은 메모리 상에 존재하는 함수를 가리키는 포인터와 같다.
(상수포인터). 컴파일을 할때 이미 메모리 상에 올라간후 런타임에 함수포인터를 이용하여
불러온다고 해야하나..맞나..아무튼 객체를 100개 생성하더라도 메모리에 올라가는 함수는
하나이고 그 100개의 객체는 함수포인터로 메모리에 올라가있는 함수를 호출하여 사용한다.
즉 모든 객체가 함수 하나를 공유하는 형태를 취한다.

2. 가상함수가 동작하는 원리

 #include <iostream>

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

class A{
 int a;
 int b;
public:
 virtual void fct1(){cout<<"fct1()"<<endl;}
 virtual void fct2(){cout<<"fct2()"<<endl;}
};
class B : public A{
 int c;
 int d;
public:
 virtual void fct1(){cout<<"Overriding fct1()"<<endl;}
 void fct3(){cout<<"fct3()"<<endl;}
};

int main()
{
 A* aaa= new A();
 aaa->fct1();

 B* bbb=new B();
 bbb->fct1();
 return 0;
}

A클래스에 가상함수가 2개가 존재한다. 저렇게 1개 이상의 클래스를 포함하는 클래스에 대해서 컴파일러는 가상함수 테이블이라는 것을 만들어준다. 이 테이블은 실제 호출되어야할 함수의 위치정보를 가지고 있는 테이블이다.

사용자 삽입 이미지

<가상함수테이블>
<내가 돈주고 산 열혈강의 C++ ppt자료에서 캡춰>


가상함수 테이블은 key와value가 있는데 key값은 호출하고자 하는 함수를 구분지어주는것이고 value는 해당 함수의 위치를 가르쳐 주는 역할을 한다.

위의 예제에서 객체를 생성하면 다음과 같은 현상이 벌어지게 된다.

사용자 삽입 이미지

<가상함수 테이블과 가상함수와의 관계>
<내가 돈주고 산 열혈강의 C++ ppt자료에서 캡춰>



하나이상의 가상함수를 멤버로 지니는 클래스 객체는 가상함수 테이블을 가리킬수 있는 포인터가 추가된다.
가상함수를 포함하지 않은 클래스는 가상함수 테이블을 생성하지 않는다.

3. 다중상속에 대한이해
다중 상속이란 하나의 Derived 클래스가 둘이상의 Base를 상속하는것을 말한다.

#include <iostream>

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

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

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

class CCC : public AAA,public BBB{
public:
 void ShowString(){
  String1();
  String2();
 }
};

int main(){
 CCC ccc;
 ccc.ShowString();
 return 0;
}


클래스 CCC는 클래스 AAA와BBB를 동시에 상속하고 있다.
별 문제 없는 코드이다..실행결과도 아주 만족하게 나오고... 그! 러! 나!

#include <iostream>

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

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

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

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

class DDD : public BBB, public CCC{
public:
 void ShowString(){
  String1();
  String2();
  String3();
 }
};
int main(){
 DDD ddd;
 ddd.ShowString();
 return 0;
}


이경우를 살펴보자. 클래스 BBB와 CCC는 각각 클래스 AAA를 상속하고 있다.
그런가운데 우리의 DDD씨는 BBB와CCC의클래스를 상속하고 계시군요! ㅋ
자..그럼 생각해봅시다. BBB와 CCC클래스는 각각 AAA클래스를 상속하고 있으니 둘다
String1()이라는 함수를 가지고 있겠지..그런 B,C클래스를 상속한 D클래스는
String1()함수를 2개나 갖게 된다..그래서 메인함수에서 저렇게 호출을 해버리면
어떤 String1()을 호출해야할지를 몰라서..오류를 뿜어내고 만다..우어~~
그럴경우 B,C클래스에서 A를 상속할떄 virtual상속을 하면 되는데(virtual base클래스)
쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지마라쓰지

블로그 이미지

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

,

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함수를 오버라이딩 했을때의 현상이 그대로 적용이된다.
(근데 저거 무한루프아니여?ㅋㅋ)

블로그 이미지

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

,

typedef struct node* tree_pointer;
typedef struct node{
 int data;
 tree_pointer left_child,right_child;
};


//중위순회
void inorder(tree_pointer ptr)
{
 if(ptr){
  inorder(ptr->left_child);
  printf("%d\n",ptr->data);
  inorder(ptr->right_child);
 }
}

//전위순회
void preorder(tree_pointer ptr)
{
 if(ptr){
  printf("%d\n",ptr->data);
  preorder(ptr->left_child);
  preorder(ptr->right_child);
 }
}

//후위순회
void postorder(tree_pointer ptr)
{
 if(ptr){
  postorder(ptr->left_child);
  postorder(ptr->right_child);
  printf("%d\n",ptr->data);
 }
}

//반복적인 중위순회(stack)
void iter_inorder(tree_pointer node)
{
 int top = -1;
 tree_pointer stack[MAX_STACK_SIZE];
 for( ; ; ){
  for( ; node; node->left_child)
   add(&top,node);
  node = delete(&top);
  if(!node) break;
  printf("%d\n", ptr->data);
  node = node->right_child;
 }
}

//레벨순서 트리 순회
void level_order(tree_pointer ptr)
{
 int front,rear = 0;
 tree_pointer queue[MAX_QUEUE_SIZE];
 if (!ptr) return;
 addq(front, &rear, ptr);
 for( ; ; ){
  ptr = deleteq(&front, rear);
  if(ptr){
   printf("%d\n",ptr->data);
   if(ptr->left_child)
    addq(front,&rear,ptr->left_child);
   if(ptr->right_child)
    addq(front,&rear,ptr->right_child);
  }
  else break;
 }
}




/////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

typedef struct Treenode{
 int data;
 struct Treenode *left,*right;
}Treenode;

Treenode n1={4,NULL,NULL};
Treenode n2={2,&n1,NULL};
Treenode n3={5,NULL,NULL};
Treenode n4={6,NULL,NULL};
Treenode n5={3,&n3,&n4};
Treenode n6={1,&n2,&n5};

Treenode* root = &n6;

inorder(Treenode *root){
 if(root){
  inorder(root->left);
  printf("%d->",root->data);
  inorder(root->right);
 }
}

preorder(Treenode* root){
 if(root){
  printf("%d->",root->data);
  preorder(root->left);
  preorder(root->right);
 }
}

postorder(Treenode* root){
 if(root){
  postorder(root->left);
  postorder(root->right);
  printf("%d->",root->data);
 }
}

void main()
{
 printf("중위순회 : ");
 inorder(root);
 printf("\n");
 printf("전위순회 : ");
 preorder(root);
 printf("\n");
 printf("후위순회 : ");
 postorder(root);
 printf("\n");
}


블로그 이미지

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

,

/*↓↓↓↓↓↓↓↓↓선형  큐↓↓↓↓↓↓↓↓↓*/
#define MAX_QUEUE_SIZE 100
typedef struct{
 int key
}element;

element queue[MAX_QUEUE_SIZE];
int rear = -1;
int front = -1;

void addq(int *rear,element item)
{
 if(*rear == MAX_QUEUE_SIZE-1){
  queue_full();
  return;
 }
 queue[++*rear]=item;
}

void deleteq(int *front, int rear)
{
 if(*front == rear)
  return queue_empty;
 return queue[++*front];
}
/*↓↓↓↓↓↓↓↓↓원형  큐↓↓↓↓↓↓↓↓↓*/
int rear = 0;
int front = 0;

void addq(int front, int *rear,element item)
{
 *rear=(*rear+1) % MAX_QUEUE_SIZE;
 if(front == *rear){
  queue_full(rear);
  return ;
 }
 queue[*rear]=item;
}

void deleteq(int *front,int rear)
{
 element item;
 if(*front == rear)
  return queue_empty();
 *front=(*front+1) % MAX_QUEUE_SIZE;
 return queue[*front];
}
/*↓↓↓↓↓↓↓↓↓연결된 큐↓↓↓↓↓↓↓↓↓*/
#define MAX_QUEUES 10
typedef struct queue* queue_pointer;
typedef struct queue{
 element item;
 queue_pointer link;
};

queue_pointer front[MAX_QUEUE],rear[MAX_QUEUE];

void addq(queue_pointer* front,queue_pointer* rear,element item)
{
 queue_pointer temp = (queue_pointer)malloc(sizeof(queue));
 if(IS_FULL(temp)){
  fprintf(stderr,"the memory is full\n");
  exit(1);
 }
 temp->item=item;
 temp->link=NULL;
 if(*front){
  (*rear)->link=temp;
 }
 else{
  *front = temp;
}
  *rear = temp;
}

element deleteq(queue_pointer *front)
{
 queue_pointer temp = *front;
 element item;
 if(IS_EMPTY(*front)){
  fprintf(stderr,"the queue is empty\n");
  exit(1);
 }
 item=temp->item;
 *front=temp->link;
 free(temp);
 return item;
}




블로그 이미지

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

,

7장정리

C++프로그래밍/C++ 2007. 7. 20. 09:41

1. Employee problem


소스 자체는 문제가 없으나 요구사항이 많아질경우 클래스전체를 뜯어 고쳐야 하는 경우가 발생할경우가 있다..그걸 막기 위해 우리는 지금 부터 상속을 배운닷!!!

2. 상속의 기본개념

class Person{
 int age;
 char name[20];
public:
 int GetAge() const{
  return age;
 }
 const char* GetName() const{
  return name;
 }
 Person(int _age=1,char* _name="noname"){
  age=_age;
  strcpy(name,_name);
 }
};

class Student: public Person{
 char major[20];
public:
 Student(char* _major){
  strcpy(major,_major);
 }
 const char* GetMajor() const{
  return major;
 }
 void ShowData() const{
  cout<<"이름 : "<<GetName()<<endl;
  cout<<"나이 : "<<GetAge()<<endl;
  cout<<"전공 : "<<GetMajor()<<endl;
 }
};

int main()
{
 Student Kim("computer");
 Kim.ShowData();
 return 0;
};
 
사용자 삽입 이미지

위의 예제의 경우 Student 클래스가 Person클래스를 public 상속한다.
여기서 상속되는 클래스는 Base클래스, 상속하는 클래스는 Derived 클래스라고 한다.
일단 Student 클래스가 Person클래스를 생성하면 이런 메모리공간이 할당된다.
사용자 삽입 이미지
<내가 돈주고 산 열혈강의 C++ 의 PPT자료중 캡춰>


상속한 클래스Student 는 상속된 Person의 멤버들도 포함시킨다는 것이다.이게 상속의 기본원리 이다.

3. 상속하는 클래스의 객체 생성 및 소멸과정

간단한 예를 하나 들어보자.
class AAA{
public:
 AAA(){
  cout<<"AAA()호출"<<endl;
 }
 AAA(int i){
  cout<<"AAA(int i)호출"<<endl;
 }
};
class BBB : public AAA{
public:
 BBB(){
  cout<<"BBB()호출"<<endl;
 }
 BBB(int j){
  cout<<"BBB(int j)호출"<<endl;
 }
};

int main()
{
 cout<<"객체1생성"<<endl;
 BBB bbb1;
 cout<<"객체2생성"<<endl;
 BBB bbb(10);
 return 0;
};

이런 코드가 있다. 일단 실행결과를 보고 말하자.
사용자 삽입 이미지


상속하고 있는 클래스의 객체 생성과정
1. 메모리공간 할당. 상속되는 클래스를 감안하여 메모리 할당
2. Base클래스의 생성자 실행
3. Deriver클래스의 생성자 실행

자..결과를 봅시다..일단 메인함수에서 처음에 BBB클래스의 객체 bbb1을 만들면서
아무것도 전달 안해주고 있지..그럼 void생성자가 호출이 된다..클래스 BBB의 void생성자로 가보니..어라..Person클래스를 상속하고 있네? 그럼 몸체를 실행하기전에 AAA클래스로간다.
자..가보니 AAA클래스에 생성자가 2개나 있다..하나는 void생성자 하나는 int i를 받을수 있는 생성자..상식적으로 뭐가 호출되겠냐? 그래 void생성자가 호출이되고 실행이 되.그다음에
아까 호출만한 BBB클래스의 void생성자를 실행하지..  그담에 또 BBB클래스의 bbb2객체를 만들면서 이번엔 인자값으로 10을 주고 있어..10을 받을수 있는 BBB클래스의 생성자를 찾아보자..있다..2번째 그걸 호출하고 몸체를 실행하려고 봤더니..또 상속하고있네..ㅅㅂ..
그럼 다시 AAA클래스로 올라가서...이번엔 어떤 생성자를 호출,실행 해야될까..이번에도
void생성자를 호출한다.자세한 설명은 좀 있다가 하고..그담에 실행후 다시 BBB클래스로 와서 나머지 부분 실행하고 종료한다..그래서 나온 결과값이 저위에 값..휴.

만약에 저 위에서 bbb1객체를 생성하면서 상속되는 클래스의 두번째 생성자.(int i)를 인자로 받는 생성자를 호출하고 싶다면..우리가 전에 배운 멤버 이니셜라이져를 써먹으면 된다.
BBB() :AAA(j){
  cout<<"BBB()호출"<<endl;
 }

j를 인자로 받을수 있는 AAA클래스의 생성자를 호출하라..라는 뜻이 된다.

객체 생성과정의 적용
class Person{
 int age;
 char name[20];
public:
 int GetAge() const{
  return age;
 }
 const char* GetName() const{
  return name;
 }
 Person(int _age=1,char* _name="noname"){
  age=_age;
  strcpy(name,_name);
 }
};
class Student: public Person{
 char major[20];
public:
 Student(int _age,char* _name,char* _major){
  age=_age;
  strcpy(name,_name);
  strcpy(major,_major);
 }
 const char* GetMajor() const{
  return major;
 }
 void ShowData() const{
  cout<<"이름 : "<<GetName()<<endl;
  cout<<"나이 : "<<GetAge()<<endl;
  cout<<"전공 : "<<GetMajor()<<endl;
 }
};

int main()
{
 Student Kim(22,"jslee","computer");
 Kim.ShowData();
 return 0;
};

첫번째 예를 보면 전달된 major인자를 빼고는 전부디폴트 매개변수에 의해 초기화가 된것을 볼수가 있는데,이제 우리가 원하는 방식으로 바꿔보자..객체생성시 모두 초기화를 해주자.
그래서 바꾼 코드가 위의 코드이다. 자 어떤가..만족하나? 오..노
age와 name는 상속하고 있긴 하지만 엄연히 다른 클래스의 private멤버이다..
그래서 자기 클래스에서만 접근이 가능하다. 상속하고 있다고 해서 접근 가능한게 아니다.
자.우리는 public로 선언되어있는 그 무언가를 상속하는 클래스에서 조작을 시도할것이다 .
자..Student 클래스의 객체를 생성하면서 인자를 전달해주고있다. 근데 age와 name은 접근을 하지 못해..일단 생성자 호출한담에 뭐?그래 상속되는 클래스로 가야지..상속하고있으니까..가봤더니 생성자가 하나가있다..지금의 Student클래스는 Person의 void 생성자를 호출하고 있으니까 우리가 age,name에 접근을 못한다고....그러니 우린 멤버이니셜라이져 특급
기능을 사용하여..Person생성자로 하여금 Person의 멤버변수 age,name에 접근 할 것이다.
Student(int _age,char* _name,char* _major) : Person(_age,_name){
  strcpy(major,_major);
 }


짜잔..어떤가..Student클래스를 호출하면서 몸체 생성하기전에 Person클래스의 생성자중
인자로 _age,_name을 받을수 있는 생성자를 호출하고있다.자.봤더니 있네?
그 생성자를 호출하고 실행을 했더니.오..Person함수내의 멤버변수에 접근해서 초기화를
해주네 오 ㄳ ..그리고 나서 다시 Student객체생성 마무리 하고..봤더니..
우리가 원하는 데로 초기화가 이루어졌다 ㅋㅋ 이제 정보은닉을 무너트리지 않고 객체생성시 우리가 원하는 데로 초기화를 해줄수 있게 되었습니다 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

요거 위에위에 노란 박스 대로 해서 초기화를 하려면 Person클래스의 멤버변수가
public인경우엔 가능하다..근데 그럼 정보은닉이 무너지니까..내가 시킨대로 해라 캬캬

객체 소멸과정의 이해

class AAA{
public:
 AAA(){
  cout<<"AAA()호출"<<endl;
 }
 ~AAA(){
  cout<<"~AAA()호출"<<endl;
 }
};

class BBB : public AAA{
public:
 BBB(){
  cout<<"BBB()호출"<<endl;
 }
 ~BBB(){
  cout<<"~BBB()호출"<<endl;
 }
};

int main()
{
 BBB bbb;
 return 0;
}


객체 소멸과정을 보기 위해 우리는 여기서 저기 위의 예제를 볼필요가 있다.
자자 긴장긴장.. 메인함수에서 BBB클래스의 객체 bbb를 생성해 void생성자 호출 ㄱㄱ
생성자 호출할려고가봤더니..어..상속하고있어..그럼 AAA클래스로 ㄱㄱ
void생성자 호출하고 실행해..그담에 다시..BBB생성자 남은 몸체 실행한다.
그담에 bbb객체가 및 BBB클래스가  소멸해야하니까..~BBB()소멸자를 호출하고 없어지겠지. 그럼..상속한 AAA클래스 소멸하려면? 어떻게 하냐?.. 자  상속하고 있는 클래스의 객체는
자신이 상속하고 있는 클래스의 소멸자 까지도 호출한다. 
어제는 이해 안가더니 오늘은 이해가네 이말이 ㅋㅋ 결과를 보자.

사용자 삽입 이미지


오~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~케이

4. protected 멤버
간단히 설명하자.
일반적인 관계에서는 private의 성질을 가지고 있지만 상속관계에선 public의 성질을 가진다.

#include <iostream>

class AAA
{
private:
 int a;
protected:
 int b;
};

class BBB : public AAA{
public:
 void SetData(){
  a=10; //에러
  b=20;
 }
};

int main()
{
 AAA aaa;
 aaa.a=10; //에러
 aaa.b=20; //에러

 BBB bbb;
 bbb.SetData();
 return 0;
}


나는 니가 이걸 보고 protected멤버를 이해할수 있으면 소원이 없겠다..자세한설명생략

5. 세가지 형태의 상속
private상속
protected상속
public상속

사용자 삽입 이미지

상속을 하더라도 어떤 상속의 종류이던간에 private멤버는 접근 불가 라는 사실만 명심하자

6. 상속을 하는 이유
이건 따로 공부하자
블로그 이미지

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

,

precedence stack[MAX_STACK_SIZE];
static int isp[]={0,19,12,12,13,13,13,0};
static int icp[]={20,19,12,12,13,13,13,0};


void postfix()
{
 char symbol;
 precedence token;
 int n=0;
 int top=0;

 stack[0] = eos;
 for(token = get_token(&symbol,&n); token!=eos ; token=get_token(&symbol,&n))
 {
  if(token == operand)
   printf("%c",symbol);
  else if(token == rparen)
  {
   //왼쪽 괄호가 나올때까지 토큰들을 제거해서 출력시킴
   while(stack[top]!=lparen)
    printf_token(delete(&top));
   delete(&top);//좌괄호를버린다
  else
   //symbol의 isp가 token의 icp보다 크거나 같으면 symbol을 제거하고 출력시킴
   while(isp[stack[top]]>=icp[token])
    printf_token(delete(&top));
  add(&top,token);
  }

 }
 while((token = delete(&top))!=eos)
  printf_token(token);  // printf_token함수는 get_token함수의 수행하는 과정을 역으로만든다.
 printf("\n");
}


블로그 이미지

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

,