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

3장정리

C++프로그래밍/C++ 2007. 7. 13. 21:36

1. class
class란 C의 구조체에 멤버로 함수를 넣은것?(이렇게 설명해도 될려나)
멤버로 멤버변수 및 멤버 함수를 갖는것..class

#include <iostream>

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

struct Account{
 char accID[20];
 char secID[20];
 char name[20];
 int balance;
};

void Deposit(Account &acc,int money)
{
 acc.balance+=money;
}
void Withdraw(Account &acc,int money)
{
 acc.balance-=money;
}


이렇게 되어있는 C스타일의 구조체와 함수를 하나로 묶어줄수 있다..무엇으로? class로

struct Account{
 char accID[20];
 char secID[20];
 char name[20];
 int balance;

 void Deposit(int money)
 {
  balance+=money;
 }
 void Withdraw(int money)
 {
  balance-=money;
 }
};


아직은 class가 아니다..C++에서 지원하는 구조체의 형태이다.
C++에서는 구조체 안에 함수가 존재할수 있도록 허락해준다..좋은놈..
자 드디어 우리의 class등장!!

class Account{
public:
 char accID[20];
 char secID[20];
 char name[20];
 int balance;

 void Deposit(int money)
 {
  balance+=money;               // balance는 class내의 변수를 참조한다.(내부접근)
 }
 void Withdraw(int money)
 {
  balance-=money;
 }
};


class다..구조체와 다른게 없다고 생각되는가?..
선언만 struct --->class로 해줬고..public하나 해줬고..
그게 차이다. 구조체는 기본값이 public이다. 즉 외부접근이 허용이된다.
그러나 class는 기본값이 private이다..저렇게 위처럼 public를 허용해 주지 않으면
내부접근만 가능하고..기본적으로는 private이기때문에 외부접근이 불가능하다.
그리고class는 더이상 변수라는 표현을 쓰지 않고 "객체"라는 표현을 쓴다..

멤버함수의 외부정의
위에처럼 정의를 하면 class의 가독성이 현저하게 떨어지므로 함수의 선언만 하고
정의는 외부에다가 할수가 있는데 그때 사용하는 것이  범위지정연산자(::)이다.

class Account{
public:
 char accID[20];
 char secID[20];
 char name[20];
 int balance;

 void Deposit(int money);
 void Withdraw(int money);
 };

void Account::Deposit(int money)
 {
  balance+=money;
 }
 void Account::Withdraw(int money)
 {
  balance-=money;
 }



짜잔~~어떤가 class의 가독성이 좀더 화사해지지 않았나?
음..좋아 ㅋㅋ

class의 멤버 함수를 내부에 정의한다는것은 함수를 inline화 시킨다는 의미가 있는데
저딴식으로 함수선언만 내부에 놓고 정의는 외부로 보내면 어떻게 하지?
간단하다..외부함수선언 앞에 inline 키워드만 붙여라..그럼 끝..
더불어 C++컴파일러는 똑똑해서 저렇게 inline을 선언해도 알아서 판단해서
알아서 프로그램 돌린다..자세한건 빌게이츠에게로..
블로그 이미지

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

,



typedef struct node* node_pointer;
typedef struct node{
           node_pointer llink;
           element data;
           node_pointer rlink;
};



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


typedef int element;
typedef struct DlistNode{
 element data;
 struct DlistNode* llink;
 struct DlistNode* rlink;
}DlistNode;

void init(DlistNode* phead)
{
 phead->llink=phead;
 phead->rlink=phead;
}

void display(DlistNode* phead)
{
 DlistNode* p;
 for(p=phead->rlink ; p!=phead ; p=p->rlink)
 {
  printf("<---| %x | %d | %x |--->\n",p->llink , p->data, p->rlink);
 }
 printf("\n");
}

void dinsert_node(DlistNode* before , DlistNode *new_node)
{
 new_node->llink = before;
 new_node->rlink = before->rlink;
 before->rlink->llink = new_node;
 before->rlink = new_node;
}
// 삽입함수


 

void dremove_node(DlistNode *phead_node , DlistNode* removed)
{
 if(removed == phead_node) return;
 removed->llink->rlink = removed->rlink;
 removed->rlink->llink = removed->llink;
 free(removed);
}
//삭제함수

