반응형

만약 4*4 행렬일 경우

 

<<2  ==  *4 와 같으며 row |  는 0~3 범위의 인덱스가 됨으로 이와 같은 비트 오퍼레이션 연산이 가능하다

 

T & element (int row, int col) {
        return _array[row | (col<<2)];
    }

 

 

 void set_row(int r, const vec4<T> & t) {
        for (int i = 0; i < 4; i++) element(r,i) = t[i];
    }

    void set_column(int c, const vec4<T> & t) {
        for (int i = 0; i < 4; i++) element(i,c) = t[i];
    }

반응형
반응형

class Matrix{

private :

union {
        struct {
            T _11, _12, _13, _14;   // standard names for components
            T _21, _22, _23, _24;   // standard names for components
            T _31, _32, _33, _34;   // standard names for components
            T _41, _42, _43, _44;   // standard names for components
        };
        T _array[16];   

    };

}



15개의 배열과 각 _숫자   는 순서적으로 이름은 다르지만 같은곳을 공유하게된다

반응형
반응형

furyheimdall.springnote.com


MFC 나 API 에서는 유저에게 숨겨진 실제 엔트리포인트가  CRTWinMain 으로 콘솔과는 전혀 다르게 사전에 초기화가 됩니다.

하지만 MFC 나 API 환경에서 간단한 수치를 디버깅 하기 위해선 익숙해지더라도 참 번거롭기 마련입니다.

이런 답답한 부분때문에 네이버에서 콘솔을 띄우는 방법에 대해 검색해보았는데 의외로 많더군요.

그중에 가장 좋다 싶은 방법을 선택하여 클래스로 만들어보았습니다만... 민망할 정도로 사용상 편의점이 아니고서는 썰렁하네요.


사용자 삽입 이미지

일단 기본적인 방법에 대해서 살펴보겠습니다.

  1. AllocConsole();   //콘솔을 할당    
  2. FreeConsole();   //콘솔을 해제   

  1. /* stdout 에 대한 설정 */  
  2. hCrt = _open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE),_O_TEXT);    
  3. hf = _fdopen(hCrt,"w");  
  4. *stdout = *hf;  
  5. setvbuf( stdout, NULL, _IONBF, 0 );   

기본적으로 위의 두가지 블럭안에 있는 코드만으로 콘솔을 띄우고 난 후 printf 나 cout 을 이용하여 출력, 콘솔을 닫는 작업들이 가능합니다.


그럼 완성된 클래스를 보지요.


  1. //[MFCConsole.h]    
  2. #pragma once  
  3. #include <io.h>  
  4. #include <fcntl.h>  
  5. #include <stdio.h>  
  6. #include <iostream>  
  7. using namespace std;    
  8. #define DEBUG_PRINT(x) printf("%s - Value : %ld[0x%lx], Address : 0x%lx\n",#x,x,x,&x)    
  9.   
  10. class CMFCConsole  
  11. {  
  12. public:  
  13.  CMFCConsole(void);  
  14.  ~CMFCConsole(void);  
  15.  void OpenConsole(void);  
  16.  void CloseConsole(void);   
  17. };  

  1. //[MFCConsole.cpp]    
  2. #include "StdAfx.h"  
  3. #include "MFCConsole.h"    
  4. CMFCConsole::CMFCConsole(void)  
  5. {  
  6.  //초기화 콘솔을 열고 stdout, stdin 설정후 콘솔을 닫음  
  7.  AllocConsole();  
  8.  int hCrtout = _open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE),_O_TEXT);  
  9.  FILE *hout = _fdopen(hCrtout,"w");  
  10.  *stdout = *hout;    
  11.  int hCrtin = _open_osfhandle((long)GetStdHandle(STD_INPUT_HANDLE),_O_TEXT);  
  12.  FILE *hin = _fdopen(hCrtin,"r");  
  13.  *stdin = *hin;  
  14.  setvbuf( stdin, NULL, _IONBF, 0 );  
  15.  FreeConsole();  
  16. }    
  17. CMFCConsole::~CMFCConsole(void)  
  18. {  
  19.  //해제  
  20.  _fcloseall();  
  21. }    
  22. void CMFCConsole::OpenConsole(void)  
  23. {  
  24.  //콘솔을 열고 타이틀을 셋팅, 열린 콘솔이 있을경우 무시됨  
  25.  if(AllocConsole()){  
  26.   SetConsoleTitle(_T("Furyheimdall's Console"));  
  27.   printf("-----------------------------------------------\n");  
  28.   printf("             Console Class for MFC             \n");  
  29.   printf("              Made by Furyheimdall             \n");  
  30.   printf("-----------------------------------------------\n\n");  
  31.  }  
  32. }    
  33. void CMFCConsole::CloseConsole(void)  
  34. {  
  35.  //콘솔을 닫음. 열린 콘솔이 없을경우 무시  
  36.  FreeConsole();  
  37. }   

반응형
반응형

http://boxbop.tistory.com/45

 

memmove : 어떠한 경우에도 사용할 수 있는 메모리 복사 함수
cf) void* memmove(void* dest, const void* src, sizt_t len);
->매개변수 src로 전달된 값을 시작주소로 하여 len바이트를 읽어 들여서, 매개변수 dest로 전달된 주소에 복사를한다.

 memcpy : 제한된 상황에서의 메모리 복사
cf) void* memcpy(void* restrict dest, const void* restrict src, size_t len);
->memmove와 기능상 차이는 없다. 그러나 dest와 src가 restrict으로 선언되었다. 따라서 함수가 호출되면서 dest와 src로 전달된 주소 값의 메모리는 각각 dest와 src로만 접근이 가능해야 한다.(원본대상과 복사본이 겹치는 경우 사용할 수 없다)

반응형
반응형


부동 소수점 기수법 
floating-point representation system, 浮動小數點記數法  [컴퓨터]


지수를 이용하는 기수 표기법. 매우 큰 수 및 매우 작은 수를 표현하는 데 사용된다. 부동 소수점 기수법에서 수는 부동 소수점 부분과 지수 부분(exponent)의 2부분으로 나뉘어 표현된다. 부동 소수점 부분을 가수부(mantissa)라고도 한다. 가수부는 그 수의 숫자 부분을 지정하고 지수부는 그 수의 크기(소수점의 위치)를 지정한다. 부동 소수점 기수법에서 어떤 수 (n)이 가수부 (a)와 지수부 (b)로 표현되면 n=a×rb가 된다(r는 기수). 예를 들면 10진수 314,600,000과 0.0000451은 각각 3,146E5와 451E-7로 표현되는데 3,146과 451은 가수부이고 E5와 E-7은 지수이다. 따라서 314,600,000=3,146×105가 되고, 0.0000451=451×10-7이 된다.

 

 

 

 

 

규칙들

1.가수는한자리,0이아닌정수로쓰여야만한다.

2.가수는소수부수들의고정된개수를가지고쓰여야만한다.(이것을M이라고정의)

3.지수는고정된개수의수들로쓰여야만한다.(이것을E라고정의)

4.가수와지수는각각개별적인부호를가진다

 

 

최대, 최소 지수 ±(10^E -1) = ±(10^2 -1) =±99

최대 기수 값 10.0 - {10^(-M) } = 10.0 -{10^(-3)} = 10.0 - 0.001 = 9.999

 

최대 표현 가능한 값 : 9.999 * 10^99

최소 표현 가능한 값 : -9.999 * 10^99

가장 작은 양의 값 : 1.000 * 10^99

 

반응형
반응형

#include <iostream>
#include <Windows.h>

using namespace std;

struct UObject{
 int aa;
 int bb;
 int cc;
};


enum EInternal {EC_Internal};


void* operator new(size_t t,EInternal* Mem){

 return malloc(t);
}

void* operator new(size_t t){

 return malloc(t);
}

int main(){

 

 int* pi=new int;

 char* pc=new char;

 void* X=new char;

 new( (EInternal*)X ) UObject;     // X 가 *Mem 으로 전달 되고 UObject 의 크기 12가 t 로 전달된다

 

// new( (EInternal*)X ) UObject( UObject 의 생성자 인수목록 ); 


 delete pi;
 delete pc;

 

 return 0;
}

반응형

'프로그래밍(Programming) > c++, 11, 14 , 17, 20' 카테고리의 다른 글

restrict 포인터 {void* restrict pt}  (0) 2012.11.01
부동 소수점( 기수법 )  (0) 2012.11.01
Protected 생성자  (0) 2012.11.01
win32 에서 출력창에 표시  (0) 2012.11.01
난수 발생 srand, rand  (0) 2012.11.01
반응형

http://blog.naver.com/kofpw?Redirect=Log&logNo=80142050098

 


복사http://blog.naver.com/kofpw/80142050098

 

 

MFC의 동적 객체 생성 방법을 공부하던 중, MFC의 메인이 되는 각 클래스, Doc, View, MainFrm은 App 클래스의 InitInstance() 함수를 통해 동적으로 생성되는데, 웃긴것은 각 클래스의 생성자가 protected로 지정되어있는데 버젓이 다른 클래스에서 접근하여 생성한다는 것이다.

 

이것에 대한 원리가 나와있길래 살펴봤다. 어려운 내용은 아니다.

 

우선 다음과 같은 코드가 있다고 하자.

 

#include <iostream>

using namespace std;

 

class Object

{

protected:

      Object() { cout << "생성자" << endl; }

 

public:

      ~Object() { cout << "소멸자" << endl; }

};

 

void main()

{

     Object obj;

}

 

실행해보면 당연히 에러가 난다. 생성자가 protected로 선언되어있어 외부에서 접근할 수 없기 때문이다.

그럼 어떻게 해야할까? 뭐 제일 간단한 방법은 public에 생성자를 선언하는 방법이지만, 그런 뻔한 방법 말고 여기서의 목적은 MFC에서 어떻게 protected 생성자가 버젓이 호출되는지 파헤치기 위함이니...

 

그럼 어떻게? 그럼 Object 객체를 생성시켜주는 public 함수를 하나 만들어 보는 것이다.

 

#include <iostream>

using namespace std;

 

class Object

{

protected:

      Object() { cout << "생성자" << endl; }

 

public:

      ~Object() { cout << "소멸자" << endl; }

     void CreateObject() { Object obj; }

};

 

void main()

{

     CreateObject();

}

 

또 당연히 에러가 난다. 메인함수에서 CreateObject()라고 쓰면 클래스의 멤버가 아닌 전역 함수를 찾기 때문이다. 하지만 그 어디를 봐도 CreateObject라는 이름을 가진 전역함수는 보이지 않는다. 따라서 CreateObject를 찾을 수 없다고 에러가 뜬다.

그렇다고 먼저처럼 Object 클래스를 생성하고 obj.CreateObject() 라고 할수도 없는 일이다. 생성자는 protected이니까..

 

여기서 떠오르는 것이 바로 static이다.

 

#include <iostream>

using namespace std;

 

class Object

{

protected:

      Object() { cout << "생성자" << endl; }

 

public:

      ~Object() { cout << "소멸자" << endl; }

     static void CreateObject() { Object obj; }

};

 

void main()

{

     Object::CreateObject();

}

 

클래스의 멤버 함수를 static으로 선언해버리면 클래스를 만들지 않고도 해당 함수를 호출 할 수 있게 된다. (static 함수는 메모리의 데이터 영역에 저장된다) 위 코드는 정상적으로 실행이 될 것이다. 그러나, static 함수에서 생성된 객체 obj는 지역 변수이므로, 함수가 종료되는 순간 소멸되어버릴 것이다. 함수가 무용지물이 되는 셈이다.

 

그럼 또 어떻게 해야할까? 뭐긴... 동적할당하면 되지~~

 

#include <iostream>

using namespace std;

 

class Object

{

protected:

      Object() { cout << "생성자" << endl; }

 

public:

      ~Object() { cout << "소멸자" << endl; }

     static Object* CreateObject() { return new Object; }

};

 

void main()

{

     Object* pObj = Object::CreateObject();

     delete pObj;

}

 

이렇게 하면 함수를 통해 생성한 객체를 받아와서 사용할 수 있게 된다.

 

결론은, MFC의 동적 객체 생성 방법도 이런 식으로 진행된다는 것이다. 다만, 그것이 표면적으로 드러나있지 않고 숨어있을뿐.....

[출처] Protected Constructor|작성자 쿨랜드


반응형
반응형


첨부파일 utillilty.cpp 

http://www.nicklib.com/bbs/board.php?bo_table=bbs_util&wr_id=12&sca=Visual+Studio

 

 


