'전체 글'에 해당되는 글 64건

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");
}


블로그 이미지

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

,

6장정리

C++프로그래밍/C++ 2007. 7. 19. 09:35

1. 클래스와 const
int a=10;
int b=20;
const int a=20;  //n값의 변경 불가능
const int* p=&a;  // 포인터p가 가리키는 변수 a를 상수화 시킨다.
                        // 포인터p가 a를 바라봤을때 상수처럼 보인다..
                        // *p=30 (x) a=20(o)
int* const p=&a; // p라는 값 자체를 상수화 시킴
                       // 프로그램이 종료될때까지 p는 a라는 변수를 가리키고 있어야함.
                       // p=&b (x) *p=30(o)

const int* const p=&a;  //위의 두가지 기능을 다 가짐


멤버변수의 상수화와 초기화

#include <iostream>

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

class Student{
 const int id;
 int age;
 char name[20];
 char major[30];
public:
 Student(int _id, int _age, char* _name, char* _major)
 {
  id=_id;  
  age=_age;
  strcpy(name,_name);
  strcpy(major,_major);
 }
 void ShowData()
 {
  cout<<"이름 : "<<name<<endl;
  cout<<"학과 : "<<major<<endl;
  cout<<"학번 : "<<id<<endl;
  cout<<"나이 : "<<age<<endl;
 }
};

int main()
{
 Student jslee(20011520,23,"이재성","컴공");
 Student jejung(20050000,21,"흰둥이","간호");

 jslee.ShowData();
 cout<<endl;
 jejung.ShowData();
 return 0;
}

클래스 내의 멤버변수 int를 상수화 시키고 싶다..왜?  id는 하나밖에없잖아.변경할일없음.
그래서..저렇게 const로 상수화를 시켰어..그럼 id란 멤버는 쓰레기값을 가지게 될거라고,왜?초기화를 안했으니까 근데 const키워드를 붙였으니까 id란 멤버는 그 쓰레기값으로 정해졌어 이제 바꿀수 없어....일단 여기서 문제점이 생긴다..상수화를 시켰는데 메인함수에서 객체를 생성하면서 생성자에서 상수화된 id값의 변경을 시도 하고 있어..이거 안되지?에러나지..
일단 상수화가 됐는데 어떻게 바꾸냔 말야..자 그래서..제공하는 문법..짜잔.~~바로
멤버이니셜라이져   생성자의 매개변수선언부분 뒤에 :id(_id)를 추가시켜야돼.
그러면 초기화가되.ㅋㅋㅋ 멤버이니셜라이져는 생성자의 몸체보다 먼저실행이된다..
Student(int _id, int _age, char* _name, char* _major) :id(_id)
 {
  //id=_id;  
  age=_age;
  strcpy(name,_name);
  strcpy(major,_major);
 }


이렇게 말이지.. 사용법은 간단하지..참.구조체나 클래스의 멤버변수는 선언과 동시에
초기화가 불가능하단걸 잊지말라규~~!!

const 멤버함수

#include <iostream>

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

class Student{
 const int id;
 int age;
 char name[20];
 char major[30];
public:
 Student(int _id, int _age, char* _name, char* _major) :id(_id)
 {
  age=_age;
  strcpy(name,_name);
  strcpy(major,_major);
 }
 void ShowData() const
 {
  cout<<"이름 : "<<name<<endl;
  cout<<"학과 : "<<major<<endl;
  cout<<"학번 : "<<id<<endl;
  cout<<"나이 : "<<age<<endl;
 }
};

int main()
{
 Student jslee(20011520,23,"이재성","컴공");
 Student jejung(20050000,21,"흰둥이","간호");

 jslee.ShowData();
 cout<<endl;
 jejung.ShowData();
 return 0;
}