main()
{
 DlistNode head_node;
 DlistNode* p[10];
 int i;

 init(&head_node);
 for(i=0 ; i<5 ; i++)
 {
  p[i] = (DlistNode*)malloc(sizeof(DlistNode));
  p[i]->data = i;

  dinsert_node(&head_node,p[i]);
 }
 dremove_node(&head_node,p[4]);
 display(&head_node);
}

블로그 이미지

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

,

2장정리

C++프로그래밍/C++ 2007. 7. 13. 10:01

1. const 키워드 사용법

const int n=10;  //n값의 변경 불가능
int a=10;
int b=20;
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;  //위의 두가지 기능을 다 가짐

                       
2. bool
bool형 변수는 true 와 false 키워드가 기본적으로 제공된다.
int형의로 변환시 1과0 이 되지만 true 와 false 는 bool형 데이터로 인정해줘야함.

3. 레퍼런스(reference)
int val=0;
int &ref=val;

val이라는 변수이름에 ref라는 별명을 붙여준개념.
레퍼런스와 변수는 생성방법에서만 차이를 보일뿐 일단 만들어지고 나면 완전히 같음.

다만..
int &ref;
int &ref=10;
이런식의 선언은 불가..변수가 선언된다음에야 레퍼런스 선언이 가능.

레퍼런스를 이용한 call-by-reference.

#include <iostream>

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

void swap(int &a,int &b)  // 레퍼런스의 형태로 받아주므로
{                                  // 스왑연산이 가능하다. 
 int temp=a;                   // 일반함수형태의 값의 복사(call-by-value)가 아닌
 a=b;                            // 레퍼런스로 연산하는 call-by-reference
 b=temp;
}

int main()
{
 int val1=10;
 int val2=20;

 cout<<"val1 = "<<val1<<endl;
 cout<<"val2 = "<<val2<<endl;

 swap(val1,val2);                  // 함수호출시 일반함수와 차이를 알아보기힘듬
 cout<<"val1 = "<<val1<<endl;
 cout<<"val2 = "<<val2<<endl;

 return 0;
}


단점은 함수호출시 일반함수와의 차이를 알아보기 힘들다.

장점은 함수호출시 매개변수의 복사가 이루어지지 않고 레퍼런스로
연산이 가능하기때문에 메모리공간의 효율성
다만..함수의 매개변수에서 상수화시켜 받아주는 센스가 필요함.

ex) void ShowData(const person &p)

설명->매개변수로 받는값에 p라는 별명을 붙여준 값을 상수화하여 함수를연산하라.

레퍼런스의 return

#include <iostream>

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

int& increment(int &val)   // 변수n에 val이라는 별명을 붙여주고
{
 val++;
 return val;                    // 그 별명값을 리턴하라..리턴형은 int의 레퍼런스
}

int main()
{
 int n=10;
 int &ref=increment(n);   // int의 레퍼런스형태로 함수를 받아주고 있다.

 cout<<"n  : "<<n<<endl;
 cout<<"ref: "<<ref<<endl;

 return 0;
}


레퍼런스 리턴시 잘못된 함수

#include <iostream>

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

int& function(void)
{
 int val=10;
 return val;
}

int main()
{
 int &ref=function();
 cout<<"ref: "<<ref<<endl;

 return 0;
}


main함수 내에 변수가 선언이 되지 않았고..
function함수 내에서 선언된 int형 변수 val은 함수가 호출되고 종료되는 순간에
스택에서 사라지므로 딱봐도 말이 좀 안된다...
지역변수를 레퍼런스로 리턴하는 일은 없어야 한다.

4. new & delete
C언어의 malloc,free함수를 C++에서는 new와 delete가 대신한다.
C    : int* arr=(int*)malloc(sizeof(int)*size));  이런식으로 선언함
        malloc함수가 리턴하는 값이 void형 포인터이기때문에 형변환을 해주어야함.
C++ : int* arr = new arr[size];
        new연산자는 용도에 맞게 포인터를 반환하므로 형변환을 할 필요가없다.
        다만 배열의 메모리반환시에
        delete []arr;
        로 선언을 해주어야한다.2차원이건 3차원이건 같다.

메모리 할당 실패시 new연산자는 NULL포인터를 리턴한다.

블로그 이미지

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

,


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

typedef int element;
typedef struct ListNode{
 element data;
 struct ListNode* link;
}ListNode;

