A라는 클래스는 다음과 같다.

class A
{
  A() { ... }
  ~A() { ... }     

private:
  Packet pac;  // Packet 클래스의 소멸자에서는 자원을 해제해 줌


public:
  GetPacket()
  {
       return pac;  
  }
}

pac는 Packet형 함수이고, GetPacket 함수는 그 pac 함수를 return 하는 함수이다.
그리고 다음과 같은 Test case를 작성해 보았다.

A a;
CuAssert( test, "Testing!", a.GetPacket().GetLength() );
CuAssert( test, "Testing!", a.GetPacket().GetIsLoaded() );
...

그런데 포인터 관련 에러가 나는 것이다.

1시간여간의 디버깅 삽질 후에 밝혀 낸 것은..

첫 번째 CuAssert() 를 빠져가가면서 GetPacket() 함수를 통해 얻어진 Packet 객체는 Scope out이 되어 소멸자를 호출하고
두 번째 CuAssert() 를 빠져가가면서도 소멸자를 호출하고..

이런 식으로 소멸자를 계속 호출하게 된다.
소멸자에서는 할당한 자원을 해제하는 코드가 들어 있는데, 이와 같이 실제 객체는 소멸되지 않은 상황에서 소멸자만 줄기차게 호출을 하게 되므로 해제한 자원을 또 해제하려 하여 에러가 발생한 것이다.

이런 상황에서는 다음과 같이 하면 될 것 같다..

...
A a;
Packet pac = a.GetPacket();
CuAssert( test, "Testing!", pac.GetLength() );
CuAssert( test, "Testing!", pac.GetIsLoaded() );
...

이렇게 하여 pac 객체가 scope-out 되지 않도록 하면 될 듯 하다.

사소한 버그지만, 마음이 급해지면 찾아내기 힘든 상황이라서 적어 보았다.

<소스1>
#include <stdio.h>

class CBaseClass
{
public:
void A(void)
{
printf("CBaseClass A()\n");
};

virtual void B() = 0;

void D(void)
{
printf("CBaseClass D()\n");
}

void C(void)
{
printf("CBaseClass C()\n");
this->B();
this->D();
}
};

class CBaseDerived1 : public CBaseClass
{
public:
void A(void)
{
printf("CBaseDerived1 A()\n");
};

void B()
{
printf("CBaseDerived1 B()\n");
};

void D(void)
{
printf("CBaseDerived1 D()\n");
}
};

void main(void)
{
CBaseDerived1 *a1 = new CBaseDerived1;
a1->A();   // 상속된 CBaseDerived1.A() 를 실행
a1->C();  // CBaseClass의 C()를 수행하고,
              // C() 함수 내에서 virtual 함수를 상속한 CBaseDerived1.B()를 수행
              // C() 함수 내에서 CBaseClass의 D()를 수행

delete a1;
}

<결과1 - virtual 함수를 상속한 함수의 실행>
CBaseDerived1 A()
CBaseClass C()
CBaseDerived1 B()
CBaseClass D()

<소스2>
#include <stdio.h>

class CBaseClass
{
public:
void A(void)
{
printf("CBaseClass A()\n");
};

virtual void B(void) { printf("CBaseClass B() - virtual \n"); } ;

void D(void)
{
printf("CBaseClass D()\n");
}

void C(void)
{
printf("CBaseClass C()\n");
this->B();
this->D();
}
};

class CBaseDerived1 : public CBaseClass
{
public:
void A(void)
{
printf("CBaseDerived1 A()\n");
};

void B()
{
//printf("CBaseDerived1 B()\n");
CBaseClass::B();         // 원래 Class의 B() 함수를 수행하도록 함
};

void D(void)
{
printf("CBaseDerived1 D()\n");
}
};

void main(void)
{
CBaseDerived1 *a1 = new CBaseDerived1;
a1->A();
a1->C();

delete a1;
}

<결과 2 - virtual 함수에 정의되어 있는 코드 실행>
CBaseDerived1 A()
CBaseClass C()
CBaseClass B() - virtual    // CBaseDerived1.B() 함수를 실행하였으나, 그 함수가 CBaseClass::B();를 실행하도록 함
CBaseClass D()

<요약>
1. virtual 로 정의된 함수를 실행할 경우, 무조건 하위 class에 재정의 된 함수를 실행하게 됨
2. 하위 class에 재정의 된 함수에서, 상위 class의 virtual 함수를 실행할 수도 있음
3. 만약, CBaseDerived1.B() 함수를 주석처리하면, 어쩔 수 없이(?) virtual임에도 불구하고 CBassClass.B() 함수를 실행함


기본적인 개념은..
메모리 할당 시에 할당된 주소를 linked list에 저장하고, 해제 시 linked list를 삭제하고..
프로그램을 실행하면서 이 linked list를 trace 하여 해제되지 않고 남은 메모리가 있는지를 파악하는 C 코드이다.

어렵지는 않지만 꽤 유용한 것 같아서 링크를 걸어 놓는다.
http://www.ibm.com/developerworks/kr/library/opendw/20061219/

Big/Little Endian

Generic C 2006/10/18 14:20

매번 헷갈리는 개념이 endian 개념이다.