여기서는 ShowData라는 멤버 함수를 상수화 시키고 있어..
일단. 멤버함수가 상수화되면 이함수를 통해서 멤버변수의 값이 변경되는것은 허용되지않음.
무슨말인고 하니 자..예문을 하나 써봅시다.

 void ShowData() const
 {
  ( age=30; )
  cout<<"이름 : "<<name<<endl;
  cout<<"학과 : "<<major<<endl;
  cout<<"학번 : "<<id<<endl;
  cout<<"나이 : "<<age<<endl;
 }

이렇게 있단말야? 그럼 저함수는 일단 멤버함수니까 private의 멤버변수에 접근이 가능해
근데 잘봐 const로 선언되어있지.상수가 됐단말야 함수자체가..근데 함수 몸체부분에서
age를 30으로 바꿀려고해..안된다고..변경 불가..ㅇㅋ?

자 그럼...좀 업그레이드된 const멤버함수를 보자

class Count{
 int cnt;
public:
 Count() :cnt(0){}
 int* GetPrt() const{
  return &cnt;
 }
 void Increment(){
  cnt++;
 }
 void ShowData() const{
  ShowIntro();
  cout<<cnt<<endl;
 }
 void ShowIntro() {
  cout<<"현재 count의 값 :"<<endl;
 }
};
자 함수 2개에 const키워드가 붙어있어..이거 별 문제 없어 보이지?
일단 상수함수의 특징을 하나 써보고 설명하자.
상수화된 함수는 상수화 되지 않은 함수의 호출을 허용하지 않을뿐만 아니라 멤버변수의
포인터를 리턴하는것도 허용하지 않는다.

우선 GetPrt함수를 보면 함수내부에서 멤버변수를 조작하는 문장은 보이질 않아.
그런데 리턴하는 값이 먼지 잘봐..멤버변수의 주소 를 리턴하고 있어. 어.저기 함수이 특징을 보면 알겠지만 주소를 리턴한다는건 뭐야? 그 멤버변수를 조작할 가능성이 있다는거지..
그래서 저 함수는 잘못이 된거고.. 두번째 ShowData함수를 보면 상수화가 되었으니 함수내에서 멤버변수의 변경이 불가하지..근데 함수를 보면 다시 ShowIntro라는 함수를 호출해.
어머?! ShowIntro함수를 보면..언제든 함수 몸체부분에 멤버변수를 조작할수있는 문장을
집어넣을수가 있지..우리의 const 님께선 이것조차 허용을 안한단 거야..일단 ShowData가
상수화가 되었으면 상수화 되지않은 ShowIntro라는 함수를 호출못해..아니 안해..
자 그럼..저 클래스의 문제점을 고쳐보실까나
class Count{
 int cnt;
public:
 Count() :cnt(0){}
const int* GetPrt() const{
  return &cnt;
 }
 void Increment(){
  cnt++;
 }
 void ShowData() const{
  ShowIntro();
  cout<<cnt<<endl;
 }
 void ShowIntro() const{
  cout<<"현재 count의 값 :"<<endl;
 }
};

저렇게 파란색으로 되어있는 부분에 const를 넣어주면되..
그럼GetPrt함수는 리턴되는 주소값이 상수화가 되니까..멤버변수 조작이 불가능하구
ShowIntro함수도 상수화가 되었으니 ShowData에서 맘놓고 호출할수 있다규~~!!
아고 허리야..

const 객체

#include <iostream>

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

class AAA{
 int num;
public:
 AAA(int _num) : num(_num){}
 void Add(int n){
  num+=n;
 }
 void ShowData(){
  cout<<num<<endl;
 }
};

int main()
{
 const AAA aaa(10);
 aaa.Add(10);
 aaa.ShowData();
 return 0;
}

 객체 aaa를 생성하면서 상수화를 시키고 있어..자..이렇게 객체가 상수화가 되면 말야
어떠한 경로를 통해서든 멤버변수의 조작은 불가능 해질뿐만 아니라 상수화된 멤버함수만 호출이 가능해진다..일반 멤버함수는 멤버변수를 조작하지 않더라도 호출이 불가능해.