/*void error(char* message)
{
 fprintf(stderr,"%s\n",message);
 exit(1);
}
*/
ListNode* create_node(element data, ListNode* link)
{
 ListNode* new_node;
 new_node = (ListNode *)malloc(sizeof(ListNode));
 if(new_node == NULL){
  fprintf(stderr,"메모리할당에러");
 exit(1);
 }
 new_node->data=data;
 new_node->link=link;
 return (new_node);
}

void insert_first(ListNode **phead , ListNode* node)
{
 if(*phead==NULL){
  *phead=node;
  node->link=node;
 }
 else
 {
  node->link=(*phead)->link;
  (*phead)->link=node;
 }
}
void insert_last(ListNode **phead , ListNode* node)
{
 if(*phead==NULL){
  *phead=node;
  node->link=node;
 }
 else
 {
  node->link = (*phead)->link;
  (*phead)->link = node;
  *phead = node;
 }
}
void display(ListNode *head)
{
 ListNode *p;
 if( head == NULL) return;

 p = head;
 do{
  printf("%d->",p->data);
  p = p->link;
 
 }while(p != head);
 printf("\n");
}
main()
{
 ListNode *list1=NULL;

 insert_first(&list1,create_node(10,NULL));
 insert_first(&list1,create_node(20,NULL));
 insert_last(&list1, create_node(30,NULL));
 display(list1);
}

블로그 이미지

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

,

1장정리

C++프로그래밍/C++ 2007. 7. 12. 09:49

1. C++입 출력방식

<iostream.h>방식
cout<<출력대상1<<출력대상2 ;
cin>>입력대상1>>입력대상2 ;

endl; 개행및 출력버퍼 비워줌

<iostream>방식
std::cout<<출력대상1<<출력대상2 ;
std::cin>>입력대상1<<입력대상2 ;

std::endl;

같으나 새로개정된ANSI표준  앞에std::를 붙여주거나
using std::cout;
using std::cin;
using std::endl;
로 정의 가능
using namespace std::로도 한번에 정의 가능하나 나중에 이름충돌가능성있음

2. 함수 오버로딩
C++에서는 함수 오버로딩이 지원됨.
즉, 함수의 이름이 같아도 컴파일러가 알아서 판단.
하지만 매개변수의 개수 및 타입이 달라야함.
리턴 타입만 다르고 함수이름 및 매개변수가 같을경우에도 적용이 안됨.

3. 디폴트 매개변수
int function(int a=0)
{
   return a*7;
}
에서와 같이 함수 매개변수 부분에 디폴트 매개변수사용 가능.
매개변수가 없을경우 0을 디폴트 값으로 설정해줌
매개변수값이 있을경우에는 그값을 설정.

주의할점

int function()
{
  return 10;
}
int function (int a=10)
{
  return a+1;
}
int main()
{
  std::cout<<function()<<std::endl;
  return 0;
}

이럴경우 함수 2개가 동시에 호출이 되기때문에 주의해야함
함수오버로딩과 디폴트 매개변수를 동시에 잘못정의.

4. inline 함수
C언어의 매크로와 같은 기능을 가진 함수
다만 매크로는 전처리기에 의해 처리가 되고
inline함수는 컴파일러에 의해 처리가 됨.
inline함수는 컴퓨터가 판단하여 성능향상에 해가된다고 판단될 경우
그냥 무시해버리기도한다.
함수이름앞에 그냥 inline 키워드를 붙여주기만 하면됨.

5. namespace
이름 공간이 다르면 같은 변수나 함수의 선언이 허용된다.
예를 들어

#include <iostream>

namespace A_COM
{
 void function(void)
 {
  std::cout<<"A.COM에서 정의한 함수"<<std::endl;
 }
}

namespace B_COM
{
 void function(void)
 {
  std::cout<<"B.COM에서 정의한 함수"<<std::endl;
 }
}

int main()
{
 A_COM::function();
 B_COM::function();
 return 0;
}

이런 식으로 가능(이건 이렇게 예제로 밖에 설명할수가 없군 -_-;)

cout,cin,endl 도 std라는 네임스페이스 안에서 정의된 것들.

6. using
1번에서 설명
return 1; ㅋㅋㅋ

7. ::  범위지정 연산자
전역변수에 접근하기 위해서도 사용됨

#include <iostream>

int val;

int main()
{
  int val=10;
  ::val+=1;
  return 0;
}


 

#include <iostream> //구버젼은 <iostream.h>