간단하게 말해 big endian은 그냥 쓰는 거고, Little endian은 뒤집어서 쓰는거다..
예를 들면, "4F52"를 저장할 때에, atomic 단위가 1 byte라면
- 4F52(4F - 1000번지, 52 - 1001번지) 로 저장되면 big endian
- 524F(52 - 1000번지, 4F - 1001번지)로 저장되면 little endian
인 것이다.

Endian 여부를 파악할 수 있는 함수를 wikipedia에서 찾아왔다.

#define LITTLE_ENDIAN   0
#define BIG_ENDIAN      1

int machineEndianness()
{
  long int i = 1;
  const char *p = (const char *) &i;
  if (p[0] == 1)  // Lowest address contains the least significant byte
     return LITTLE_ENDIAN;
  else
     return BIG_ENDIAN;
}

long int 형은 4 byte이고, 0x00000001 일 것이므로
big endian이라면 첫 번째 바이트에 0x00이
little endian이라면 첫 번째 바이트에 0x01이 저장될 것이다.

이와 같이 하면, endian 여부를 파악할 수 있을것이다.

 

1) #

- # 뒤의 첫 번째 argument가 "" 로 둘러 쌓인 string이 되도록 함

- Ex)

#define to_string( s ) # s

cout << to_string( Hello World! ) << endl; // ==> cout << "Hello World!" << endl;


2) ##
- ## 앞의 것과 ## 뒤의 것을 연결(concatenate) 한다.

- Ex)

#define concatenate( x, y ) x ## y
int xy = 10;

cout << concatenate( x, y ) << endl; // ==> cout << xy << endl;

http://blog.naver.com/choies1?Redirect=Log&logNo=120014759691

그동한 궁금해 왔던 것이었는데..
괜찮은 강좌를 http://www.winapi.co.kr에서 보았다..

http://www.winapi.co.kr/ApiBoard/content.php?table=tbltip&pk=1695

PASCAL, WINAPI 등의 매크로 뿐 아니라..
__stdcall과 __cdecl에 관련된 설명이 잘 되어 있는 강좌이다.

결론만 말하면...

PASCAL이건, WINAPI건 CALLBACK이건 APIENTRY이건..
모두 __stdcall 이라는 것-_-;;

젝일;;;

reference parameter

Generic C 2006/02/02 11:46
파라메타에서의
CArchive& ar 과
CArchive* ar 은 엄연히 사용법이 다르다.

CArchive& ar 은 refernece이고, CArchive* ar 은 pointer이니

reference인 경우에는 ar.IsStoring() 이렇게 사용하고..
pointer인 경우에는 ar->IsStoring() 이렇게 사용한다.
(데브피아 QnA 게시판 참고)

그리고...

http://www.cs.wisc.edu/~hasti/cs368/CppTutorial/NOTES/PARAMS.html#ref 를 참조해 보면, reference parameter 관련 예제가 나와 있다.

[code]
void f(int &n) {
n++;
}

int main() {
int x = 2;
f(x);
cout << x;
}

[output]
3

즉, reference parameter는 formal parameter의 어떤 변화도 actual parameter에 영향을 미치게 된다.

http://www.cs.rit.edu/~afb/20012/cs1/slides/javatypes-03.html

출처: MSDN
ms-help://MS.MSDNQTR.2004JAN.1033/vclang/html/_pluslang_Formal_and_Actual_Arguments.htm

Calling programs pass information to called functions in "actual arguments." The called functions access the information using corresponding "formal arguments."

When a function is called, the following tasks are performed:

All actual arguments (those supplied by the caller) are evaluated. There is no implied order in which these arguments are evaluated, but all arguments are evaluated and all side effects completed prior to entry to the function.
Each formal argument is initialized with its corresponding actual argument in the expression list. (A formal argument is an argument that is declared in the function header and used in the body of a function.) Conversions are done as if by initialization — both standard and user-defined conversions are performed in converting an actual argument to the correct type. The initialization performed is illustrated conceptually by the following code:
void Func( int i ); // Function prototype
...
Func( 7 ); // Execute function call
The conceptual initializations prior to the call are:

int Temp_i = 7;
Func( Temp_i );
Note that the initialization is performed as if using the equal-sign syntax instead of the parentheses syntax. A copy of i is made prior to passing the value to the function. (For more information, see Initializers and Conversions, Initialization Using Special Member Functions, and Explicit Initialization.

Therefore, if the function prototype (declaration) calls for an argument of type long, and if the calling program supplies an actual argument of type int, the actual argument is promoted using a standard type conversion to type long (see Standard Conversions).

It is an error to supply an actual argument for which there is no standard or user-defined conversion to the type of the formal argument.

For actual arguments of class type, the formal argument is initialized by calling the class's constructor. (See Constructors for more about these special class member functions.)

The function call is executed.
The following program fragment demonstrates a function call:

// expre_Formal_and_Actual_Arguments.cpp
void func( long param1, double param2 );

int main()
{
long i = 1;
double j = 2;

// Call func with actual arguments i and j.
func( i, j );
}

// Define func with formal parameters param1 and param2.
void func( long param1, double param2 )
{
}
When func is called from main, the formal parameter param1 is initialized with the value of i (i is converted to type long to correspond to the correct type using a standard conversion), and the formal parameter param2 is initialized with the value of j (j is converted to type double using a standard conversion).