const와 함수오버로딩
void function(int n) const{...}
void function(int n) {...}
저 두개의 함수는 이름도 같고 매개변수로 전달받는 수도 같고 리턴타입도 같다
하지만 하나는 const로 상수화가 되었으므로 함수오버로딩이 가능하데..이런 미친.
그리고, 상수화된 함수보다 상수화 되지 않은 함수가 우선순위가 높댄다..

class AAA{
 int num;
public:
 AAA(int _num) : num(_num){}
 void ShowData(){
  cout<<"void ShowData()호출"<<endl;
  cout<<num<<endl;
 }
 void ShowData() const {
  cout<<"void ShowData() const호출"<<endl;
  cout<<num<<endl;
 }
};

int main()
{
 const AAA aaa(10);
 AAA bbb(20);
 aaa.ShowData();
 bbb.ShowData();
 return 0;
}


자 클래스 내에 함수 두개는 오버로딩되..위에서 설명했지..그럼 메인함수를 쳐보자고..ㅋㅋ
일단 객체 aaa를 생성하면서 상수화 시켜줬으니까 멤버변수조작 불가하고 상수화된 멤버함수만 호출한다..그럼 객체aaa에선 void ShowData() const호출  이값이 출력되겠지
근데 객체bbb를보자고..다른건 다 제껴두고 저건 함수 두개 전부 호출이 가능해..그럼..어떻게 한다? 그렇지..상수화 되지 않은 함수가 우선순위가 높으므로..상수화 되지 않은 함수가
호출이 되면서 void ShowData()호출  이게 출력이 된다규~~

2. 클래스와 static
아 힘들다..
힘드니까 일단 예제보자..ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ

#include <iostream>

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

int count = 1;

class Person{
 char name[20];
 int age;
public:
 Person(char* _name,int _age){
  strcpy(name,_name);
  age=_age;
  cout<<count++<<"번째 person 객체생성"<<endl;
 }
 void ShowData()
 {
  cout<<"이름 : "<<name<<endl;
  cout<<"나이 : "<<age<<endl;
 }
};

int main()
{
 Person p1("jslee",25);
 Person p2("girl",19);

 return 0;
}

이 예제의 의도는 객체가 생성될때마다 n번째 객체가 생성되었다는 메세지를 출력하고 싶은거야..헐..별.. 이뭐병. ㅋ
그래서 객체가 생성될때마다 참조해야되는(클래스내에서 생성자에서 참조하기위해)변수를 전역적으로 선언했지..어디서든 접근이 가능하게 근데 객체지향에선 전역이라는 개념이없데
그럼 어떻게해? 저기 전역으로 선언되있는 변수를 클래스 안에꾸겨 넣어야지뭐..함 넣어보자.

class Person{
 char name[20];
 int age;
 int count;
public:
 Person(char* _name,int _age){
  count=1;
  strcpy(name,_name);
  age=_age;
  cout<<count++<<"번째 person 객체생성"<<endl;
 }
 void ShowData()
 {
  cout<<"이름 : "<<name<<endl;
  cout<<"나이 : "<<age<<endl;
 }
};

자 넣었다.. private공간에 넣어주고 생성자내에서 1로 초기화 시켜줬어..
자 그럼? 객체를 생성할때~~마다..저기 저 count값은 1이야
그럼 뭐? 생성할때마다 1번째.1번째 1번째 ...객체를 생성했다고 지랄할거란 말이지..

사용자 삽입 이미지

요렇게..그럼 안되 우리가 원하는 건 뭐?
사용자 삽입 이미지
이걸 원하는거야 그지 ㅋㅋㅋ
어떻게 해야 저딴식으로 나올려나..하고 봤더니..static멤버 초기화 라는 문법이 존재해..헐
나쁜놈들.어떻게 해야 하느냐..