//usiong namespace std;  가능
using std::cout;
using std::endl;

//네임스페이스 생성
namespace JS
{
 int function(int a,int b,int c)
 {
  std::cout<<"네임스페이스function(int a,b,c)"<<std::endl;
  return 0;
 }
}
//using
using JS::function;


//함수오버로딩
int function(int a=0)// 디폴트매개변수선언 : 매개변수에 전달되는값이 없으면 0 으로 초기화
{
 std::cout<<"function(int a)"<<std::endl;
 return 0;
}

int function(int a, int b)
{
 cout<<"function(int a,int b)"<<endl;
 return 0;
}
//inline 함수 : 매크로와 달리 컴파일러에 의해 처리되기때문에 최적화가 용이
inline int SQUARE(int x)
{
 return x*x;
}

int main()
{
 JS::function(1,2,3);//JS네임스페이스의 함수 호출
 function(1,2,3);
 function(1);
 function(1,2);

 return 0;
}

블로그 이미지

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

,


ListNode* reverse(ListNode* head)
{
     ListNode* p, *q, *r;
     p=head;
     q=NULL;
     while(p!=NULL)
    {
        r=q;
        q=p;
        p=p->link;
        q->link=r;
    }
    return q;
}


사용자 삽입 이미지


위에껀 내가 95퍼센트 이해한 거


ListNode* reverse(ListNode* head)
{
     ListNode* p, *c, *n;
     p=head;
     c=NULL;
     while(p!=NULL)
    {
        n=c;
        c=p;
        p=p->link;
        c->link=n;
    }
    return c;
}


여기까지가 본인이 작성함..





아래내용은 내가 더 잘 이해하기위해 다른 블로그 글을 추가함


-----------------------------------------------------------------------------------



출처 : 네이버 oasess님




일단 변수초기화 부분을 실행하면 아래와 같이 되겠지요.

(이미지는 Paran계정에 올려두었는데, 사이즈가 안맞아 잘려 보이네요. 클릭해서 원본 사이즈로 보시기 바랍니다.)




변수 p에는 Head, n에는 null값을, c는 초기화하지 않았으므로 쓰레기주소값을 가집니다.

( 나중에 보시면 아시겠지만, 변수 c는 이곳에서 null로 초기화되어야 합니다 )

 

다음에 실질적인 while()문을 최초 실행하게 되면...




위와같이 변수의 값들이 변경됩니다.

그림설명을 잠깐 드리자면, 회색 화살표는 이전에 가리키던 값이고, 검은색화살표가 현재 변경된 값입니다. 코드에 적힌 숫자와 그림에 적힌 숫자를 매치하면서 살펴보시면 이해가 될 것입니다.

 

(3)번을 보시면, p = p->link 를 실행하는데, p는 현재 1번 노드를 가리키고 있고, p->link 2번노드를 가리키므로, p = p->link 는 현재의 다음노드를 가리키라는 의미입니다.

(4)번 역시 (3)번과 일맥상통합니다. 이하 설명은 생략하고 이미지로 대신하도록 하겠습니다.

 

위의 그림이 좀 복잡하므로(화살표가 너무 어지럽죠?) 좀 정리를 해 보도록 하죠.

아래의 그림과 같습니다.


이해 되시리라 생각합니다.

다시한번 while()문을 적용해보겠습니다



설명은 생략하겠습니다. 복잡한 위의 그림을 다시한번 정리해보도록 하겠습니다.

아래와 같습니다.



다시 while()문을 적용합니다




다시 정리..^^;




슬슬 끝이 보이는군요. ^^

다시한번 while()문을 적용토록 하겠습니다.




정리 들어갑니다





이로서 완전한 리버스(Reverse)가 적용되었습니다. while()문의 조건이 p!=null 인데, 현재 p의 값이 null 이므로 루프를 빠져나와 c(리버스가 적용된 연결리스트의 헤드값)를 리턴하면서 함수는 종료합니다.

 

위의 그림을 보시면 아시겠지만, 마지막노드의 link가 쓰레기값을 가리키면서 끝나게 되지만 이는 잘못된 것입니다. 따라서 맨 처음에 언급드린 바와 같이, 변수 c또한 null값으로 초기화하는 코드가 필요하게 됩니다.

 

 

부디 이해되셨길 바랍니다. 수고하세요.

 


블로그 이미지

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

,