#ifdef WIN32
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <crtdbg.h>
#endif
void Trace(char* lpszFormat, ...)
{
#ifdef WIN32
 char szBuffer1[512];
 char* lpBuf;
 char* lpsz;
 char szBuffer[512];

 va_list args;
 va_start(args, lpszFormat);
 _vsntprintf(szBuffer1, sizeof(szBuffer1)/sizeof(szBuffer1[0]), lpszFormat, args);
 va_end(args);
 lpsz = szBuffer1;
 lpBuf = szBuffer;
 while (*lpsz != '\0')
 {
  if (lpBuf > szBuffer + sizeof(szBuffer)/sizeof(szBuffer[0]) - 3)
  {
   *lpBuf = '\0';
   if ((1 == _CrtDbgReport(_CRT_WARN, NULL, 0, NULL, "%s", szBuffer))) 
    _CrtDbgBreak(); 
   lpBuf = szBuffer;
  }
  if (*lpsz == '\n')
   *lpBuf++ = '\r';
  *lpBuf++ = *lpsz++;
 }
 *lpBuf = '\0';
 if ((1 == _CrtDbgReport(_CRT_WARN, NULL, 0, NULL, "%s", szBuffer))) 
  _CrtDbgBreak(); 
#else
 return;
#endif


반응형
반응형

http://pcsak3.com/431

 

 

난수 발생 함수

   

 

srand( (unsigned)time( NULL ) ); // 난수 발생초기화

m_iNowBlockShape = rand()%7; // 난수 발생

  

 

 

 

 

C에서 난수 발생

   

다른 언어와 비슷하겠지만 C언에서 여러 개의 난수표를 가지고 있습니다. 이 난수표에는 임의의 수가 나열되어 있으며 C에서 난수표의 값을 가져오는 방식입니다.

이 때 동일한 난수표와 동일한 위치에서 난수를 가져오게 된다면 어떻게 될까요?

항상 같은 값만 나올 것입니다.

그래서 사용할 난수표와 위치를 항상 변경해 줘야 하는데 srand() 함수로 변경할 수 있습니다.

srand 파라미터에 어떤 숫자를 넣어 주면 난수표와 위치가 변경됩니다.

그렇다면 항상 다른 값의 파라미터를 입력해야 하는데, 어떻게 해야 할까요?

또 난수를 발생시켜 넣어 줄까요? ;;

이 때는 시간을 파라미터로 넣어 주면 됩니다. 시간은 항상 변하니까요^^.

 

그래서 C 에서는 아래처럼 사용됩니다.

 

srand( (unsigned)time( NULL ) ); // 난수 발생초기화

m_iNowBlockShape = rand(); // 난수 발생

  

 

 

 

난수 발생하는 방법을 알았는데,

만약 0 ~ 6까지의 난수를 생성하려면 어떻게 해야 할까요?

생성된 난수를 7로 나눈 나머지를 사용하면 되겠죠^^

 

srand( (unsigned)time( NULL ) ); // 난수 발생초기화

m_iNowBlockShape = rand()%7; // 0 ~ 6까지의 난수 발생

  

 

 

 

그렇다면 난수의 최대값을 얼마일까요?

컴파일러 마다 다를 수 있는데 헤더 파일에 선언되어 있습니다.

 

printf("RAND_MAX: %d", RAND_MAX); // 생성할 수 있는 난수의 최대값

  

 

 

 

 

RAND_MAX 는 어디 있으며, srand(), rand() 함수는 어디에 있나요?

아래 헤더 파일을 추가해 줘야 합니다.

 

#include <stdlib.h> // RAND_MAX, srand(), rand() 함수

#include <time.h> // time() 함수

  

 

 

 

 

난수 초기화 함수를 여러 번 실행하면 난수 발생이 더 효과적인가요?

=> 그렇지 않습니다. 초기화를 한 번하던 열번 하던 효과는 같습니다.~~

 

 

 

 

난수 초기화를 하지 않으면 항상 같은 수가 나오나요?

=> 컴파일러마다 약간 다를 가능성도 있지만 이론상으로는 항상 같은 값이 나옵니다.

 

아래는 C에서 난수를 발생한 예입니다.

 

 

#include <stdio.h>

#include <stdlib.h> // RAND_MAX, srand(), rand() 함수

#include <time.h> // time() 함수

 

 

int main(int argccharargv[])

{

 

//    srand( (unsigned)time( NULL ) ); // 난수 발생초기화

 

    for (int i=0;i<10;i++)

    {

        printf("\n%d",rand());

    }

      

    

    return 0;

}

 

 

 

41

18467

6334

26500

19169

15724

11478

29358

26962

24464

반응형
반응형

 

0과 1이나 true/false같은 switch개념의 변수를 사용하려면 C언어에서 최소사양에 해당하는 char형을 생각해 볼 수 있습니다. 물론 int형이나 그 밖에 다른 형을 써도 상관은 없지만 1Bit만 있으면 되므로 굳이 많은 Data형을 할당하여 Memory를 낭비할 필요는 없습니다.

그런데 char형도 그리 효츌적이지는 않습니다. 왜냐하면 char는 1Byte단위(8bit)이므로 0과 1을 표시하는데 1Bit를 할당하고 나면 나머지 7Bit는 결국 낭비되기 때문입니다.

따라서 C에서는 Bit단위의 Data를 다룰때 해당 Bit영역을 최대한 활용하기 위해 Bit Field라는 것을 사용합니다.

Bit Field는 선언하고자 하는 Data형의 크기 만큼 Bit Field를 구조체 형식으로 선언하면 됩니다.

struct bitfield{
    unsigned char a : 1;
    unsigned char b : 1;
    unsigned char c : 1;
    unsigned char d : 1;
    unsigned char e : 1;
    unsigned char f : 1;
    unsigned char g : 1;
    unsigned char h : 1;
};

bitfield라는 이름으로 unsigned char형(8bit)의 a부터 h까지 Bit Field를 선언하였습니다.

unsigned char라고 한 이유는 char형의 전체 8Bit중 최상위 Bit(부호를 정하는 bit)까지 모두 사용하기 위해서 입니다.(만약 unsigned int라고 한다면 16Bit만큼을 의미하게 됩니다.)

unsigned char는 전체 8bit이므로 a부터 h까지 1비트씩 차지하여 결국 합이 1Byte의 Memory를 할당하게 됩니다. 이때 a부터 h까지 1Bit씩 차지한다는 것은 a : 1 등에 의해 정해지게 됩니다.

따라서 1Bit가 아닌 그 이상의 Bit를 할당하고자 할 경우에는 오른쪽의 숫자만 바꾸어 주면 될것입니다.

struct bitfield{
    unsigned char a : 2;
    unsigned char b : 3;
    unsigned char c : 1;
    unsigned char d : 1;
    unsigned char e : 1;
};

a는 2Bit, b는 3Bit.. 나머지는 모두 1Bit를 할당하였습니다. 따라서 a는 2Bit이므로 0~3까지 그리고 b는 3Bit이므로 최소 0~7까지의 Data를 담을 수 있습니다.

이때 만약 Data형의 범위를 벗어나도록 bit field를 지정하면 어떻게 될까요?

struct bitfield{
    unsigned char a : 2;
    unsigned char b : 3;
    unsigned char c : 1;
    unsigned char d : 1;
    unsigned char e : 1;
    unsigned char f : 1;
};

f 1Bit까지 전체 9Bit를 할당하였습니다.

unsigned char는 8bit이므로 f를 수용할 수 없습니다. 하지만 Compiler는 오류를 표시하지 않고 위에서 순서대로 각 Bit를 모두 담은 뒤 그 이상의 Bit가 존재하면 다음 영역을 또 다시 확보하고 해당 Bit를 담게 됩니다.

즉, 처음 a부터 e까지 8Bit(1Byte)에 담고 그 다음 f를 위해 다시 8Bit(char = 1Byte)영역을 확보한 후 처음 Bit부분에 f를 담게 되는 것입니다.(나머지 7bit는 낭비됩니다.)

#include <stdio.h>

main()
{
  struct bitfield{
    unsigned char a : 2;
    unsigned char b : 3;
    unsigned char c : 1;
    unsigned char d : 1;
    unsigned char e : 1;
  };
  
  struct bitfield bit;
  
  bit.a = 2;
  bit.b = 5;
  bit.c = 1;
  bit.d = 0;
  bit.e = 1;
  
  printf("%d\n", bit.a);
  printf("%d\n", bit.b);
  printf("%d\n", bit.c);
  printf("%d\n", bit.d);
  printf("%d\n", bit.e);
}


각 Bit에 값을 설정하고 해당 값을 확인합니다.


bit field라고 해서 특별히 정해진 구문이 있는것은 아닙니다. 단지 bit field를 선언하는데 구조체의 선언방법을 빌려 선언되는것 뿐이며 구문상의 차이도 몇 Bit의 영역을 차지하는가를 나타내는 : x 형식의 구현부분만 다를 뿐입니다.

때문에 실제 bit filed와 구조체사이에는 그리 큰 차이가 없습니다.

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

main()
{
  struct bitfield{
    unsigned char a : 2;
    unsigned char b : 3;
    unsigned char c : 1;
    unsigned char d : 1;
    unsigned char e : 1;
    
    char name[10];
    int age;
  };
  
  struct bitfield bit;
  
  bit.a = 2;
  bit.b = 5;
  bit.c = 1;
  bit.d = 0;
  bit.e = 1;
  
  strcpy(bit.name, "youngsoo");
  bit.age = 30;
  
  printf("%d\n", bit.a);
  printf("%d\n", bit.b);
  printf("%d\n", bit.c);
  printf("%d\n", bit.d);
  printf("%d\n", bit.e);
  
  printf("%s\n", bit.name);
  printf("%d\n", bit.age);
}


구조체와 bit field를 동시에 구현합니다.


bit field를 선언할때 몇 Bit를 할당할지에 대한 내용은 생략하고 Data형만 쓰는 경우가 있습니다. 이는 실제 Bit영역을 사용하지 않으면서 Program구조상에 구분영역을 만들어 주는 역활을 수행하기 위함입니다.

또한 Bit를 처음부터 채우지 않고 일정부분 비워둬야 하는 경우에도 자주 쓰이는 방법입니다.

#include <stdio.h>

main()
{
  struct bitfield{
    unsigned char a : 2;
    unsigned char   : 3;
    unsigned char c : 1;
    unsigned char   : 1;
    unsigned char e : 1;
  };
  
  struct bitfield bit;
  
  bit.a = 2;
  bit.c = 0;
  bit.e = 1;
  
  printf("%d\n", bit.a);
  printf("%d\n", bit.c);
  printf("%d\n", bit.e);
}


bit field의 3번째 부터 5번째(이전의 b영역)과 7번째(이전의 d영역)은 사용하지 않습니다. 또한 a와 c, e를 영역별로 구분하도록 합니다.


참고 :
각 Field의 구분은 Program이 아닌 개발자 입장에서 입니다.

반응형
반응형

strtok(_tcstok)는 문자열을 특정 기준에 따라 나누어 주는 함수이다.

 TCHAR.H routine _UNICODE & _MBCS not defined _MBCS defined _UNICODE defined
 _tcstok strtok _mbstok wcstok
 _tcstok strtok_l _mbstok_l wcstok_l


예를 들어

string[] = _T("My Friends are honest. \n But, JK is very stupid");

 라는 문장이 있다 이 문장에서 \n을 기준으로 두개의 문자열로 나누고 싶다면

다음과 같이 사용하면 된다.

특정 기준을 사용하기 위해 배열에 기준에 대한 항목을 넣어준다.

TCHAR seps[] = _T("\t\n");  //\t와 \n이 나오면 문자열을 나눈다.

아래는 위에서 설명한 내용에 따라 예제로 나타낸 것이다.
(MSDN 참고)
#include <string.h>
#include <stdio.h>
#include <tchar.h>
TCHAR string[] = _T("My Friends are honest. \nSo, I'm happy");
TCHAR seps[] = _T("\t\n");
TCHAR *token;

int _tmain(int argc, TCHAR argv[])
{
//문자열을 기준에 따라 token에 임시 저장한다.
token = _tcstok( string, seps );

while( token != NULL )
{
  //기준에 의해 나눈 문자열을 출력한다.
_tprintf(_T("%s\n"), token );

// 다음 문자열을 구한다.
token = _tcstok( NULL, seps ); // C4996
}
}

실행결과
My Friends are honest.
So.I'm happy

반응형
반응형

위의 표 중 Parameters in registers 에서 32bit 일 경우 _fastcall 은 파라미터 전달시 저장을 레지스터에 저장한다는 것 한개도 아닌 두개로

_thiscall 의 경우엔 한개로,  그런데 표준함수 호출인 _stdcall 은 레지스터를 사용하지 않는다 _cdecl 경우도 마찬가지

 

하지만 64 bit 컴퓨터에서는 레지스터 사용량이 대폭 증가한다,

 

속도면에서 상당히 우월한 레지스터이니 빨라질 수 밖에없다 

반응형

'프로그래밍(Programming) > c++, 11, 14 , 17, 20' 카테고리의 다른 글

비트필드(Bit Field)  (0) 2012.11.01
_tcstok 사용방법(문자열 분리)  (0) 2012.11.01
입,출력 스트림 - IO stream  (0) 2012.11.01
strcmp.c  (0) 2012.11.01
c++ 무한대 표기  (0) 2012.11.01
반응형


    1. 표준 입력와 출력

     

    모니터와 키보드를 콘솔(console)이라고 하는데, 이것들은 컴퓨터의 기본적인 입출력을 담당하는 표준 입출력(standard input and output) 장치입니다. 모니터로 데이터를 출력하는 방법과 키보드로 데이터를 입력받는 방법에 관하여 살펴봅시다.

     

    C++에서는 파일(장치도 파일로 간주)을 다루기 위해 스트림(stream)이라는 개념을 도입하였습니다. 스트림은 데이터가 이동하는 파이프와 같은 것으로 목적지까지 데이터를 순차적으로 전송하는 역할합니다. 또한 스트림은 데이터를 보내는 속도와 받아서 처리하는 속도의 차를 극복하기 위한 버퍼의 역할도 합니다. 스트림은 데이터의 전송 방향에 따라 출력 스트림과 입력 스트림으로 나누지고, 제작하고 있는 프로그램을 기준으로 밖으로 데이터를 보내면 출력, 외부에서 데이터를 가져오면 입력 스트림입니다. 이러한 스트림을 사용하기 위해서는 스트림을 만들고, << 와 >> 연산자를 사용하여 데이터를 전송하고, 사용 후에는 제거해 줘야 합니다. 표준 입출력을 위한 스트림은 만들고 제거하는 과정없이 항상 사용할 수 있는데, 다음과 같이 준비되어 있습니다.

     

    종류

     스트림

     연산자

    출력

     cout

     <<

    입력

     cin

     >>

     

    스트림을 사용하기 위해서는 먼저 해당 헤더 파일을 소스에 포함시켜야 하고, 이름 공간의 사용을 명시할 필요가 있습니다.

     
    #include <iostream>  // 헤더 파일
    using namespace std; // std 이름 공간 사용
     

    입력 스트림을 사용할 때는 입력된 데이터를 보관할 변수(확보된 메모리 공간)를 미리 준비해야 한다는 것입니다.
    예를 들어 친구의 이름과 나이를 입력 받아, 출력해 봅시다. 

     
    #include <iostream>
     
    using namespace std;
     
    int main()
    {
        char name[9]; // 이름을 입력 받기 위한 변수
        int  age;     // 나이를 입력 받기 위한 변수
     
        cout << "친구 이름 : ";
        cin >> name;
        cout << "친구 나이 : ";
        cin >> age;
     
        cout << "친구의 이름은 " << name << "이고, " 

             << "나이는 " << age << "입니다." << endl;
     
        return 0;
    }

     

    친구의 이름과 나이를 입력받으려면, 먼저 이를 위한 메모리 공간을 확보해야 합니다. 물론 가장 쉬운 방법이 변수를 만드는 것입니다. 먼저 이름을 저장할 변수를 만들어 봅시다. 이름은 문자열이므로 문자 배열이 필요합니다. 한글 4 자를 저장하려면 8 바이트가 필요하고 NULL문자를 위한 공간도 필요하므로 char[9]인 자료형이 필요합니다. 나이는 정수이므로 int 형 변수를 만들면 되겠습니다.

    반응형
    반응형

    /***
    *strcmp.c - routine to compare two strings (for equal, less, or greater)
    *
    *       Copyright (c) Microsoft Corporation. All rights reserved.
    *
    *Purpose:
    *       Compares two string, determining their lexical order.
    *
    *******************************************************************************/

    #include <cruntime.h>
    #include <string.h>

    #ifdef _MSC_VER
    #pragma function(strcmp)
    #endif  /* _MSC_VER */

    /***
    *strcmp - compare two strings, returning less than, equal to, or greater than
    *
    *Purpose:
    *       STRCMP compares two strings and returns an integer
    *       to indicate whether the first is less than the second, the two are
    *       equal, or whether the first is greater than the second.
    *
    *       Comparison is done byte by byte on an UNSIGNED basis, which is to
    *       say that Null (0) is less than any other character (1-255).
    *
    *Entry:
    *       const char * src - string for left-hand side of comparison
    *       const char * dst - string for right-hand side of comparison
    *
    *Exit:
    *       returns -1 if src <  dst
    *       returns  0 if src == dst
    *       returns +1 if src >  dst
    *
    *Exceptions:
    *
    *******************************************************************************/

    int __cdecl strcmp (
            const char * src,
            const char * dst
            )
    {
            int ret = 0 ;

            while( ! (ret = *(unsigned char *)src - *(unsigned char *)dst) && *dst)
                    ++src, ++dst;

            if ( ret < 0 )
                    ret = -1 ;
            else if ( ret > 0 )
                    ret = 1 ;

            return( ret );
    }


    반응형
    반응형

    http://evax.springnote.com/pages/4607951.xhtml

     

    현재 개발환경에서는 최신 공용 라이브러리 링크에 문제점이 발생하게 되는데 Visual Studio sp1이 설치된 개발머신에서 Visual Studio2008로 컴파일을 할 경우, 메니페스트는 기존 Visual Studio2008용 라이브러리들의 정보가 기술되게 됩니다. 따라서, 개발은 sp1에 포함된 라이브러리에서 되었지만 실행은 구버전의 라이브러리로 되는 현상이 발생하게 됩니다. 이는 tr1과 같은 라이브러리의 새로운 기능이나 MFC의 새로운 기능을 사용했을 때 문제가 될 소지가 더 많게 됩니다.

     

    따라서 최신 버전의 라이브러리 버전을 기술해 주는 작업이 필요한데, 코드상으로 이 부분을 선언 해 줄 수 있습니다.

    #define _BIND_TO_CURRENT_CRT_VERSION 1
    #define _BIND_TO_CURRENT_ATL_VERSION 1
    #define _BIND_TO_CURRENT_MFC_VERSION 1#define _BIND_TO_CURRENT_OPENMP_VERSION 1

     

    이름을 보면 알 수 있듯, 이 상수는 현재 사용하고 있는 라이브러리의 버전을 메니페스트에 바인딩 해주는 작업을 합니다. 이 네가지의 상수를 하나로 묶어서 다음과 같이 하나의 상수만 선언해 주어도 4개를 선언한것과 동일한 효과를 거둘 수 있습니다.

     

    #define _BIND_TO_CURRENT_VCLIBS_VERSION 1

     

    이 상수는 프리프로세스에 선언해 주는 것이 좋습니다. stdafx.h에 선언하게 되면 중복선언 되는 케이스가 생길 수도 있고 프로젝트에 따라 프리컴파일드 헤더를 사용하지 않을 경우 선언하는 곳이 모호해질 수 있는 문제가 있습니다.(2009-01-23 revolution 팀장님 감사합니다. ^^) 컴파일이 처음 되는 stdafx.h의 가장 상단 같은 곳에 선언해 주는 것이 좋습니다. Visual Studio 2008 sp1을 설치한 다음 현재 라이브러리 바인드 상수를 선언한 후 애플리케이션을 컴파일 하면 다음과 같이 sp1의 라이브러리 버전으로 바인드 되어 있음을 확인할 수 있습니다. 공용 CRT 라이브러리와 공용 MFC 라이브러리 버전이 9.0.21022.8에서 9.0.30729.1로 변경된 부분에 주목해 주세요.

     

    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">

    <security>

    <requestedPrivileges>

    <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>

    </requestedPrivileges>

    </security>

    </trustInfo>

    <dependency>

    <dependentAssembly>

    <assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>

    </dependentAssembly>

    </dependency>

    <dependency>

    <dependentAssembly>

    <assemblyIdentity type="win32" name="Microsoft.VC90.MFC" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>

    </dependentAssembly>

    </dependency>

    <dependency>

    <dependentAssembly>

    <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*"></assemblyIdentity>

    </dependentAssembly>

    </dependency>

    </assembly>

     


     

     


     


     


    Visual C++ 동적으로 MFC 를 사용할 경우 필요한 DLL 파일들  Information 

    2008/08/14 16:26

    복사http://blog.naver.com/ysoftman/20053674809

    MFC 를 동적으로 사용할 경우 필요한 dll 로, exe 와 같은 폴더나 시스템 폴더에 존재해야 한다.

     

    // DEBUG 경우

    // Visual C++ 6.0 경우

    mfc42d.dll  mfco42d.dll msvcirtd.dll msvcrtd.dll

    // Visual C++ 9.0 (2008) 경우

    mfc90d.dll mfcm90d.dll msvcpm90d.dll msvcp90d.dll msvcr90d.dll

     

    // RELEASE 경우

    // Visual C++ 6.0 경우

    mfc42.dll  mfco42.dll msvcirt.dll msvcrt.dll

    // Visual C++ 9.0 (2008) 경우

    mfc90.dll mfcm90.dll msvcpm90.dll msvcp90.dll msvcr90.dll



    1 개요

    VisualCpp 8.0부터 뭔가 배포가 귀찮아졌다.

    A required .DLL file, "MSVCR80.dll" was not found.
    app.exe has failed to start because the application configuration is incorrect.
    응용프로그램 구성이 올바르지 않기 때문에 이 응용 프로그램을 시작하지 못했습니다. 이 문제를 해결하려면 응용 프로그램을 다시 설치하십시오.


    2 Side-by-side Assembly

    DLL 충돌 문제 때문에 마이크로소프트가 Side-by-side Assembly인지 뭔지를 만들었다. 같은 컴퓨터 안에서 여러 버전의 DLL을 동시에 실행할 수 있도록 해주는 기능인 모양이다. 즉 각각의 애플리케이션에게 독립적인 DLL 환경을 보장해준다는 말이다. 이 파일들은 \Windows\WinSxS 디렉토리에 존재한다. Side-by-side Assembly가 무엇인지는 .NET 어셈블리 쪽을 참고하기 바란다.

    어쨌든 이것 때문에 기존 방식(배포할 애플리케이션과 같은 디렉토리에 VisualCpp 런타임 DLL을 같이 넣어두기)이 통하지 않게 되었다. 애플리케이션을 정상적으로 돌아가게 하기 위해서는 이 Side-by-side DLL들을 배포하고, 뭔가 레지스트리를 잔뜩 건드려야한다.


    3 해결 방안

    3.1 윈도우즈 인스톨러를 이용해 셋업 프로젝트를 만든다

    아. 짱나.

    3.2 VC 8.0 안에 있는 셋업 파일을 같이 배포한다

    위 파일을 먼저 설치한 후, 원래 애플리케이션을 돌리면 된다. 그런데 이상하게 인스톨 화면도 딱히 없고, 프로세스 창에 msiexec.exe가 반응이 없는 채로 상당 시간 떠 있는 것을 볼 수 있다. 뭐 어쨌든 설치는 되더라. 비교적 간단한 방법이기는 하지만 이넘을 실행시키기 위해 필요한 것들이 많다는 게 문제다.

    from [WWW]http://www.codeproject.com/useritems/vcredists_x86.asp

    OSInstallableRequired Service PackOther Software
    Windows 3.x/NT/95NoN/AN/A
    Windows 98/MEYesInternet Explorer 5.0 required (included in Win98SE/ME)Windows Installer 2.0 required
    Windows 2000YesService Pack 3 required (includes Windows Installer 2.0)Windows Installer 3.0 required
    Windows XPYesService Pack 2 RecommendedWindows Installer 3.0 required (included in Service Pack 2)
    Windows Server 2003YesService Pack 1 RecommendedWindows Installer 3.0 required (Windows Installer 3.1 included in Service pack 1)
    Windows VistaYesNoneNone


    3.3 Private Assembly를 설치한다

    실행 파일 자체와 실행 파일에서 액세스하는 DLL들에 대한 manifest 파일들을 private assembly로서 같이 배포하면, 에러를 피할 수 있다. 실행 파일의 manifest 파일은 오브젝트 파일 생기는 디렉토리 잘 뒤져보면 있을 것이다. 대충 아래와 같은 내용이다.

    XXX.exe.manifest
    <?xml version='1.0' encoding='UTF-8' standalone='yes'?> 
    <assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'> 
      <dependency> 
        <dependentAssembly> 
          <assemblyIdentity type='win32' name='Microsoft.VC80.CRT' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' /> 
        </dependentAssembly> 
      </dependency> 
      <dependency> 
        <dependentAssembly> 
          <assemblyIdentity type='win32' name='Microsoft.VC80.MFC' version='8.0.50608.0' processorArchitecture='x86' publicKeyToken='1fc8b3b9a1e18e3b' /> 
        </dependentAssembly> 
      </dependency> 
    </assembly> 
    

    그 다음 각각의 어셈블리, 즉 DLL에 대한 manifest 파일도 같이 배포해야 한다. (이 파일들은 기본적으로 ...\Microsoft Visual Studio 8\VC\redist 디렉토리 아래에 각 플랫폼 별로 존재한다.)

    Microsoft.VC80.CRT.manifest
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>  
    <!-- Copyright &copy; 1981-2001 Microsoft Corporation-->  
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">  
    <noInheritable/>  
    <assemblyIdentity  
        type="win32"  
        name="Microsoft.VC80.CRT"  
        version="8.0.50608.0"  
        processorArchitecture="x86"  
        publicKeyToken="1fc8b3b9a1e18e3b" />  
    <file name="msvcr80.dll"/>  
    <file name="msvcp80.dll"/>  
    <file name="msvcm80.dll"/>  
    </assembly>  
    
    Microsoft.VC80.MFC.manifest
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>  
    <!-- Copyright &copy; 1981-2001 Microsoft Corporation-->  
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">  
    <noInheritable/>  
    <assemblyIdentity  
        type="win32"  
        name="Microsoft.VC80.MFC"  
        version="8.0.50608.0"  
        processorArchitecture="x86"  
        publicKeyToken="1fc8b3b9a1e18e3b" />  
    <file name="mfc80.dll"/>  
    <file name="mfc80u.dll"/>  
    <file name="mfcm80.dll"/>  
    <file name="mfcm80u.dll"/>  
    </assembly>  
    
    이 manifest 파일과 DLL 파일들을 애플리케이션 실행 파일이 존재하는 디렉토리에 같이 넣어두면, 별도의 셋업 없이도 실행이 된다. 문제는 이렇게 했을 때, 저 DLL들의 새 버전이 나와도 XXX.exe는 이 혜택을 받을 수 없다는 점이다.

    3.4 .NET framework 2.0을 설치한다

    다운 열라 받아야함...

    3.5 CRT 소스를 이용해 커스텀 CRT를 빌드한다

    빌드한 파일을 마이크로소프트에서 배포하는 이름과 똑같은 이름으로 배포하면 법에 저촉되는 모양이다. 사실 빌드하기도 귀찮다.

    3.6 정적 링크를 이용한다

    C/C++ > Code Generation 항목에서 DLL 버전 말고 정적 CRT를 사용하도록 한다. 제일 간단한 방법이기는 하지만, 온라인 게임 클라이언트 같은 경우에는 매번 업데이트 때마다 커다란 크기의 실행 파일을 다운로드받아야하는 단점이...
    출처
      
    http://serious-code.net/moin.cgi/RedistributingVisualCppRunTimeLibrary

    반응형
    반응형

    요즘은 소프트웨어를 개발시 주프로그램과 독립적이면서 작은 기능을 수행하는 여러 작은프로그램을 분리해서 개발하게 됩니다. 작은 프로그램은 보통 dll(dynamic link library) 형태로 구현하며, 주프로그램과 소스상에서 분리되며 실행시 주프로그램과 동적으로 연결되게 됩니다.

    소스상에서 이둘의 통신은  함수호출을 통해서 이루어집니다.

    dll은 외부에서 자신의 기능을 사용할수 있도록 호출가능한 함수들을 제공합니다. 반대로, 주프로그램은 외부에서 호출할수 있는 함수들을 제공하지 않습니다.

     


     

    만일, 주프로그램이 DLL에게 어떠한 작업요청을 한후, 이 작업이 완료되었을때 DLL로 부터 응답을 받고 싶은 경우가 생겼다고 하면 어떻해야 할까요?

    이 경우, 주프로그램은 외부에서 호출가능한 함수를 제공하지 않으므로, 호출받고 싶은 함수의 포인터를 DLL에 넘겨준후, DLL에서 호출하도록 구현해야 합니다.

     

    다음은 dll에 주프로그램내 호출될 함수포인터의 등록과 dll에서 함수포인터 호출에 대한 간단한 소스입니다.

     

    dll 내부

     

    //registerfuncptr.h

    long DECLSPEC__ __cdecl  SetCallbackProcAddr(long fuctionPtr, int type);

    void (*successfptr)(); //함수포인터 선언

    void (*failfptr)();

     

    //registerfuncptr.c

    #include "registerfuncptr.h"

    // 함수포인터 등록
    long DECLSPEC__ __cdecl  SetCallbackProcAddr(long fuctionPtr, int type)
    {
      long Result    = 0;
      if(type == 1)
      {
       successfptr = fuctionPtr;
       if(successfptr != NULL)
          Result = 1;
      }
      else if(type == 2)
      {
       failfptr= fuctionPtr;
       if (failfptr!= NULL) 
        Result = 1;
      }

      return Resul;

    }

     

    //함수포인터 호출

    void work() {

    ...

       // 작업성공

      if(bState)

         (*successfptr)();

       //작업실패

       else

          (*failfptr)();

    }

     

     

    주프로그램

     

    //response.h

    void register();

    void success();

    void fail();

     

    //response.c

    void register()

    {

       SetCallbackProcAddr((long)(&success)   ,1);

       SetCallbackProcAddr((long)(&fail)   ,2);

    }

     

    void success() {

    ...

    }

     

    void fail() {

    ...

    }


    반응형
    반응형

    printf를 MSDN에서 찾아보면

     

    If the argument corresponding to a floating-point specifier is infinite, indefinite, or NaN, printf gives the following output.


    위의 내용을 해석하면...

    ( 만약 플로팅지정자로 일치하는 아규먼트가 무한대, 정의되지 않는 숫자(orNaN), 일경우

    printf 는 다음 출력구문을 보여준다  )



    ms 페이지에서 보면 해당 타입의 std::numeric_limits<T>::has_infinity


    의 has_infinity 가 true 일 경우 이러한 무한대 값을 표시해준다라고 나와 있다




    출처 : 지식인



    ValueOutput
    + infinity1.#INFrandom-digits
    – infinity–1.#INFrandom-digits
    Indefinite (same as quiet NaN)digit.#INDrandom-digits
    NAN

    digit.#NANrandom-digits

     

    이렇게 되어 있네요

     

    실수를 프린트에서 찍을때

    +무한은 1.#INF

    -무한은 -1.#INF

    정의되어 있지않은 숫자는  #IND

    NAN(Not a Number)는 #NAN이라고 표시된답니다.

     

    무한(Infinity)은 말 그대로 컴퓨터의 double로 표시할 수 있는 범위를 넘어간 큰수흘 의미 합니다.

    미정의 숫자(Indefinite)는 루트(-1)처럼 정의할 수 없는 숫자를 의미합니다.

    이런 에러가 발생했는 지를 찾기 위해서는 float.h에 있는 _isnan이나 _finite 함수를 이용할 수 있습니다. 다음 예를 참고하십시요

     

    #include < float.h>
    #include < math.h>
    #include < stdio.h>


    void testnumber(double val)
    {
        if(_isnan(val))
        {
            printf("%e은 숫자가 아닙니다.\n",val);
        }else if(!_finite(val))
        {
            printf("%e은 유한 범위의 숫자가 아닙니다.\n",val);
        }
        else
            printf("%e은 정상적인 숫자입니다.\n",val);


    }

    void main()
    {
        double val=1;

        // indefinite   
        testnumber(sqrt(-1));
       

        // infinity
        for(int i=0;i<10;i++)
        {
            val=val*1000000000000000000000000000000000.0;
            testnumber(val);
        }

    }

     

    반응형
    반응형

    대입연산자 오버로딩은 클래스 멤버로만 정의 가능하고 전역으로는 할 수 없다


    정적으로도 만들 수 없다.




    http://winapi.co.kr

    28-3-다.대입 연산자

    대입 연산자는 자신과 같은 타입의 다른 객체를 대입받을 때 사용하는 연산자이다. 객체 자체와 직접적인 연관이 있기 때문에 클래스의 멤버 함수로만 정의할 수 있으며 전역 함수로는 정의할 수 없다. 정적 함수로도 만들 수 없고 반드시 일반 멤버 함수로 만들어야 한다. 다음 예제는 앞장에서 만들었던 Person2예제에 디폴트 생성자를 추가하고 main 함수의 테스트 코드를 약간 수정한 것이다. Person 클래스는 생성자에서 동적으로 버퍼를 할당한다는 점에서 Time이나 Complex 클래스와는 다르며 이 버퍼를 주의깊게 다루어야 할 필요가 있다.

     

      : Person3

    #include <Turboc.h>

     

    class Person

    {

    private:

         char *Name;

         int Age;

     

    public:

         Person() {

              Name=new char[1];

              Name[0]=NULL;

              Age=0;

         }

         Person(const char *aName, int aAge) {

              Name=new char[strlen(aName)+1];

              strcpy(Name,aName);

              Age=aAge;

         }

         Person(const Person &Other) {

              Name=new char[strlen(Other.Name)+1];

              strcpy(Name,Other.Name);

              Age=Other.Age;

         }

         ~Person() {

              delete [] Name;

         }

         void OutPerson() {

              printf("이름 : %s 나이 : %d\n",Name,Age);

         }

    };

     

    void main()

    {

         Person Boy("강감찬",22);

         Person Young("을지문덕",25);

         Young=Boy;

         Young.OutPerson();

    }

     

    Person2 예제에서는 Young 객체를 선언할 때 Person Young=Boy; 형식으로 선언하면서 동시에 초기화를 했었다. 이때는 복사 생성자가 호출되는데 Person2예제에 복사 생성자가 작성되어 있으므로 이 코드는 이상없이 잘 동작한다. 그러나 일단 선언한 후 대입을 받게 되면 문제가 달라진다. 이 예제를 실행해 보면 프로그램이 종료될 때 다운되는 것을 확인할 수 있다.

    선언과 동시에 다른 객체로 초기화하면 이때 복사 생성자가 호출되고 복사 생성자는 새로 생성되는 객체를 위해 별도의 버퍼를 준비하므로 두 객체가 버퍼를 따로 가져 아무런 문제가 없다. 그러나 실행중에 이미 사용중인 객체를 다른 객체로 대입할 때는 초기화 단계가 아니므로 복사 생성자는 호출되지 않는다. 다음 두 경우를 잘 구분하자.

    대입은 ① 이미 생성된 객체에 적용된다. ② 실행중에 언제든지 여러 번 대입될 수 있다는 점에서 초기화와는 다르다. 실행중에 객체끼리 대입 연산을 하면 어떤 일이 벌어지는지 보자.

    깊은 복사를 하는 대입

    대입 연산자를 별도로 정의하지 않을 경우 컴파일러는 디폴트 대입 연산자를 만드는데 이 연산자는 디폴트 복사 생성자와 마찬가지로 단순한 멤버별 대입만 한다. 우변 객체의 모든 멤버 내용을 좌변 객체의 대응되는 멤버로 그대로 대입함으로써 얕은 복사만 하는 셈이다. 결국 Young의 Name 멤버는 Boy의 Name 멤버가 가리키는 버퍼의 주소를 그대로 가지게 될 것이다. 이때의 메모리 상황을 그림으로 그려 보자.

    두 객체 모두 "강감찬"을 가리키고 있으며 main 함수가 종료될 때 각각의 파괴자가 호출되는데 먼저 파괴되는 객체가 Name 버퍼를 정리할 것이고 나중에 파괴되는 객체가 이 버퍼를 이중으로 정리하려고 하므로 무효해진 메모리를 해제하는 오류를 범하는 것이다. 결국 이 문제는 복사 생성자를 정의하지 않았을 때의 문제와 동일하며 생성과 동시에 초기화할 때처럼 대입을 받을 때도 깊은 복사를 하도록 해야 한다.

    뿐만 아니라 생성할 때와는 달리 대입 연산은 실행중에 언제든지 여러 번 일어날 수 있기 때문에 객체가 사용중이던 메모리를 해제하지 않으면 다시는 이 메모리에 접근할 수 없는 문제도 있다. 위 그림에서 Young이 Boy를 대입받은 후 "을지문덕"은 더 이상 읽지도 쓰지도 못하며 해제할 방법조차 없다. 동적으로 할당한 메모리는 포인터가 진입점인데 이 진입점을 잃어버린 것이다. 이런 문제들을 해결하려면 = 연산자를 오버로딩하여 대입할 때도 깊은 복사를 하도록 해야 한다. Person 클래스에 다음 멤버 연산자 함수를 추가해 보자.

     

    class Person

    {

         ....

        Person &operator =(const Person &Other) {

            if (this != &Other) {

               delete [] Name;

               Name=new char[strlen(Other.Name)+1];

               strcpy(Name,Other.Name);

               Age=Other.Age;

            }

            return *this;

        }

    };

     

    복사 생성자의 코드와 유사한 코드가 반복되는데 대입되는 Other의 Name 길이+1만큼 버퍼를 새로 할당한 후 내용을 복사했다. Age는 단순한 정수형 변수이므로 그냥 대입하기만 하면 된다. 복사 생성자와 마찬가지 방법으로 깊은 복사를 하되 대입 동작은 실행중에 여러 번 그것도 임의의 순간에 발생할 수 있기 때문에 좀 더 신경써야 할 것들이 많다.

    우선 Name 멤버를 할당하기 전에 이전에 사용하던 메모리를 먼저 해제해야 한다. 복사 생성의 경우 Name은 새로 만들어지는 중이므로 할당되어 있지 않지만 대입은 사용중인 객체에 대해 일어나는 연산이므로 Name이 이미 할당되어 있을 것이다. 다른 객체를 대입받는다는 것은 이전의 내용을 버린다는 뜻이므로 이미 할당된 메모리를 해제할 필요가 있는데 이 처리를 하지 않으면 대입할 때마다 이전에 사용하던 메모리가 누수될 것이다. 그래서 new 연산자로 Name을 할당하는 코드 앞에 delete [] Name이 필요하다. 이때 Name이 이미 할당되어 있는지는 점검할 필요가 없는데 디폴트 생성자가 1바이트를 할당하고 있으므로 Name은 항상 동적으로 할당되어 있기 때문이다.

    그리고 대입 요청을 받았을 때 대입 대상이 자기 자신이 아닌지도 꼭 점검해야 하는데 A=A 같은 대입문도 일단은 가능해야 하기 때문이다. 이 문장은 자기가 자신의 값을 대입받는 사실상의 NULL문장이지만 고의든 실수든 아니면 코드의 일관성을 위해서건 틀린 문법은 아니므로 지원하는 것이 옳다. 자기 자신이 대입될 때는 아무 것도 하지 않고 자신을 리턴하기만 하면 된다. 만약 이 조건문을 빼 버리면 delete [] Name에 의해 자신의 버퍼를 먼저 정리해 버리고 정리된 버퍼의 내용을 다시 복사하려고 들기 때문에 객체의 내용이 제대로 유지되지 않을 것이다.

    대입 후 리턴되는 값

    대입 연산자의 리턴 타입이 Person &인 이유는 A=B=C식의 연쇄적 대입이 가능해야 하기 때문이다. 대입만이 목적이라면 void형으로 선언해도 상관없겠지만 기본 타입에서 가능한 모든 연산이 객체에서도 가능해야 하므로 가급적 똑같이 동작하도록 만들어야 한다. 대입 연산자가 대입된 결과값을 리턴하기 때문에 연쇄적인 대입이 가능하다.

    이때 리턴되는 객체가 상수일 필요는 없는데 대입 후 리턴되는 객체를 바로 사용할 수도 있고 변경할 수도 있다. (Young=Boy).OutPerson(); 식으로 대입받은 좌변 객체에 대해 멤버 함수를 호출할 수 있다. 설사 이 멤버 함수가 객체의 상태를 변경하는 비상수 함수라도 말이다. 기본 타입도 대입 연산자에 의해 리턴되는 것은 좌변값인데 다음 테스트 코드를 통해 확인해 보자.

     

         int i=1,j=2;

         (i=j)=3;

         printf("%d,%d\n",i,j);

     

    i=j 대입문에 의해 i에 2가 대입되고 i 자체가 리턴된다. 이때 리턴되는 레퍼런스는 좌변값이므로  바로 3을 대입할 수 있다. 출력되는 결과는 3,2가 된다. 실제로 이런 식은 잘 쓰이지도 않고 실용성도 없지만 어쨌든 클래스는 기본 타입과 같아야 하므로 기본 타입들이 하는 짓은 다 할 수 있어야 한다.

    올바른 디폴트 생성자

    Person3 예제는 디폴트 생성자를 정의하고 있으므로 Person Young; 선언문으로 일단 객체를 먼저 만들어 놓고 다른 객체의 값을 대입받아도 상관없다. 디폴트 생성자는 받아들이는 인수가 없으므로 멤버들을 NULL, 0, FALSE로 초기화하여 쓰레기를 치우는 것이 통상적인 임무이지만 동적 할당을 하는 클래스의 경우 포인터를 NULL로 초기화해서는 안된다. 왜 그런지 다음 테스트 코드를 실행해 보자.

     

         Person() { Name=NULL;Age=0; }

     

         Person Boy;

         Person Young=Boy;

     

    디폴트 생성자가 쓰레기를 치우고 있으므로 인수없이 객체를 생성할 수 있다. 그러나 이렇게 만들어진 객체를 사용할 때 여기저기서 문제가 생긴다. 위 테스트 코드는 복사 생성자를 호출하는데 복사 생성자의 본체에서 strlen 함수로 Other.Name의 길이를 구하고 있다. 0번지는 허가되지 않은 영역이므로 이 번지를 읽기만 해도 당장 다운되어 버린다. 복사 생성자가 쓰레기만 치운 객체를 전달받아도 죽지 않으려면 예외 처리 코드가 더 작성되어야 한다.

     

         Person(const Person &Other) {

              if (Other.Name == NULL) {

                  Name=NULL;

              } else {

                  Name=new char[strlen(Other.Name)+1];

                  strcpy(Name,Other.Name);

              }

              Age=Other.Age;

         }

     

    초기식의 객체가 NULL 포인터를 가리키면 새로 선언되는 객체도 같이 NULL포인터를 가지도록 해야 한다. 복사 생성자뿐만 아니라 대입 연산자, Name을 참조하는 모든 멤버 함수에서 Name이 NULL인 경우를 일일이 예외 처리해야 하는 것이다. 이렇게 하는 것이 귀찮고 비효율적이기 때문에 디폴트 생성자가 포인터를 초기화할 때는 비록 1바이트라도 할당하여 Name이 NULL이 되지 않도록 하는 것이 좋다. 비록 1바이트에 빈 문자열밖에 들어 있지 않지만 이 메모리도 동적으로 할당한 것이므로 읽을 수 있다.

    Person3의 디폴트 생성자가 할당하는 1바이트는 자리만 지키는 플레이스 홀더(PlaceHolder) 역할을 한다. 아무 짝에도 쓸모없는 것 같지만 Name이 반드시 동적 할당된 메모리임을 보장하여 이 버퍼를 참조하는 모든 코드를 정규화시키는 효과가 있다. 모든 멤버 함수는 Name의 길이가 얼마이든지 무조건 할당되어 있다는 가정하에 Name을 안심하고 액세스할 수 있다.

    동적 할당 클래스의 조건

    이 예제에서 보다시피 초기화와 대입은 여러 모로 다르다는 것을 알 수 있다. 초기화는 객체를 위한 메모리를 할당할 때 이 공간을 어떻게 채울 것인가를 지정하며 일회적인데 비해 대입은 실행중에 같은 타입인 다른 객체의 사본을 작성하며 회수에 제한이 없다. 대입이 초기화보다는 훨씬 더 복잡하고 비용도 많이 든다. 그래서 컴파일러는 복사 생성자와 대입 연산자를 구분해서 호출하며 따라서 우리는 둘 다 만들어야 한다. class A=B; 선언문을 디폴트 생성자로 A를 먼저 만든 후 B를 대입하는 것으로 처리할 경우 속도가 훨씬 더 늦어질 것이다. 실제로 구형 컴파일러는 이런 식으로 초기화를 구현했었다.

    Time이나 Complex 클래스는 복사 생성자가 없어도 선언할 때 다른 객체로 초기화할 수 있으며 대입 연산자를 굳이 정의하지 않아도 객체끼리 안심하고 대입할 수 있다. 왜냐하면 값만을 가지는 클래스는 컴파일러가 만들어 주는 디폴트 복사 생성자, 디폴트 대입 연산자만으로도 충분히 잘 동작하기 때문이다. 이에 비해 Person 클래스는 동적으로 할당하는 메모리가 있기 때문에 여러 모로 관리해야 할 것들이 많은데 최소한 다음과 같은 함수들이 있어야 한다.

     

    함수

    설명

    생성자

    생성될  메모리를 할당한다.

    파괴자

    사용하던 메모리를 반납한다.

    복사 생성자

    초기화될  별도의 메모리를 할당한다.

    대입 연산자

    사용하던 메모리를 해제하고 대입받는 객체에 맞게 다시 할당한다.

     

    이 중 하나라도 빠지거나 생략되면 Person 클래스는 제대로 동작하지 않는다. 생성자는 초기화라는 중요한 임무를 가지므로 꼭 동적 할당을 하지 않더라도 대부분의 클래스에 필수적이다. 나머지 셋은 생성자에서 동적 할당이나 그와 유사한 효과의 동작을 할 때 꼭 필요한데 셋 중 하나가 필요하다면 나머지 둘도 마찬가지로 필요하다. 그래서 이 셋은 같이 뭉쳐서 다니는 특징이 있으며 흔히 삼총사라고 부른다.

    Person3 예제의 Person 클래스는 비로소 완벽해졌으며 선언과 동시에 초기화, 실행중 대입 등이 가능해져 기본 타입과 동등한 자격을 가지게 되었다. 그러나 상속을 하지 않을 경우에만 완벽하며 상속할 경우 파괴자가 가상 함수여야 한다는 조건이 하나 더 추가된다. 이 예에서 동적으로 할당되는 메모리란 클래스 동작에 꼭 필요한 어떤 자원의 비유에 해당한다. 예를 들어 하드웨어 장치를 열어야 하거나 네트워크 접속, DB 연결, 권한 획득 등이 필요한 클래스는 모두 비슷한 법칙이 적용된다. 아무튼 멤버를 그대로 복사해서는 똑같은 객체를 만들 수 없는 모든 클래스에는 이런 함수들이 필요하다.

    복합 대입 연산자

    이번에는 대입 연산자와 유사한 복합 대입 연산자를 오버로딩해 보자. 복합 대입 연산자는 대입과 비슷한 동작을 하기는 하지만 아예 다른 연산자이므로 필요할 경우 따로 정의해야 한다. 예를 들어 Time 클래스에 operator + 연산자를 오버로딩했다고 해서 operator += 까지 같이 정의되는 것은 아니다. 다음은 += 복합 대입 연산자의 오버로딩 예이다.

     

      : OpPlusEqual

    #include <Turboc.h>

     

    class Time

    {

    private:

         int hour,min,sec;

     

    public:

         Time() { }

         Time(int h, int m, int s) { hour=h; min=m; sec=s; }

         void OutTime() {

              printf("%d:%d:%d\n",hour,min,sec);

         }

         Time &operator +=(int s) {

              sec += s;

              min += sec/60;

              sec %= 60;

              hour += min/60;

              min %= 60;

              return *this;

         }

    };

     

    void main()

    {

         Time A(1,1,1);

     

         A+=62;

         A.OutTime();

    }

     

    + 연산자와 다른 점은 호출한 객체를 직접 변경시키기 때문에 const가 아니라는 점, 그리고 자기 자신이 피연산자이므로 임시 객체를 필요로 하지 않는다는 점 정도이다. A+=62 연산문에 의해 A가 가진 시간에 62초를 더한 값이 A에 다시 대입된다. 사용자는 + 연산이 가능하면 +=연산도 가능하다고 기대하므로 가급적이면 두 연산자를 같이 제공하는 것이 좋다. 이 경우 +=을 먼저 정의해 놓고 + 연산자는 이 함수를 호출하는 것이 효율적이다.

     

    Time operator +(int s) {

         Time R=*this;

         R+=s;

         return R;

    }

     

    +=에 정수를 더하는 연산이 먼저 정의되어 있으므로 +는 임시 객체에 += 연산한 결과를 값으로 리턴하기만 하면 된다. 뿐만 아니라 덧셈의 규칙이 변경되더라도 +=의 코드만 수정하면 되므로 코드를 유지하기도 훨씬 더 쉽다.

    복사 생성 및 대입 금지

    클래스는 일종의 타입이므로 기본 타입과 완전히 동일해질 수 있는 모든 문법이 제공된다. 선언, 초기화, 복사, 대입, 연산 등등 int가 할 수 있는 모든 동작을 다 할 수 있다. 그러나 경우에 따라서는 이런 것이 어울리지 않거나 그래서는 안되는 클래스들도 있다. 예를 들자면 하드웨어를 직접적으로 제어하거나 유일한 자원을 관리하는 객체를 들 수 있는데 하나만 가지고도 충분히 원하는 동작을 모두 할 수 있으므로 굳이 둘을 만들 필요가 없다.

    이런 예는 멀리서 찾을 것도 없이 표준 입출력 스트림 객체인 cin, cout을 보면 된다. 이 객체 한쌍으로 화면에 원하는 모든 출력을 할 수 있고 키보드로 입력을 받을 수 있는데 cin, cout이 두 개씩 있을 필요가 없지 않은가? 어떤 경우에는 동일한 타입의 객체가 두 개 있을 경우 혼선이 빚어지기도 하고 서로 간섭하여 오동작하거나 데드락에 걸리는 부작용도 있다. 이런 클래스들은 허가되지 않는 연산을 적절히 막아야 하는데 금지할 필요가 있는 대표적인 연산이 복사 생성과 대입이다.

    복사 생성과 대입을 못하는 클래스를 만드는 방법은 생각보다 쉽다. 복사 생성자와 대입 연산자를 선언하되 둘 다 private 영역에 두는 것이다. 아예 정의하지 않으면 컴파일러가 디폴트를 만드므로 반드시 private영역에 직접 선언해야 한다. 어차피 호출되지 않을 함수들이므로 본체의 내용은 작성하지 않아도 상관없다. Person 클래스는 복사, 대입이 모두 가능한 경우이긴 하지만 금지해야 한다고 가정하고 위 예제를 대상으로 이 동작들을 금지시켜 보자.

     

    class Person

    {

    private:

         char *Name;

         int Age;

         Person(const Person &Other);

         Person &operator =(const Person &Other);

         ....

     

    이렇게 해 놓으면 Person Young=Boy; 같은 선언문이나 Girl=Boy; 같은 대입문이 실행될 때 컴파일러가 복사 생성자나 대입 연산자를 호출하려고 할 것이다. 객체를 선언하는 곳은 객체의 외부이므로 private 멤버를 호출할 수 없으며 컴파일 중에 이 동작이 허가되지 않는다는 것을 알 수 있다. 실행중에 문제를 일으키는 것보다 컴파일할 때 이 동작은 금지되었음을 확실히 알리는 것이 바람직하다. 좀 더 적극적으로 에러 내용을 상세하게 알리고 싶을 때는 이 둘을 public 영역에 두되 assert문을 작성해 놓는 방법을 쓸 수 있다.

    두 함수를 private 영역에 둘 때 본체 내용은 아예 작성하지 않는 것이 좋다. 왜냐하면 외부에서 이 함수를 호출하는 것은 컴파일러가 컴파일 중에 막아 주지만 클래스 내부의 멤버 함수나 프렌드 함수에서는 여전히 이 함수를 호출할 수 있기 때문이다. 함수를 선언만 해 놓고 본체를 정의하지 않더라도 이 함수가 호출되기 전에는 링커가 본체를 찾지 않으므로 아무 이상이 없다. 만약 정의되지도 않는 함수를 호출하려고 하면 컴파일은 무사히 되지만 링크할 때 에러로 처리되므로 이 동작이 불가능하다는 것을 알 수 있다. 외부에서 불가능한 동작을 시도하면 컴파일러가 막아주고 내부에서 엉뚱한 짓을 하려면 링커가 막아준다. C++의 객체는 이런 식으로 실수든 고의든 허가되지 않는 위험한 연산을 스스로 방어하도록 작성되어야 한다.

    복사 생성자, 대입 연산자 작성 규칙은 나름대로 복잡해서 이해는 되더라도 실무에서 직접 작성하기는 쉽지가 않다. 개념적인 이해는 꼭 해 두고 실제 코드를 작성할 때는 Person3 예제에서 코드를 복사한 후 원하는 부분만 수정하는 것이 편리하다. Person3 예제의 복사 생성자, 대입 연산자는 모든 상황에 대해 잘 작동하도록 만든 모범 답안이다.

     

    반응형
    반응형

    다음과 같이 선언 되어 있다고 가정한다

    어떤 .h 파일에서...

    class NineTile{
    public :

    static CRect mTileRect;

    static void SetTileRectSize( CRect TileSize ){
    mTileRect = TileSize; 
    }
    }




    main.cpp 가 아닌 어느 cpp 파일에서 Ninetile 의 static 함수로 접근하여 멤버변수 static CRect mTileRect;

    에 접근하려고 하면  NineTile.cpp 에 static CRect NineTile::mTileRect; 의 멤버 초기화를 쓰면 안되는 경우가

    발생한다..,  다른 프로젝트에서 만들어진 Dll 파일을 다른 cpp 에서 읽어들여 사용 하는 경우.

    호출은 아래에서와 같이 main 이 아닌 어느 cpp

    CRect tempTileRect = CRect(0,0, pMapLayerData->miTileWidth,pMapLayerData->miTileHeight );
    NineTile::SetTileRectSize(tempTileRect );





    어떤 cpp 파일에서 static 함수를 호출 한 후 nineTile 의 static 멤버에 접근하려면

    어느 cpp 위에 CRect NineTile::mTileRect;  의 static 변수 선언 문이 있어야 에러가 나지 않는다...


    dll 에서 읽어 온것인지라 static 변수 선언을 양쪽에서 해줘야 하나 ????
     

     
     
     
    프로세스간 static 변수 공유
     
    dll은 기본적으로 코드 공유를 위한 목적으로 사용됩니다.
    즉, dll에서 사용되는 데이터 값은 dll을 사용하는 애플리케이션 간에 공유되지 않죠.

    예를 들어, dll 안에 static int g_nCount = 0; 라는 값이 있을 때, 
    dll에서 export하는 Increase라는 함수를 호출하면 g_nCount++; 라는 구문이 실행되고, g_nCount 값을 리턴한다고 가정하죠.

    이 때, 서로 다른 2개의 애플리케이션(즉, 프로세스)에서 
    이 dll을 로딩하고 Increase를 호출하면 g_nCount 값은
    각각의 프로세스에서 증가되고 모두 1이 리턴됩니다.

    즉, 처음에 말한 바와 같이 dll 내에서 static으로 선언한다고 해서
    이 변수가 dll을 사용하는 프로세스 간에 공유되지는 않습니다.
    그러나 변수 값을 공유하는 방법이 있습니다.

    pragma directive를 사용하면 되는데
    공유하고자 하는 변수를 

    #pragma data_seg(".ioshare")
    static int g_nCount = 0;
    #pragma data_seg()

    위와 같이 data_seg pragma directive 안에 넣어줍니다.
    이 때 .ioshare 라는 문자열은
    dll 내에 어떤 데이터 영역으로 표시되는지를 결정합니다.
    지정하지 않으면 기본적으로 .data 영역에 들어가게 되죠.
    (Windows PE 포맷의 기본 데이터 영역입니다.)

    프로세스간 데이터 공유에 대해서는 다양한 IPC 방법이 있지만
    dll을 사용해서 이렇게도 할 수 있다는 것을 기억하면 좋겠죠. ^^




    반응형
    반응형


    <헤더파일>

    ...

    class Nice

    {

    private:

       static int totalObject;

    // static int totalObject = 0;  (안 돼!)

    public:

        Nice();

        ~Nice();

        void ImAMethod();

    }

    ...

     

    <구현파일>

    ...

    int Nice::totalObject = 0; // static 멤버 변수의 초기화

     

    Nice::Nice()

    {

        // totalObject = 0;  (안 돼!)

    ...







    // 정적 변수

     

    Num은 클래스의 멤버이면서 클래스로부터 생성되는 모든 객체가 공유하는 변수여야 한다. 이것이 바로 정적 멤버 변수의 정의이며 이 문제를 풀 수 있는 유일한 해결책이다.

     

    //h. 파일

    class Count

    {

    private:

         int Value;

        static int Num;

    public:

         Count() { Num++; }

         ~Count() { Num--; }

         void OutNum() {

              printf("현재 객체 개수 = %d\n",Num);

         }

    };

     

    //cpp 파일

    int Count::Num=0;

    .....

     

    헤더 파일의 클래스 선언부에 정적 멤버 변수에 대한 내부 선언이 있고 구현 파일에 정적 멤버 변수에 대한 외부 정의 및 초기값 지정문이 온다. 이게 관례이다. 만약 헤더 파일에 외부 정의를 둔다면 헤더 파일이 두 번 인클루드될 때 이중 정의되므로 에러로 처리될 것이다.

     

    이렇게 선언하면 Num은 Count 클래스에 소속되며 외부 정의에서 지정한 초기값으로 딱 한 번만 초기화된다. Count형의 객체 A,B,C가 생성되었다면 각 객체는 자신의 고유한 멤버 Value를 개별적으로 가지며 정적 멤버 변수 Num은 모든 객체가 공유한다. 그래서 각 객체의 생성자에서 증가, 파괴자에서 감소하는 대상은 공유된 변수 Num이며 한 변수값을 모든 객체가 같이 관리하므로 Num은 생성된 객체의 정확한 개수를 유지할 수 있다.

     

    정적 멤버 변수는 객체와 논리적으로 연결되어 있지만 객체 내부에 있지는 않다. 정적 멤버 변수를 소유하는 주체는 객체가 아니라 클래스이다. 그래서 객체 크기에 정적 멤버의 크기는 포함되지 않으며 sizeof(C) = sizeof(Count)는 객체의 고유 멤버 Value의 크기값인 4가 된다.

     

    단 외부에서 정적 멤버 변수를 정의할 때는 예외적으로 액세스 속성에 상관없이 초기값을 줄 수 있다. 초기식은 대입과는 다르므로 액세스 속성의 영향을 받지 않는다. 정적 멤버 변수를 외부에서도 참조할 수 있도록 공개하려면 클래스 선언부의 public영역에 선언하면 된다. 외부에서 정적 멤버를 액세스할 때는 반드시 소속을 밝혀야 하는데 두 가지 방법으로 소속을 밝힐 수 있다.

     

    Count C;

    Count::Num=3;            // 클래스 소속

    C.Num++;                        // 객체 소속

     

    원한다면 C.Num처럼 객체.멤버 식으로 객체의 소속인 것처럼 표현할 수도 있다. 이 때 C객체의 이름은 별다른 의미는 없으며 C객체가 소속된 클래스를 밝히는 역할만 한다. 정적 멤버에 대해 객체의 소속으로 액세스하는 것은 일단 가능하지만 일반적이지 않으며 바람직하지도 않다. 정적 멤버는 논리적으로 클래스 소속이므로 가급적이면 클래스::멤버 식으로 액세스하는 것이 합당하다.

     

    //정적 함수

     

    정적 멤버 함수의 개념도 정적 멤버 변수의 경우와 비슷하다. 객체와 직접적으로 연관된다기보다는 클래스와 연관되며 생성된 객체가 하나도 없더라도 클래스의 이름만으로 호출할 수 있다. 일반 멤버 함수는 객체를 먼저 생성한 후 obj.func() 형식으로 호출한 객체에 대해 어떤 작업을 한다. 이에 비해 정적 멤버 함수는 Class::func() 형식으로 호출하며 클래스 전체에 대한 전반적인 작업을 한다. 주로 정적 멤버 변수를 조작하거나 이 클래스에 속한 모든 객체를 위한 어떤 처리를 한다.

    정적 멤버 함수를 선언하는 방법은 정적 멤버 변수와 동일하다. 클래스 선언부의 함수 원형앞에 static이라는 키워드만 붙이면 된다. 정적 멤버 함수의 본체는 클래스 선언부에 인라인 형식으로 작성할 수도 있고 아니면 외부에 따로 정의할 수도 있는데 외부에 작성할 때 static 키워드는 생략한다.

     

    #include <Turboc.h>

     

    class Count

    {

    private:

         int Value;

         static int Num;

     

    public:

         Count() { Num++; }

         ~Count() { Num--; }

         static void InitNum() {

              Num=0;

         }

         static void OutNum() {

              printf("현재 객체 개수 = %d\n",Num);

         }

    };

    int Count::Num;

     

    void main()

    {

         Count::InitNum();

         Count::OutNum();

         Count C,*pC;

         C.OutNum();

         pC=new Count;

         pC->OutNum();

         delete pC;

         pC->OutNum();

         printf("%d",sizeof(C));

    }

     

    InitNum은 정적 멤버 함수이므로 Count 클래스의 객체가 전혀 없는 상태에서도 호출될 수 있다. main에서 Count::InitNum()을 먼저 호출하여 Num을 0으로 초기화하였다. 변수를 초기화하는 별도의 함수를 만들었으므로 원한다면 실행중에 언제든지 이 함수를 호출하여 Num을 0으로 리셋할 수도 있다.

     

    C 객체를 생성한 후 C.OutNum()을 호출하면 1이 출력되고 pC객체를 동적 생성한 후 pC->OutNum()을 호출하면 2가 출력된다. 이 두 호출의 예처럼 정적 멤버 함수를 객체의 이름으로 호출할 수도 있지만 이때 객체의 이름은 아무런 의미가 없으며 컴파일러는 객체가 소속된 클래스의 정보만 사용한다. C.OutNum(), pC->OutNum(); 이라는 표현을 허용할 뿐이지 이 호출은 실제로 Count::OutNum()으로 컴파일된다는 얘기다.

     

    그래서 delete pC;로 pC 객체를 해제한 후에도 pC->OutNum()이라는 호출이 정상적으로 동작한다. 컴파일러는 pC에 실제로 객체가 생성되어 있는지를 볼 필요도 없으며 pC가 Count *형이라는 것만 참조할 뿐이다. 심지어 main의 4번째 줄에 pC가 할당되기도 전인 C.OutNum()을 pC->OutNum()으로 바꿔도 잘 동작한다. 이걸 보면 컴파일러가 포인터의 타입만으로 호출할 함수를 결정한다는 것을 알 수 있다.

     

    주의)

    정적 멤버 함수는 특정한 객체에 의해 호출되는 것이 아니므로 숨겨진 인수 this가 전달되지 않는다. 클래스에 대한 작업을 하기 때문에 어떤 객체가 자신을 호출했는지 구분할 필요가 없으며 따라서 호출한 객체에 대한 정보도 필요가 없는 것이다. 그래서 정적 멤버 함수는 정적 멤버만 액세스할 수 있으며 일반 멤버(비정적 멤버)는 참조할 수 없다. 왜냐하면 일반 멤버 앞에는 암시적으로 this->가 붙는데 정적 멤버 함수는 this를 전달받지 않기 때문이다. 정적 멤버 함수인 InitNum에서 비정적 멤버인 Value를 참조하는 것은 불가능하다.

     

    static void InitNum() {

         Num=0;

         Value=5;

    }

     

    이 코드를 컴파일하면 정적 멤버 함수에서 Value를 불법으로 참조했다는 에러 메시지가 출력된다. InitNum의 본체에서 Value를 칭하면 누구의 Value인지를 판단할 수 없다. 또한 정적 멤버 함수는 생성된 객체가 전혀 없어도 호출할 수 있는데 이때 Value는 아예 존재하지도 않는다. 비정적 멤버 함수도 호출할 수 없으며 오로지 정적 멤버만 참조할 수 있다.

    [출처] Static 멤버 변수의 초기화|작성자 

    반응형
    반응형

    C++ 소스 에러 목록

     

    1. 'strcpy': 식별자를 찾을 수 없습니다.

    - strcpy 함수를 포함한 헤더 string.h를 추가해준다.

     

    2. estrcpy' : 매개 변수 2을(를) 'char'에서 'const char *'(으)로 변환할 수 없습니다.

    - strcpy 함수에 들어가는 문자를 문자열로 선언해준다.

     

    3. 'init' :참조되지 않은 지역 변수입니다.

    - 사용되지 않는 변수이므로, 삭제해준다.

     

    4. 구문 오류 : ')'이(가) '문자열' 앞에 없습니다.

    - ')' 가 빠졌으므로, 빠진 위치에 적절히 추가해준다.

     

    5. 'fopen' : 함수는 1개의 매개 변수를 사용하지 않습니다.

    - fopen 에는 2가지 파라메터가 필요하다. 반드시 2가지를 추가해주도록 한다.

     

    6. 'p_' : 선언되지 않은 식별자입니다.

    - p_를 사용하기전에 이것이 무엇인지 반드시 선언해주어야한다.

     

    7. 구문 오류 : ';'이(가) ')' 앞에 없습니다.

    - ';' 가 빠졌으므로, 빠진 위치에 적절히 추가해준다.

     

    8. '=' : 'char'에서 'char [100]'(으)로 변환할 수 없습니다.

    - 등호 표시 '=' 는 양쪽이 같은 형일때만 사용할 수 있다. 양쪽을 같게 바꿔준다.

     

    9. 'delete' : 포인터가 아닌 개체를 삭제할 수 없습니다.

    - delete를 사용하는 개체가 포인터인지 확인하고 포인터가 아닐 시 해당 라인을 제거한다.

     

    10. 'fprintf' : 매개 변수 1을(를) 'const char [9]'에서 'FILE *'(으)로 변환할 수 없습니다.

    - fprintf 맨 앞에 파일 포인터 값을 넣어준다.

     

    11. 구문 오류 : ')'이(가) 'return' 앞에 없습니다.

    - return 구문 앞에 )를 삽입하여 블록을 닫아준다.

     

    12. 오버로드된 'operator ->'의 'StudentInfo **' 반환 형식이 잘못되었습니다.

    - -> 연산자를 사용하기 위해 사용된 변수를 포인터로 사용했는지 확인한다.

     

    13.'student_number' : 'std::list<_Ty>::_Iterator<_Secure_validation>'의 멤버가 아닙니다.

    - student_number 가 포인터의 멤버가 맞는지 확인한다.

     

    14. '초기화 중' : 'std::list<_Ty>::_Iterator<_Secure_validation>'(으)로 변환할 수 없습니다.

    - iterator을 호출한 개체에 '*' 가 붙어있는지 확인한다.

     

    15. 이항 '!=' : 오른쪽 피연산자로 연산자가 없거나 허용되는 변환이 없습니다.

    - iterator을 호출한 개체에 '*' 가 붙어있는지 확인한다.

     

    16. 'StudentInfo' 형식에 오버로드된 멤버 'operator ->'가 없습니다.

    - iterator을 호출한 개체에 '*' 가 붙어있는지 확인한다.

     

    17. '->StudentInfo::student_number' : 왼쪽 피연산자에 'struct' 형식이 있습니다. '.'를 사용하십시오.

    - '->' 포인터 호출 기호 대신 '.'  스트럭트 호출 기호로 바꿔서 사용한다.

     

    18. 첨자는 배열 또는 포인터 형식을 사용해야 합니다.

    - 첨자에 []를 붙이거나 *를 붙여준다.

     

    19. 구문 오류 : ';'이(가) ')' 앞에 없습니다.

    - ';' 가 빠졌으므로, 빠진 위치에 적절히 추가해준다.

     

    20. 구문 오류 : ')'이(가) 'return' 앞에 없습니다.

    - return 구문 앞에 )를 삽입하여 블록을 닫아준다.

     

    21. 'jeongsu' : 선언되지 않은 식별자입니다.

    - jeongsu를 사용하기전에 이것이 무엇인지 반드시 선언해주어야한다.

     

    22. 'int'에서 'bool'(으)로 잘립니다.

    - bool 로 선언된 것을 int 형으로 바꿔준다.

     

    23. 왼쪽 피연산자는 l-value이어야 합니다.

    - 왼쪽에 있는 피연사자를 상수가 아닌 변수로 바꿔준다.

     

    24. '->student_number' 왼쪽은 클래스/구조체/공용 구조체/제네릭 형식을 가리켜야 합니다.

    - student_number를 호출한 것이 무엇인지 확인하고 클래스/구조체/공용 구조체로 바꿔준다.

     

    25. '->height' 왼쪽은 클래스/구조체/공용 구조체/제네릭 형식을 가리켜야 합니다.

    - height를 호출한 것이 무엇인지 확인하고 클래스/구조체/공용 구조체로 바꿔준다.

     

    26. 구문 오류 : '>'

    - <> 안에 들어간 것이 무엇인지 확인하고 매개 변수로 올바르게 바꿔준다.

     

    27. 'std' : 클래스 또는 네임스페이스 이름이 아닙니다.

    - "stdlib.h" 헤더를 include 시켜준다.

     

    28. 'iterator' : '`global namespace''의 멤버가 아닙니다.

    - iterator을 호출한 매개 변수가 올바르게 사용되었는지 확인한다.

     

    29. 'Compare2' : 값을 반환해야 합니다.

    - return 구문을 붙여 함수에 맞는 값을 반환해준다.

     

    30. 'fopen' : 함수는 1개의 매개 변수를 사용하지 않습니다.

    - fopen 에 2개의 파라메터 값을 삽입해준다. 파일포인터, 파일모드의 2개.

     

    31. '->std::list<_Ty>::push_back' : 왼쪽 피연산자에 'class' 형식이 있습니다. '.'를 사용하십시오.

    - push_back 왼쪽에는 클래스 형식에 알맞는 '.' 기호를 사용한다.

     

    32. 'std::list<_Ty>' 형식에 오버로드된 멤버 'operator ->'가 없습니다.

    - 특정 함수를 호출할 때 사용되는 연산자가 '->' 로 잘못 사용되었으므로 '.' 로 바꿔준다.

     

    33. 'getch': 식별자를 찾을 수 없습니다.

    - conio.h 헤더를 include 해준다.

     

    34. 이전 오류를 복구할 수 없습니다. 컴파일이 중지됩니다.

    - 상위에 생긴 C++ 소스 에러를 모두 올바르게 수정하고 컴파일을 다시 시도한다.

     

    35. 구문 오류 : ']'

    - [] 표시가 제대로 열리고 닫혔는지 확인해준다.

     

    36. 'fopen': This function or variable may be unsafe. Consider using fopen_s instead.

    - 새로운 C++ 에서는 각 상황에 알맞는 형식의 내장함수가 새로 추가되었다. 상황에 맞게 바꿔주도록한다.

     

    37. 'fscanf': This function or variable may be unsafe. Consider using fscanf_s instead.

    - 새로운 C++ 에서는 각 상황에 알맞는 형식의 내장함수가 새로 추가되었다. 상황에 맞게 바꿔주도록한다.

     

    38. 'printf': This function or variable may be unsafe. Consider using printf_s instead.

    - 새로운 C++ 에서는 각 상황에 알맞는 형식의 내장함수가 새로 추가되었다. 상황에 맞게 바꿔주도록한다.

     

    39. 'scanf': This function or variable may be unsafe. Consider using scanf_s instead.

    - 새로운 C++ 에서는 각 상황에 알맞는 형식의 내장함수가 새로 추가되었다. 상황에 맞게 바꿔주도록한다.

     

    40. 'StudentInfo' 다음에 'bool'이(가) 올 수 없습니다. ';'이 있어야 합니다.

    - StudentInfo 뒤에 ; 종결문이 제대로 적혀있는지 확인하고 추가해준다.

     

    41. 'return' : 'bool'에서 'StudentInfo'(으)로 변환할 수 없습니다.

    - return 반환하는 부분에 bool 로 선언된 것이 올바른 것이 확인해준다.

     

    42. 구문 오류 : ';'이(가) '}' 앞에 없습니다.

    - ';' 기호가 제대로 적혀있는지, 위치를 확인한다.

     

    43. 구문 오류 : '>'

    - > 연산자가 올바르게 사용되었는지 확인한다. 대소 비교에 사용된다.

     

    44. 'sqrt' : 오버로드된 함수에 대한 호출이 모호합니다.

    - 제곱근을 구하는 함수에 인수를 double 형으로 임시 선언해준다.

     

    45. 이항 '!=' : 오른쪽 피연산자로 연산자가 없거나 허용되는 변환이 없습니다.

    - 오류가 난 줄의 != 연산자 좌우의 형을 비교하고 같은것인지 확인한다.

     

    46. '<' : 의미 없는 연산자입니다. 파생 작업이 있는 연산자여야 합니다.

    - < 연산자를 사용하기 이전에 그 바로 전의 연산이 제대로 되어있는지 확인한다.

     

    47. 초기화되지 않은 'dummyDay' 지역 변수를 사용했습니다.

    - dummyDay의 초기값을 설정해준다.

     

    48. 'Sort': 식별자를 찾을 수 없습니다.

    - Sort 함수를 메인함수 위에 적어, preprocessing 해준다.

     

    49. 'gets': This function or variable may be unsafe. Consider using gets_s instead.

    - 새로운 C++ 에서는 각 상황에 알맞는 형식의 내장함수가 새로 추가되었다. 상황에 맞게 바꿔주도록한다.

     

    50. 'strcmp' : 매개 변수 1을(를) 'char [1][1]'에서 'const char *'(으)로 변환할 수 없습니다.

    - 매개 변수를 임시로 (const char*) 로 선언해준다.

     

    51. 'double'에서 'float'(으)로 잘립니다.

    - float 으로 사용되는 것을 double 로 확장해준다.

     

    52. 'temp' :참조되지 않은 지역 변수입니다.

    - temp 는 사용되지 않는 변수이므로 삭제해주도록 한다.

     

    53. 상수에 줄 바꿈 문자가 있습니다.

    - 상수가 올바르게 상수만 적혀있는지 확인한다.

     

    54. 'y' : 선언되지 않은 식별자입니다.

    - y 로 선언된 변수가 있는지, 오타인지 확인한다.

     

    55. ';' : 제어된 빈 문이 있습니다. 이 문이 필요합니까?

    - 아무 명령이 없는 문장이므로 삭제해준다.

     

    56. 'menu' 레이블이 정의되지 않았습니다.

    - goto 문에 사용될 레이블의 위치를 지정해준다.

     

    57. 'Sort' : 함수는 3개의 매개 변수를 사용하지 않습니다.

    - Sort 라고 정의된 함수가 사용하는 매개 변수의 갯수에 따라 소스를 수정해준다.

     

    58. 구문 오류 : int'은(는) ';' 다음에 와야 합니다.

    - int 앞에 ';' 기호가 제대로 적혀있는지 확인하고 추가해준다.

     

    59. 함수 호출에 인수 목록이 없습니다. '&std::list<_Ty>::size'을(를) 사용하여 멤버에 대한 포인터를 만드십시오.

    - size 라는 함수가 ()를 붙여 포인터로 만들어서 사용되었는지 확인한다.

     

    60. '<=' : 'int'에서 'unsigned int (__thiscall std::list<_Ty>::* )(void) const'(으)로 변환되지 않았습니다.

    - '<=' 연산자 양쪽의 변수 형이 'int' 로 같게 맞춰준다.

     

    반응형
    반응형

    _MSC_VER 은 Visual C++ 의 컴파일러 버전을 나타내는 매크로 상수임

    1000 : Visual C++ 4.x
    1100 : Visual C++ 5
    1200 : Visual C++ 6
    1300 : Visual C++ .NET
    1310 : Visual C++ .NET 2003
    1400 : Visual C++ .NET 2005
    1500 : Visual C++ .NET 2008

    /* stdafx.h */

    #if _MSC_VER > 1000
    #pragma once
    #endif // _MSC_VER > 1000

    반응형
    반응형

    상수란 말씀하신 것처럼 값을 가지는 변수이나 그 값을 바꿀 수 없는 변수입니다.

    즉 한번 메모리에 변수를 지정하고 그 변수에 값을 초기화하고 난 그 이후에는 값을 바꿀 수 없는

    변수를 상수라고 일컫습니다. 다음과 같은 경우, PI가 상수가 되겠죠.

    const float PI=3.14f; // C

    public static final float PI=3.14f; // Java

    반면 리터럴은 이러한 변수 및 상수에 저장되는 값 자체를 일컫습니다.

    정수 리터럴, 실수 리터럴, 문자열 리터럴 이런 것들이 프로그래밍 언어의 한 요소로서

    리터럴이라고 불리는 겁니다.

    정수 리터럴 ---> 10, 1, 1000 등등
    실수 리터럴 ---> 10.1, 10e3 등등
    문자열 리터럴 ---> "System" "Exit" 등등

    쉽게 얘기하면 변수나 상수는 메모리에 할당된 공간(내지는 그 이름, 사실 이보다는 바인딩이라고 해서

    좀더 복잡한 얘기를 해야하지만 질문 내용과 무관하므로 생략하도록 하겠습니다.)이라면 리터럴은

    이 공간에 저장되는 값이라고 할 수 있습니다.

    ===========================================================================================

    바로 값이 되는 상수를 말하는 거죠.

    보통 프로그래밍을 할때 변수라는 걸 사용하는데.
    이 변수라는건 여러가지가 결합된 요소로 볼 수 있습니다.
    변수명, 주소, 공간, 형식이란 걸로 결합이 됩니다.
    변수명이란 식별자로 해당 메모리 주소를 참조해서 지정된 형식으로 데이타를 읽거나 쓸 수 있는것이죠.
    이에 반해서 상수란건 동일하게 구성이 되지만 쓰기연산이 금지된 겁니다.

    읽기만 가능하고 쓰기가 불가능해서 항상 같은 값을 유지하자는 거죠.

    리터럴은 다시 변수명이란게 사라진겁니다.

    이름같은 식별자를 통하지 않고. 바로 값이 되는 상수들을 특별히 리터럴이라고 합니다.

    숫자 상수 10이나 문자 상수 'A' 문자열 상수 'ABC' 이런것들이 리터럴이죠.

    C에서는 특별히 '\xx'같은 형태를 이스케이트 캐릭터, 확장열이라고 따로 문자상수를 정의해서 사용하기도 합니다

    반응형
    반응형

    http://msdn.microsoft.com/ko-kr/library/2k3te2cs(VS.90).aspx

    http://bingsoo.com/blog/?p=1507


    ex)

    \.SaveBin\(|\-\>SaveBin\(

    .SaveBin( 또는 ->SaveBin( 의 문자열을 찾는다

     

    찾기 및 바꾸기 작업에 사용할 정규식


    식 작성기에는 다음과 같은 자주 사용하는 정규식이 표시됩니다.

    구문

    설명

    예제

    단일 문자

    .

    줄 바꿈 문자를 제외한 모든 단일 문자를 찾습니다.

    a.o는 "around"에서 "aro"와 일치하고 "about"에서 "abo"와 일치하지만 "across"에서 "acro"와는 일치하지 않습니다.

    0 이상

    *

    앞의 식을 0번 이상 찾습니다.

    a*b는 "bat"의 "b" 및 "about"의 "ab"를 찾습니다.

    e.*e는 "enterprise"라는 단어를 찾습니다.

    하나 이상

    +

    앞의 식을 한 번 이상 찾습니다.

    ac+는 "race" 및 "ace" 같이 문자 "a"와 최소한 하나 이상의 "c"를 포함하는 단어를 찾습니다.

    a.+s는 "access"라는 단어를 찾습니다.

    줄의 시작

    ^

    줄의 시작 부분에서 일치하는 문자열을 찾습니다.

    ^car는 "car"라는 단어가 편집기 줄의 첫 번째 문자 집합에 나올 때만 찾습니다.

    줄의 끝

    $

    줄의 끝 부분에서 일치하는 문자열을 찾습니다.

    end$는 "end"라는 단어가 편집기 줄의 마지막 문자 집합에 나올 때만 찾습니다.

    단어의 시작

    <

    텍스트의 해당 지점에서 시작하는 단어만 찾습니다.

    <in은 "in"으로 시작하는 "inside" 및 "into" 등의 단어를 찾습니다.

    단어의 끝

    >

    텍스트의 해당 지점에서 끝나는 단어만 찾습니다.

    ss>는 "ss"로 끝나는 "across" 및 "loss" 등의 단어를 찾습니다.

    줄 바꿈

    \n

    운영 체제에 독립적인 줄 바꿈을 찾습니다. 바꾸기 식에 줄 바꿈을 삽입합니다.

    End\nBegin은 "End"가 줄의 마지막 문자열이고 "Begin"이 다음 줄의 첫 번째 문자열인 경우에만 단어 "End" 및 "Begin"을 찾습니다.

    바꾸기 식의 경우에는 다음과 같습니다.

    Begin\nEnd는 첫 줄에서 "End"라는 단어를 "Begin"으로 바꾸고 줄 바꿈을 삽입한 다음 "Begin"을 "End"로 바꿉니다.

    집합에 있는 한 문자

    []

    [] 안의 문자 중 하나를 찾습니다. 문자 범위를 지정하려면 대시(-)로 구분하여 시작 및 끝 문자를 입력합니다(예: [a-z]).

    be[n-t]는 "between"의 "bet", "beneath"의 "ben", 그리고 "beside"의 "bes"는 찾지만 "below"의 "bel"은 찾지 않습니다.

    집합에 없는 한 문자

    [^...]

    ^ 뒤에 오는 문자 집합에 포함되지 않는 문자를 찾습니다.

    be[^n-t]는 "before"의 "bef", "behind"의 "beh", 그리고 "below"의 "bel"은 찾지만 "beneath"의 "ben"은 찾지 않습니다.

    또는

    |

    OR 기호(|) 앞 또는 뒤에 나오는 식과 일치하는 항목을 찾습니다. 이 구문은 그룹에서 가장 자주 사용됩니다.

    (sponge|mud) bath는 "sponge bath"와 "mud bath"를 모두 찾습니다.

    이스케이프

    \

    백슬래시(\) 뒤에 나오는 문자열을 리터럴로 간주하고 일치하는 항목을 찾습니다. 이렇게 하면 { 및 ^과 같이 정규식 표기에 사용되는 문자를 찾을 수 있습니다.

    \^는 ^ 문자를 검색합니다.

    태그 식

    {}

    중괄호로 묶인 식을 포함하여 태그가 지정된 텍스트를 찾습니다.

    zo{1}은 "Alonzo1" 및 "Gonzo1"의 "zo1"은 찾지만 "zone"의 "zo"는 찾지 않습니다.

    C/C++ 식별자

    :i

    ([a-zA-Z_$][a-zA-Z0-9_$]*) 식의 약식 형태입니다.

    가능한 모든 C/C++ 식별자를 찾습니다.

    따옴표 붙은 문자열

    :q

    (("[^"]*")|('[^']*')) 식의 약식 형태로, 작은따옴표나 큰따옴표로 묶인 모든 문자를 찾고 따옴표 자체도 찾습니다.

    :q는 "test quote" 및 'test quote'는 찾지만 can't의 't는 찾지 않습니다.

    공백 또는 탭

    :b

    공백 또는 탭 문자를 찾습니다.

    Public:bInterface는 텍스트에서 "Public Interface"라는 구를 찾습니다.

    정수

    :z

    모든 숫자 조합을 찾는 ([0-9]+) 식의 약식 형태입니다.

    "1", "234", "56" 등의 모든 정수를 찾습니다.


    반응형
    반응형

    http://www.winapi.co.kr/:

    아래 3개는 모두 동일

    const int Day=24;

    int const Day=24;

    const Day=24; //int 를 안쓰면 default 로 int 가 선언되어진다

    int 의 경우 const 순서 상관없이 변수값이 const 가 되지만 포인터의 경우에는 차이가 있다

    15-1-나.포인터와 const

    const를 포인터와 함께 사용하면 효과가 조금 달라진다. 다음 예제는 포인터와 const의 관계를 실험해 보기 위해 작성했는데 컴파일해 보면 몇 군데서 에러가 발생할 것이다.

    : ConstPointer

    #include <Turboc.h>

    void main()

    {

    int ar[5]={1,2,3,4,5};

    int *pi1=&ar[0];

    pi1++; // 포인터가 다른 대상체를 가리킬 수 있다.

    *pi1=0; // 대상체를 변경할 수 있다.

    const int *pi2=&ar[0];

    pi2++; // 포인터가 다른 대상체를 가리킬 수 있다.

    *pi2=0; // 에러 : 대상체가 상수이므로 변경할 수 없다.

    int * const pi3=&ar[0];

    pi3++; // 에러 : 포인터가 다른 대상체를 가리킬 수 없다.

    *pi3=0; // 대상체는 변경할 수 있다.

    const int * const pi4=&ar[0];

    pi4++; // 에러 : 포인터가 다른 대상체를 가리킬 수 없다.

    *pi4=0; // 에러 : 대상체가 상수이므로 변경할 수 없다.

    }

    반응형
    반응형

    http://www.gpgstudy.../viewtopic.php?t=566
    http://www.gpgstudy.../viewtopic.php?t=627

    http://www.gpgstudy.../viewtopic.php?t=860

    * get - 가장 흔히 쓰이며 의미하는 범위도 가장 넓다. GetDC(), getInstance(), getPosition() 등등. get은 아래에 나열된 다른 모든 돌려주는 동사들을 포괄한다. 그러나, get의 용도를 최대한 한정시킨다면 주로 ‘이미 계산되어 있는 객체의 속성 또는 시스템의 특정 상태를 돌려주며 상태, 시스템을 변경하거나 어떠한 복잡한 처리를 하지 않는’ 함수에 쓰는 것이 좋다고 본다.


    * retrieve - get에 비해 이것은 반환할 결과를 찾기 위한 처리(주로 검색)가 일어남을 암시한다. 필요하다면 이 동사는 search 같은 실제 수행 동사와 get으로 나눌 수 있다.

    예:

    aCheapestApple = anAppleBox.retrieveApple(CHEAPEST);

    =>

    anAppleBox.search(CHEAPEST);
    aCheapestApple = anAppleBox.getSearchResult();


    * acquire - 제한된 자원을 독점적으로 확보한다는 뜻이 있다. 예를 들어 주어진 윈도우 또는 시스템 전체에 DC가 하나이며 GetDC()로 얻은 DC를 ReleaseDC()로 풀기 전까지는 혼자만 사용해야 하는 상황이라고 하자. 그런 경우라면 GetDC() 보다는 AcquireDC()가 더 적합하다. 반환값은 확보한 객체 자체나 포인터, 핸들 등이다. 반대말은 release나 unacquire(get에 대해 release를 사용한다면 acquire에 대해서는 unacquire를 사용해서 구분해 주는 것이 좋을 것임).

    예:

    AudioManager AudioMgr = new DxAudioManagerImpl();
    ...
    AudioDevice* anAudioDevice = AudioMgr.acquireDevice();
    ...
    AudioMgr.unacquireDevice(anAudioDevice);

    * fetch - 돌려준 값을 담고 있는 컨테이너의 내부 상태가 변함을 의미한다. 주어진 컨테이너 안의 어떤 값을 가져오면 현재 값의 위치를 가리키는 포인터가 다음 값으로 이동하게 되는 경우에 fetch가 쓰인다. 데이터베이스 API에서 흔히 볼 수 있다. 스택의 pop과도 비슷하다. 또한 파일 시스템의 많은 함수들이 이러한 fetch 식으로 작동한다. 이는 moveNext와 get으로 분리될 수 있다.

    예:

    UnitList.reset();
    pUnit1 = UnitList.fetch();
    pUnit2 = UnitList.fetch();
    =>
    UnitList.moveFirst();
    pUnit1 = UnitList.getUnit();
    UnitList.moveNext();
    pUnit2 = UnitList.getUnit();

    * be 동사와 조동사 - 이들은 어떠한 상태 또는 여부를 뜻하는 부울 값을 돌려줄 때 주로 쓰인다.

    예:
    while ( isDone ) {
    if ( currentUnit.hasWeapon() ) ....
    ....

    }

    2. 객체의 상태를 변경하는 함수에 주로 쓰이는 동사들

    객체 또는 현재 범위의 어떤 값을 변경하는 함수들에 가장 흔히 쓰이는 동사는 set이다. 이런 동사들은 반환값이 없거나 또는 함수의 성공 여부를 알려주는 반환값을 가진다.

    * set - 가장 흔하게 쓰인다. 설명이 필요없겠지만 굳이 부연 설명을 하자면, get의 반대물이라는 차원에서, 상태의 변경에 복잡한 처리가 일어나지 않는 비교적 단순한 코드로 된 함수에 적합하다.

    그래픽 시스템

    display - 실제의 시각적 변화를 일으킨다는 의미가 있다(버퍼 스왑 등 실제 비디오 메모리 갱신의 차원에서). 또는 아래의 동사들을 포괄하는 고차원 함수에도 쓰인다(특정 그래픽 API에 국한되지 않는...)

    render - display와 비슷하나 내부 버퍼 메모리의 갱신만 의미할 수도 있다. 즉 render 후 상황에 따라 display가 수행되는 등. render부터는 특정 그래픽 API에 국한된 함수에 가까와진다. 목적어를 가질 수 있다(renderScene, renderBackground 등)

    draw - 개별 데이터나 개별 개체를 그린다는 의미로 많이 쓰인다. Model.draw()나 drawText() 등. render 안에서 일어나는 경우가 많다.

    put - 주로 2D에서 점이나 스프라이트를 ‘찍는다’는 의미로 쓰인다. 실제의 시각적 변화보다는 내부 메모리에서의 변화일 가능성이 크다. 3D에서는 별로 쓰이지 않음. 목적어를 가지는 경우가 많다(putPixel, putSprite 등)

    flush - 그래픽이나 장면 데이터를 스트림으로 간주할 때 쓰인다. 지금까지 명령 버퍼 또는 프레임 버퍼에 쌓여 있는 데이터를 실제 장치로 밀어 내보낸다는 의미를 가진다. 






    올리기동사에 추가해서 올려짐: 2002-09-06 09:49
    인용과 함께 답변 이 게시물을 del.icio.us에 추가

    가끔가다가..
    명사나 형용사로 함수명을 지어야 할때가 있습니다.
    무엇무엇과 같은것을...해라..
    음 단순한 동사로 했다가는 의미가 애매해지는 경우를 말하는것이죠..
    그럴때는 저의 경우는 Do를 앞에 써줍니다.
    행동을 해줘야 한다는걸 강조해 주는거죠..
    벽깨는 동작을 해라.. 라고 할때 BreakTheWall을 하면..
    벽을깨는거지만.
    DoMotionBreakingTheWall 이라고 하면 벽깨는 동작을 해라 라고 하게 됩니다.

    혹은.. bool값을 반환하는 단순상태의 경우는 Is나 Does를 앞에 써줍니다.
    이런 펑션을 If문 안에 써주면 주석을 쓰지않아도 의미가 명료해 집니다.
    GetBound?? 이런건 이름자체가 잘못된거지만.. bool을 이렇게 쓰면 웃길때가 있다고 치고
    IsInsideBound 는 명료하게 참거짓을 쿼리하는거라는 인식을 하게 하죠..
    음... 이건 제 습관이라서... 많은 공격 바랍니다.







    류광



    가입: 2001년 7월 25일
    올린 글: 3452
    소속: GPGstudy


    타 사이트 ID(?):
    docbook.kr::류광
     
    올려짐: 2002-09-06 17:18
    인용과 함께 답변 이 게시물을 del.icio.us에 추가


    아 좋은 지적입니다.. 동사들을 이야기하면서 뭔가 빠진 듯한 느낌이 들었는데 Do 류의 동사들이었네요. do, excute, run, process 등... 이들의 미묘한 차이에 대해서도 조만간 정리해야 겠습니다...

    한 가지.. Do의 경우 또 다른 용법은 Do 다음에 실제 동사가 오는 형태입니다. 이런 것은 처리의 위임이나 전처리 후 실제 처리 같은 경우에 많이 보이더군요. 예를 들어

    코드:
    // 다른 어떤 곳에서
    obj->OnDraw(); // 또는 그냥 obj->Draw()
    ...
    void SomeObject::OnDraw() // 역시 또는 그냥 Draw()
    {
    ...// 전처리
    // 실제 처리
    DoDraw();
    }


    이런 예에서 Do는 '이제 진짜로 하자~'라는 강조로 쓰이는 것 같습니다..






    반응형
    반응형


    보통은 자식 클래스가 자신의 부모 클래스의 생성자만 호출 가능하고, 할아버지 클래스의 생성자를

    호출 할 수 없지만, 가상 기반 클래스 에서는 예외가 있습니다...

    즉, 손자 클래스가 자신의 부모보다도 상위인 할아버지 클래스의 생성자를 호출 할 수

    있다는 점이죠...

    또한, 이 경우에 있어서는 중간 클래스를 의미하는 부모 클래스(class XY, class XZ) 에서는 비록...

    최상위 클래스의 생성자를 호출하는 코드인 CPointX(a) 가 XY, XZ 클래스의 생성자 초기화 리스트에

    있지만, 손자 클래스인 XYZ 클래스의 생성자 초기화 리스트 부분에서...단 "한 번만" 호출된다는

    점 입니다...

    브레이크 포인트를 걸어서 코드 흐름을 추적해 보면 알겠지만, 위 코드에선 CPointX(a) 부분이 한 번 밖에

    호출되지 않고, 이를 담당하는 것은 손자 클래스인 XYZ 클래스 입니다...

    정리하면,,,가상 기반 상속에서는 손자 클래스에서 할아버지 클래스의 생성자를 직접 호출한다는

    점이고, 중간 클래스인 부모 클래스에서는 비록, 그 상위 클래스의 생성자를 호출하는 코드가

    있다고 하더라도 호출되지 않는다는 점 입니다...

    또한...가상 상속을 사용하는 이유는 할아버지 클래스인 X 클래스를 상속받는 XY, XZ 클래스를 XYZ 라는

    클래스가 상속받기 때문에...XYZ 입장에서는 X 클래스의 멤버가 어디에서 온 멤버인지 모호하기 때문에

    이 가상상속을 사용하여, 모호함을 없애는 것입니다...

    X X

    | |

    XY XZ

    XY, XZ 를 상속받는 XYZ 클래스는 X의 멤버를 참조할 때 XY 에서 온 X인지, XZ 에서 온 X인지

    모호함으로 인해 판단하기 어렵다는 점입니다...

    그래서...이를 해결하고자 가상상속을 사용하는 것이고, 손자 클래스가 할아버지 클래스의 생성을

    직접 담당하는 것입니다...

    코드를 다음과 같이 수정 합니다...

    class CPointXY : virtual public CPointX{

    .....

    // CPointX(a) 생성자는 여기에서 호출되지 않는다.

    CPointXY(int a, int b) : CPointX(a), y(b)

    };

    class CPointXZ : virtual public CPointX{

    .....

    // CPointX(a) 생성자는 여기에서 호출되지 않는다.

    CPointXZ(int a, int c) : CPointX(a),z(c)

    };

    class CPointXYZ : public CPointXY, public CPointXZ{

    ...

    // 할아버지 클래스의 생성자를 직접 호출한다

    // 만약, CPointX(a) 를 명시적으로 호출하는 코드가 없다면 할아버지 클래스인

    // CPointX 클래스에 인자를 가지지 않는 디폴트 생성자가 있어야 합니다...

    CPointXYZ(int a, int b, int c) : CPointX(a), CPointXY(a,b), CPointXZ(a,c), xyz(0)

    ...

    };





    출처 : 

    http://kin.naver.com/qna/detail.nhn?d1id=1&dirId=1040101&docId=76771879&qb=7KaJLCDshpDsnpAg7YG0656Y7Iqk6rCAIOyekOyLoOydmCDrtoDrqqjrs7Tri6Trj4Qg7IOB7JyE7J24IO2VoOyVhOuyhOyngCDtgbTrnpjsiqTsnZgg7IOd7ISx7J6Q66W8IO2YuOy2nCDtlaAg7IiYIOyeiOuLpOuKlCDsoJDsnbTso6AuLi4=&enc=utf8&section=kin&rank=1&search_sort=0&spq=0&pid=R5vBMF5Y7uKsscvCnRhssc--453720&sid=UJDjOsm@kFAAAHDkOiE

    반응형
    반응형

    출처 : http://kin.naver.com/browse/db_detail.php?d1id=1&dir_id=10104&docid=285334

    void 형변환에 관하여 %p


    #include

    int multi[2][4];

    int main()
    {
    printf("\nmulti = %p",(void *)multi); /* ----------- 1
    printf("\nmulti[0] = %p",(void *)&multi[0]); /* ---------- 2
    printf("\nmulti[0] = %p",(void *)multi[0]); /* ---------- 3
    printf("\n&multi[0][0] = %p\n",(void *)&multi[0][0]); /* ---------- 4
    return 0;
    }

    요기에 보면.. void로 형변환 해주자나여.. 이게 맞는 거는 알겠는데, 왜
    void 형변환을 꼭 해줘야 하는지..

    형변환 안하고, 그냥 %u 쓰구 컴파일하면,

    warning: unsigned int format, pointer arg (arg 2) 이런 경고 메시지가
    뜨더라구여..

    void 포인터가 뭔지는 아는데요..

    가령 1번 줄에서, multi 를 (void *)multi 로 형변환해주면, multi가 가지고
    있는 bit(표현이 맞나?)에

    어떤 변화가 생기는건지 궁금하구, 또 왜 그렇게 형변환을 해줘야 하는지요..

    그것은 %p라는 것은 void* 형을 다룹니다.



    [질문자 평]

    사실, 제가 VC++로 컴파일로 하니.. 에러는 발견되지가 않더군요..

    아마 컴파일러랑 라이브러리의 영향을 받는 warning 증상 같습니다...^^;

    그런데, 조금 좋군요..ㅋㅋㅋ;

    %p는 (void*)를 취급하며, 그것의 주소를 화면의 출력을 하는 구조입니다.

    %u는 그것을 unsigned int 정수형으로 출력을 하라는 것이구요..

    실제 구현상에는 돌아갈때는 아무런 변화가 있을수도 있고, 없을 수도 있습니다.

    int 형은 32 bit인데 char형은 8 bit에다 넣을 려면은 어떻게 해야할까요?

    이중에서 해당하는 하위 8 bit를 넣어야 겠죠.. 이런식의 변화는 있을수 있습니다.

    또 실수형을 정수형으로 넣을경우는? 거기의 맞는 특별한 연산을 해줘야 겠죠?

    물론, (void*)는 영향이 없고, 단순히 시작주소만을 가집니다.. 즉, 모형를 모른다는 것입니다.

    모형의 요소 - 할당바이트, 데이터 저장 형식(실수, 정수, 문자..)

    다시 정리하면은 %p는 (void*)로 받도록 약속을 하였습니다. 그래서 형변환을 해줘야 warning을 뜨지않고, 프로그래머가 의도적으로 한것이구나를 컴파일러가 알아서 거기의 맞는 동작을 하겠죠? 그래서 형변환을 하였습니다.^^;


    그래서 어떠한 포인터를 삭제할때

    struct del{

    void operator()(void * ptr){

    delete ptr;

    }

    };

    이 가능하다

    반응형
    반응형

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <iostream>
    #include <Windows.h>
    #include <math.h>
    using namespace std;


    class c{
    public :
    c(){

    cout<<"c 생성자"<<endl;
    }

    ~c(){

    cout<<"c 소멸자"<<endl;
    }
    };


    class a : public c{
    public:

    a(){

    cout<<"a 생성자"<<endl;
    }
    ~a(){
    cout<<"a 소멸자"<<endl;
    }
    };

    class b{
    public:
    a ai;
    b(){
    cout<<"b 생성자"<<endl;
    }
    ~b(){
    cout<<"b 소멸자"<<endl;
    }
    };

    int main(void)
    {
    /*
    a ai; 의 경우
    생성자는 부모부터 실행되지만
    소멸자는 virtual 처럼 자식 부터 실행된다.
    */

    /*
    b bi;
    b 안에 클래스 멤버가 포함되어 있으면 포함된 생성자가 먼저 호출 된다
    */
    return 0 ;
    }

    반응형
    반응형

     rand() 함수는 난수를 발생하는데 srand(시드값)에 의한 시드값 설정이 없다면

    rand() 자체에서 고정된 시드값으로 난수를 발생하게 된다, 즉 시드값 을 따로 설정하지 않으면

    동일한 시드값으로 rand() 가 계산되어지기 때문에 프로그램이 끝나고 다시 실행하면 동일한 난수들이 나열된다

    내부적으로 고정된 씨드값 =c

    rand(); -> // srand(c); c 라는 상수로 srand 함수가 난수 테이블을 설정하면 rand()함수의 한번 콜이 일어날때

    // 난수테이블의 첫번째 숫자부터 리턴 되는 형식, (난수 테이블이 난수 함수일 수 있다.)

    즉 srand( 시드값 ) 난수테이블을 변경할 수 있는 상수 시드값이 아닌 변경되는 값을 시드값으로 준다면

    rand() 는 변경되는 테이블값의 다른 첫 난수 값을 가져올 수 있을 것이다

    변경되는 시드값은 time(NULL) 로 쓸 수도 있으나 이것은 1초 사이클로 +1 씩증가하는 함수이기 때문에

    1초 사이에는 동일한 난수가 발생할 것이다

    계속 적인 변화를 원한다면 GetTickCount() 같은 함수를 쓰면 되겠다.

    반응형

    + Recent posts