class Person{
 char name[20];
 int age;
 static int count;
public:
 Person(char* _name,int _age){
  strcpy(name,_name);
  age=_age;
  cout<<count++<<"번째 person 객체생성"<<endl;
 }
 void ShowData()
 {
  cout<<"이름 : "<<name<<endl;
  cout<<"나이 : "<<age<<endl;
 }
};

int Person::count=1;
//Person::count=1;
int main()
{
 Person p1("jslee",25);
 Person p2("girl",19);

 return 0;
}

일단 멤버변수를 static로 선언해주고..저렇게 static멤버를 초기화 해 주어야해
static 멤버의 특징..(멤버라고 하기엔 좀 거시기 하다)
1)메인 함수가 호출되기도 전에 메모리 공간에 올라가서 초기화가 된다. 따라서public로 선언이 된다면 객체생성이전에도 접근이 가능하다.
2)객체의 멤버로 존재하는것이 아니고 다만 선언되어있는 클래스내에서 직접 접근할수 있는 권한이 부여된 것이다.

아..어렵다..

#include<iostream>

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

class AAA{
 int val;
 static int n;
public:
 AAA(int a=0){
  val=a;
  n++;
 }
 void ShowData(){
  cout<<"val : "<<val<<endl;
  cout<<"n : "<<n<<endl;
 }
};

int AAA::n=1;

int main()
{
 AAA a1(10);
 a1.ShowData();
 AAA a2(20);
 a2.ShowData();

 return 0;
}

이걸 설명하자면..일단 static는 메인함수가 호출되기도 전에 메모리에 올라가서 초기화가
된다고 하였다!! 그지 1로 초기화가 되었을꺼야..저기 static멤버초기화 문장이 있잔아..
그럼..객체 a1을 생성하면서 1이 증가해..생성자내에서..2가됐지..객체a2를생성하면서3이되
그럼 ShowData란 함수로 각각 객체를 출력해 줄때..n의 값은 2,3이 출력이 된단 소리지..
여기서 두번째 특징인데..만약에 static멤버(?)가 객체의 멤버로 존재를 한다면..
두번의 객체생성과 출력과정에서 둘다 2가 출력이 되어야해..멤버로 존재를 한다면
근데 2,3이 출력이 됐단 소리는 static멤버(?)가 객체의 멤버라는 소리가 아니지..휴..
접근을 할수있는 권한만 있을뿐이지 절대 멤버변수가 아니다..참고로 이강의 보면서
내가이해했던건..static변수는 메모리상에딱 한번만 초기화되고 그담부터초기화안된다
이렇게 알고있던거적용하니까 별무리없이 이해되던데...;;

그래서.우리는 static멤버변수나 멤버함수는 객체의 멤버가 아니기때문에 클래스변수
클래스 함수
라고 한다.

3. 그외의 키워드
explicit & mutale
이건 걍 예제보고 이해하자..


끝~~!!!
블로그 이미지

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

,

#include <stdio.h>
#define MAX_STACK_SIZE 100
#define MAX_EXPR_SIZE 100

typedef struct{
 int key;
}element;
element stack[MAX_STACK_SIZE];
int top = -1;

//스택으로의 삽입함수
void add(int* top, element item)
{
 //전역 stack에 item을 삽입
 if(*top>=MAX_STACK_SIZE-1){
  stack_full();
  return ;
 }
 stack[++*top] = item;
}
//스택으로부터 삭제함수
element delete(int *top)
{
 if(*top == -1)
  return stack_empty();
 return stack[(*top)--];
}

/*-------------------------------------------------------------------------------*/
typedef enum {
 lparen, rparen, plus, minus, times, divide, mod, eos, operand
}percedence;
int stack[MAX_STACK_SIZE];
char expr[MAX_EXPR_SIZE];

//후위표기식 함수
int eval()
{/*전역 변수로 되어있는 후위표기식 expr을 연산한다.\0은 수식의 끝을 나타낸다.stack과top은 전역변수이다
 함수get_token은 토큰의 타입과 문자 심벌을 반환한다.피연산자는 한 문자로 된 숫자임을 가정한다.*/
 precedence token;
 char symbol;
 int op1,op2;
 int n=0; //수식 문자열을 위한 카운터
 int top = -1;
 token = get_token(&symbol,&n);
 while(token !=eos){
  if(token == operand)
   add(&top,symbol- '0'); //스택삽입
  else{
   op2=delete(&top); //스택삭제,삭제하면서 그자리에 있던값을 반환.
   op1=delete(&top); //스택삭제
   switch(token){
    case plus: add(&top,op1+op2); break;
    case minus: add(&top,op1-op2); break;
    case times: add(&top,op1*op2); break;
    case divide: add(&top,op1/op2); break;
    case mod: add(&top,op1%op2); break;
   }
  }
  token = get_token(&symbol,&n)
 }
 return delete(&top);//결과를 반환
}

precedence get_token(char* symbol, int* n)
{/*다음 토큰을 취한다. symbol은 문자표현이며 token은 그것의 열거된 값으로 표현되고 명칭으로 반환된다*/
 *symbol = expr[(*n)++];
 switch(*symbol){
  case '(' :return lparen;
  case ')' :return rparen;
  case '+' :return plus;
  case '-' :return miuns;
  case '/' :return divide;
  case '*' :return times;
  case '%' :return mod;
  case '\0':return eos;
  default  :return operand;//오류검사는 하지 않고 기본값은 피연산자
 }
}


블로그 이미지

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

,

5장정리

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

1. C++ & C 스타일 초기화

int a=10;
int b=20;

int a(10);
int b(20);

C++은 저런 두가지 형태의 초기화 방법을 제공하고 있다.
위 와 아래는 완전히 똑같은 코드이다.

#include <iostream>

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

class AAA
{
 int val;
public:
 AAA(int n){
  val=n;
 }
 void ShowData(){
  cout<<val<<endl;
 }
};

int main()
{
 AAA a1(10);
 a1.ShowData();

 AAA a2=20;     // 객체생성시도 적용이된다 그러나..밑의 설명으로...
 a2.ShowData();
 return 0;
}


그러나 객체생성시에는 AAA a2=20;은 묵시적인 변환이 일어난다 AAA a2(20); 으로..

2. 복사생성자의 형태

#include <iostream>

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

class AAA{
public:
 AAA(){
  cout<<"AAA()호출"<<endl;
 }
 AAA(int i){
  cout<<"AAA(int i)호출"<<endl;
 }
 AAA(const AAA& a){
  cout<<"AAA(const AAA& a)호출"<<endl;
 }
};

int main()
{
 AAA obj1;
 AAA obj2(10);
 AAA obj3(obj2);
 return 0;
}


세번째의 생성자의 형태를 가리켜 복사 생성자라고 한다. 메인 함수에서 obj3객체를
만들면서 obj2 객체를 인자로 전달하고 있다. 그렇기 때문에 객체를 받을수 있는 생성자가
필요하다. 그것이 바로 복사생성자인 AAA(const AAA& a) 이다.
레퍼런스로 접근 하기때문에 const키워드를 붙여주지 않으면 내용변경이 가능하기때문에
상수화(const)를 시켜준다.

3. 디폴트 복사 생성자

#include <iostream>

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

class Point{
 int x,y;
public:
 Point(int _x,int _y)
 {
  x=_x;
  y=_y;
 }
 void ShowData(){
  cout<<x<<' '<<y<<endl;
 }
};

int main()
{
 Point p1(10,20);
 Point p2(p1);

 p1.ShowData();
 p2.ShowData();

 return 0;
}


메인 함수의 두번째 p2객체를 생성하면서 p1객체를 인자로 전달하고 있다.
그러나 Point 클래스에서는 객체p1을 받을수 있는 생성자가 정의 되어있지 않다..
결과는 제대로 나온다..
사용자 삽입 이미지

이게 무슨 조화인가?  그렇다..저렇다는 얘기는 p1을 객체로 받아줄수 있는 생성자가
있다는 소리다.. 그것이 바로 디폴트 복사생성자 이다.

디폴트 복사 생성자는 멤버변수대 멤버변수의 복사를 수행한다.


즉....

#include <iostream>

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

class Point{
 int x,y;
public:
 Point(int _x,int _y){
  x=_x;
  y=_y;
 }
 Point(const Point& p1){
  x=p1.x;
  y=p1.y;
 }

 void ShowData(){
  cout<<x<<' '<<y<<endl;
 }
};

int main()
{
 Point p1(10,20);
 Point p2(p1);

 p1.ShowData();
 p2.ShowData();

 return 0;
}


파란색 글씨 형태의 생성자가 하나가 추가되었단 소리다..저것은 디폴트 복사생성자와
같은 형태이다.저렇게 써주지 않아도 디폴트로 저런 형태의 복사생성자가 삽입이된다.

4. 깊은 복사
우선 아주 긴 예문 하나를 보자..

#include <iostream>

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

class Person
{
 char* name;
 char* phone;
 int age;
public:
 Person(char* _name, char* _phone, int _age);
 ~Person();
 void ShowData();
};

Person::Person(char* _name, char* _phone, int _age)
{
 name=new char[strlen(_name)+1];
 strcpy(name, _name);

 phone=new char[strlen(_phone)+1];
 strcpy(phone,_phone);

 age=_age;
}
Person::~Person()
{
 delete []name;
 delete []phone;
}
void Person::ShowData()
{
 cout<<"name : "<<name<<endl;
 cout<<"phone: "<<phone<<endl;
 cout<<"age  : "<<age<<endl;
}


int main()
{
 Person p1("jslee","010-1010-1010",25);
 Person p2=p1;
 
 return 0;
}


너무길다..ㅠㅠ 여기서 하고픈 이야기는 뭘까?까먹었어 ㅅㅂ..
메인함수에서 p1이라는 객체를 생성함과 동시에 초기화 시켜주고 있고..
p2라는 객체를 만들면서 p1객체를 인자로 넘겨주고 있다.
p1을 받을수 있는 생성자가 없다고 생각하는가?
조기 위에서 배운 디폴트 복사생성자가 호출된다.. ㅋㅋ바보 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ

그럼.생각해봅시다..
p1이나 p2나 두개다 heap에 올라가있는 데이터를 가리키는 포인터가 있다..그런데 p1의 포인터 값. 즉p1이 가리키는 데이터의 주소값을 고대로~~p2로 복사했으니까..
p1이나 p2나 가리키는 대상은 같을것이라고..맞지? 그림 하나 보자

사용자 삽입 이미지

<그림 출처> 내가 돈주고 산 열혈강의 C++ PPT자료에서 캡춰!


자..우리는 여기서 스택의 구조를 다시한번 상기해야될듯 하다 ㅋ
스택은 First in Last out. Last in First out. 똑같은 말인데 어떤걸쓰야되나??후입선출 이래.
암튼 저래..먼저 들어간건 나중에 나오고 나중에 들어간게 젤 먼저 나와. ㅋㅋ
그럼..위에서 보면 p2가 나중에 생겼으니까..먼저 사라질꺼라고..근데 저기 그림봐봐.
p2도 p1에서 동적메모리 할당해서 생성된 데이터를 가리키고 있다고..
p2객체가 소멸하면서 소멸자를 부른다? 그럼 소멸자에선 저기 heap의 메모리를 반환해..
delete 키워드로 반환했으니까..자.. 그럼p1은 도대체 누굴 가리켜야 하지? 미쳤군 ㅋㅋ

이게 문제야..디폴트 복사생성자는.. 그럼 어떻게 해줘야 저 문제를 해결할수 있을까?
또 그림하나 보자..

사용자 삽입 이미지

<그림 출처> 내가 돈주고 산 열혈강의 C++ PPT자료에서 캡춰!


 저딴식으로 해주면 문제가 없겠지? 그럼 어떻게 해야되? 그렇지 복사생성자를 하나 정의해.

ㅇㅋㅂㄹ??
그럼 자 간다. 두눈 똑바로 뜨고 쳐다봐라 ㅋㅋ

Person::Person(const Person& p)
{
 name=new char[strlen(p.name)+1];
 strcpy(name,p.name);

 phone=new char[strlen(p.phone)+1];
 strcpy(phone,p.phone);
 age=p.age;
}

이딴식으로 복사생성자의 정의를 해주면되..자 그럼 p2도 복사생성자를 호출하면서
heap새로운 영역을 할당하고 그놈을 가리키겠지..
머리 잘굴려라 헷갈릴라..내가 볼거 내가 쓰면서 내가 헷갈리네 ㅋㅋ아놔
이게 바로 DEEP COPY 깊은 복사다..아까 그건 얕은 복사~~
Do u Understand? ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

아...전반적인건 마무리 됐다..여기까지만 이해하고 있어도 된데. 우리 윤성우 사부님이.
자..이제 좀 빡신거 들어가보자!

5. 복사생성자가 호출되는 시점
복사생성자가 호출되는 시점은 세가지의 형태로 분류할수 있다.
저기 위에처럼 기존에 생성된 객체로 새로운 객체를 초기화 하는 경우.
두번째로 함수 호출시 객체를 값에 의해 전달 하는 경우
세번째는 함수 내에서 객체를 값에 의해 리턴 하는 경우..

아..저거만 봐도 오바이트 쏠려 ㅅㅂ..ㅋㅋㅋㅋ
첫번째 경우는 안써놔도 알겠고...그럼 두번째 부터 가자..자 긴장해라..

일단 함수호출과정을 세분화 시켜보재..책에서...나도 해야지..나중에까먹을라.
함수도 호출이 되면서 매개변수로 전달되는 인자값의 메모리 공간을 할당을 하겠고.
그다음에 전달된 인자를 복사하겠지..call-by-value의경우에 말이야..

자..그럼 들어간다 두번째 함수 호출시 객체를 값에 의해 전달 하는 경우

#include <iostream>

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

class AAA{
 int val;
public:
 AAA(int i){
  val=i;
 }
 AAA(const AAA& a){
  cout<<"AAA(const AAA& a)호출"<<endl;
  val=a.val;
 }
 void ShowData(){
  cout<<"val : "<<val<<endl;
 }
};

void function(AAA a)
{
 a.ShowData();
}

int main()
{
 AAA obj(30);
 function(obj);

 return 0;
}

자자..긴장해라 function함수 보면 분명히 전역 함수다..그지?
메인 함수에서 function함수 호출하면서 만들어진 obj객체를 인자로 전달했어..
그럼 전역함수 function에서 그 함수를 a로 받어..저건 call-by-value야..
그럼. function함수 내에 객체 obj와 똑같은 메모리가 할당이 되었을껄?그지..
자 그럼 객체 생성과정에선 1)메모리할당 ,2)생성자의호출 이되어야 비로소 객체라는 이름을 가질수 있다고 배웠다 기억안나?ㅅㅂ.. 아무튼 메모리는 할당되었는데..생성자를 호출해야되..어떻게해?그냥 function함수에 단순히 객체만 복사되어 전달이 되었는데 말여..
객체obj를 복사하는 과정에서 단순히 값만 복사하는게 아니고 복사생성자의 호출을 통해
이 과정을 처리한데...자 그럼 먼 소리냐? 일단 메모리 공간을 할당받았으니까 저것들이 들어있을거란 말야..변수의 값은 아직몰라..근데 거기서 복사생성자를 호출하면서~!
복사의 대상이 되는 객체obj를 인자로 전달해...아 먼소리야 젠장..그림 하나 보고 넘어가.

사용자 삽입 이미지
<그림 출처> 내가 돈주고 산 열혈강의 C++ PPT자료에서 캡춰!

저렇게 된다고..아 골치아프다..좀 생각더해보자..
자 그다음에..세번째 함수 내에서 객체를 값에 의해 리턴 하는경우.

리턴되는 값은 받아주는 변수가 없더라도 함수를 호출한 영역으로 복사되어넘어간다.
뜬금없이 뭔소리..책(203쪽)참고해라..

#include <iostream>

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

class AAA{
 int val;
public:
 AAA(int i){
  val=i;
 }
 AAA(const AAA& a){
  cout<<"AAA(const AAA& a)호출"<<endl;
  val=a.val;
 }
 void ShowData(){
  cout<<"val : "<<val<<endl;
 }
};

AAA function(void)
{
 AAA a(10);
 return a;
}

int main()
{
 function();    //.ShowData();
 return 0;
}

음..자..AAA클래스형 전역함수 function을 보자..메인함수에서 함수를 호출한다.
그럼 function에서 a객체를 생성하면서 10으로 초기화해주고 그 값을 복사해서
리턴해주겠지..복사해서 리턴해준데..그럼 복사본이 어떻게 객체냐?
메모리 공간은 할당받았을테고..생성자호출은??
복사본이 리턴이 되면서 복사생성자를 호출해서 함수function에서 생성된 객체의 값을
인자로 전달받지.아..ㅠㅠ복잡하다..결국 2번째와 3번째 케이스는 비슷한 케이스라고..
할수가 있지 마지막 그림 하나 더 보자..

사용자 삽입 이미지

<그림 출처> 내가 돈주고 산 열혈강의 C++ PPT자료에서 캡춰!

자 여기까지 5장 마무리..좀더 고민해보자..흠..
이거쓰는데만 1시간걸렸다 ㅠㅠㅠㅠ

블로그 이미지

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

,
stack

Last In First Out(후입 선출의 형태)


#define MAX_STACK_SIZE 100
typedef struct{
           int key;
}element;

element stack[MAX_STACK_SIZE];
int top = -1;
로 정의 되어있을때.

stack에로의 삽입 함수
void add(int* top,element item)
{/*전역스택에 item을 삽입*/
  if(*top>=MAX_STACK_SIZE-1){
   stack_full()'
   return;
}
 stack[++*top];
}

stack으로부터 삭제 함수
element delete(int* top)
{
   if(*top == 1)
   return stack_empty(); //오류키를 반환
 return stack[(*top)--];
}

설명: 스택으로 삽입 할때는 스택을 하나증가시킨후 삽입
        삭제시는 나중에 들어간 item을 빼낸후 스택을 하나 감소 시킨다.

일반적인 배열스택프로그램



동적연결된 스택(linked list로 구현된 스택)

#define MAX_STACKS 10
typedef struct
{
 int key;
}element;
typedef struct stack* stack_pointer;
typedef struct stack{
 element item;
 stack_pointer link;
};
stack_pointer top[MAX_STACKS];



삽입 함수

void add(stack_pointer *top,element item)
{//스택의 top에 원소를 삽입
 stack_pointer temp=(stack_pointer)malloc(sizeof(stack)); //메모리동적할당
 if(IS_FULL(temp)){
  fprintf(stderr,"메모리풀\n");
  exit(1);
 }
 temp->item=item;
 temp->link=*top;
 *top=temp;
}

삭제함수

element delete(stack_pointer,element item)
{//스택으로부터 원소를 삭제
 stack_pointr temp=*top;
 element item;
 if(IS_EMPTY(temp)){
  fprintf(stderr,"스텍이 비었음\n");
  exit(1);
 }
 item=temp->item;
 *top=temp->link;
 free(temp); // 메모리반환
 return item;
}

연결된 스택 프로그램
블로그 이미지

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

,

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선언은 정보은닉에 위배된다.

블로그 이미지

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

,