VS 6.0등에서 만든 프로젝트를 VS 2005 에서 빌드시 나타나는 링크 에러

원인  VS 2005 에서는 싱글 쓰레드용 라이브러리(libc, libcd)가 더이상 지원되지 않음.

런타임 라이브러리 종류별 스레드 타입의 예

LIBCI.lib : Single-thread, Static Link /ML
LIBCIMT.lib : Multithreaded, Static Lik /MT
MSVCIRT.lib : Multithreaded, dynamic link /MD

해결책

프로젝트 속성 -> 구성 속성 -> 링커 -> 입력 -> 특정 라이브러리 무시에 libcd.lib를 포함시키면 해결된다. 

프렌드 개념 잡기!!!

 

1. 프렌드 지정은 단방향이다.

 

내가 소영이한테 친구를 지정했어!!..
소영이는 나를 친구로 지정안했어!!..
난 소영이를 친구로 생각하니까 소영이가 요청하면 다 받아줘!!..
소영이는 나를 친구로 생각안하니까 내가 요청하는걸 안받아줘!..
이렇게 비참하게 생각하면 딱 와닿음.

 

2. 프렌드 지정은 전이되지 않는다. (EX 친구의 친구따위는 없다는거다)

3. 복수의 대상에 대해 동시에 프렌드 지정이 가능하다.

 EX ) A는 B랑 C랑 친구하고 싶어요!! +_+ 그럼

         B,C야 친구하자 는 안되고

         B야 친구하자;

         C야 친구하자;

라고 해야된다.

 

4. 프렌드관계는 상속되지 않는다. (엄마의 친구가 내 친구가 될 순 없자나? -_-..)

하지만, 여기서 예외가 있다.

나에겐 유니크한 부분과 엄마에게 유전받은 똑같은 부분이 있다.

그런 똑같은 부분에선 엄마의 친구와 공감대를 찾을 수 있는것처럼, 사용할수 있지만

나의 유니크한 부분은 안된다는거지.

 

 

 

아래의 예제를 보자.

 

#include <Turboc.h>

 

class Time;

class Date

{

private:

     int year,month,day;

public:

     Date(int y,int m,int d) { year=y;month=m;day=d; }

     void OutToday(Time &t);

};

 

class Time

{

     friend void Date::OutToday(Time &t);

        (time t) < 값 변경 불가능, 복사만.

        (tima &t) 만약 size가 200byte면 그 200byte와 값을 그대로 전달받음

                         받은 값을 수정 가능

        (time *t) 하면 주소값만 가지고 오는것이므로 4byte가 되서 size가 적절해짐

                        const time *t를 한다면? 참조만 가능해진다.

         그러니까 값을 변경해야된다거나 그대로 전달받아야 할때는 &가 제일 좋다.

private:

     int hour,min,sec;

public:

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

};

 

void Date::OutToday(Time &t)

{

     printf("오늘은 %d년 %d월 %d일이며 지금 시간은 %d:%d:%d입니다.\n",

          year,month,day,t.hour,t.min,t.sec);

}

 

void main()

{

     Date D(2005,01,02);

     Time T(12,34,56);

     D.OutToday(T);

}

 
 
 
 
 
프렌드 멤버 함수를 알아보자!!
 

class Some

{

     ....

     friend void Any::func(Some &S);

};

 

이렇게 하면 Any 클레스의 func멤버만 Some에 엑세스가 가능해지지.
 

프렌드 멤버 함수를 알아보자!!endMem

#include <Turboc.h>

 

class Time;

class Date

{

private:

     int year,month,day;

public:

     Date(int y,int m,int d) { year=y;month=m;day=d; }

     void OutToday(Time &t);

};

 

class Time

{

     friend void Date::OutToday(Time &t); // Date의 OutToday만 Time &t와 친구먹는다.

private:

     int hour,min,sec;

public:

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

};

 

void Date::OutToday(Time &t)  // Date의 outToday에서 Time &t를 참조한다.

{

     printf("오늘은 %d년 %d월 %d일이며 지금 시간은 %d:%d:%d입니다.\n",

          year,month,day,t.hour,t.min,t.sec);

}

 

void main()

{

     Date D(2005,01,02);

     Time T(12,34,56);

     D.OutToday(T);   // Data에 OutToady에서만 Time클레스를 참조할수있다

}

 

기본적인 파일 관리함수

 

int _access( const char *path, int mode );

 

파일을 엑세스 하기 전에 파일이 실제로 존재하는지 확인하는 함수입니다.

 

path : 조사할 파일의 경로

 

mode: 조사할 상태 지정 ( 0 은 존재, 2 는 쓰기, 4 는 읽기 )

 

return : 요청한 허가 상태를 가지면 0을 리턴, 그렇지 않으면 -1을 리턴

 

 

 

int remove( const char *path );

int _unlink( const char *filename );

 

위의 두 함수는 파일을 삭제 합니다.

 

path : 삭제하고자 하는 파일의 경로만 지정

 

 

 

int rename( const char *oldname, const char *newname );

 

이 함수는 파일의 이름을 변경합니다.

 

oldname : 변경하고자 하는 파일의 이름

 

newname : 새로 설정할 파일의 이름

 

 

 

int _chmod( const char *filename, int pmode );

 

파일의 속성을 변경 합니다.

 

pmode : _S_IWRITE  ,  _S_IREAD  둘중 하나를 주거나 둘다 줄수도 있음

 

 

파일검색 함수

 

long _findfirst( char *filespec, struct _finddata_t *fileinfo );

 

위의 함수로 첫번째 파일 검색 합니다.

 

int _findnext( long handle, struct _finddata_t *fileinfo );

 

위의 함수로 조건이 일치하는 다음 파일을 찾을수 있습니다.

 

int _findclose( long handle );

 

위의 함수로 모든 검색이 완료된 후 검색 핸들을 닫아 줍니다.

 

 

 

디렉토리 관리 함수

 

int _chdir( const char *dirname );

 

현제 작업중인 디렉토리의 경로 변경

 

char *_getcwd( char *buffer, int maxlen );

 

현재 작업 디렉토리를 조사

 

int _mkdir( const char *dirname );

 

디렉토리 생성

 

int _rmdir( const char *dirname );

 

디렉토리 제거

 

void _splitpath( const char *path, char *drive, char *dir, char *fname, char *ext );

 

경로 관리 함수로서  4개의 구분자로 분리한다.

 

void _makepath( char *path, const char *drive, const char *dir, const char *fname

                              , const char *ext );

 

경로 관리 함수로서 분리되어 있는 구분자를 합친다.

 

 

 

 

디스크 관리 함수

 

int _getdrive( void );

 

int _chdrive( int drive );

 

위의 두 함수는 작업 드라이브를 조사하거나 변경한다.

ShellExecute(NULL, "open", http://naver.com, "", "", SW_SHOW );

내장 되어 있는 기본 웹 브라우저를 뜨우는 방법

ShellExecute(NULL, "open",

"C:\\Program Files\\InternetExplorer\\IExplorer.exe", 

"http://naver.com",
"",
SW_SHOW
);

인터넷 익스플로러를 지정하여 띄우는 방법


플라이웨이트 패턴 (Fly Weight)

동일한 종류의 객체 끼리는 중복된 정보를 가지게 될 가능성이 높습니다. 그래서 같은 종류의 객체끼리 중복되는 정보를 공유하는 것이 메모리 관리 측면이나, 중복을 제거하는 측면이나 이롭습니다.

중복되는 정보를 공유 하는 방법을 플라이 웨이트 패턴이라고 합니다.



class CImageData

{

        int m_nIdx;

        int m_nWidthm_nHeight;

        char *m_pData;

public:

        char *GetData(){return m_pData;}

        int GetWidth(){return m_nWidth;}

        int GetHeight(){return m_nHeight;}

};

 

class CImagePool //CImageData의 객체 소유권은 CImagePool에 있.

{

        std::map<intCImageData *>m_mapImageData//여러이미지데이터를담고있는map이다.

public:

        CImageData *GetImageData(int a_nIdx); //m_mapImageData에서해당idx를가진CImageData*를리턴한다.

};

 

class CMenu

{

        CImageData *m_pcImageData;

public:

        CMenu();

        ~CMenu(){} //여기서m_pcImageDatadelete 하면큰일난다.

 

        void SetImageData(CImageData *a_pcImageData){m_pcImageData = a_pcImageData;}

        void Draw()

        {

               //m_pcImageData의픽셀데이터를통해서화면에메뉴이미지데이터를그린다.

        }

};

 
팝업 메뉴마다 공유 될 수 있는 이미지 데이터를 메뉴 객체마다 따로 들고 있는 것은 메모리상의 중복이 생깁니다. 같은 이미지 데이터를 사용하는 메뉴 객체가 CImageData의 정보를 링크해 사용 한다면CImageData의 관리도 한 곳에서 이뤄지는 
잇점과, 위에서 말한 메모리 상의 중복도 제거할 수 있게 되는 것입니다.

 

 

class CQuestInfo

{

        int m_nQuestNo//퀘스트식별번호

        char m_szTItle[32]; //퀘스트이름

        char m_szDesc[512]; //퀘스트설명

        int m_nQuestMissionNo//퀘스트목표에할당된번호

        int m_nCompensateNo//퀘스트보상번호

        int m_nRequireProgressQuantity//목표 달성을 위해 필요한 진행량
        //
기타등등..

};

 

class CQuestInfoPool //CQuestInfo의객체소유권은CQuestInfoPool에게있다

{

        std::map<intCQuestInfo *>m_mapQuestInfo//CQuestInfo 데이터를담고있는map이다.

public:

        CQuestInfo *GetQuestInfo(int a_nQuestNo); //m_mapQuestInfo에서해당QuestNo를가진CQuestInfo*를리턴한다.

};

 

class CQuestData

{

        int m_nIdx//퀘스트고유번호

        int m_nProgressQuantity//진행량

        CQuestInfo *m_pcQuestInfo;

public:

        CQuestData();

        ~CQuestData(){} //여기서m_pcQuestInfodelete 하면큰일난다.

 

        void SetQuestInfo(CQuestInfo *a_pcQuestInfo){m_pcQuestInfo =a_pcQuestInfo;}

};

 
퀘스트의 경우에도 퀘스트의 종류에 따라 퀘스트 이름, 설명, 보상번호, 목표 번호, 목표 달성량 같은 것은 고정된 정보이고, 퀘스트의 고유 번호, 진행량과 같은 정보는 현재 진행중인 퀘스트마다 다르다. 

이렇게 고정된 정보와 변할 수 있는 정보를 나누고, 고정된 정보를 묶고 링크해서 사용함으로써 플라이 웨이트 패턴을 구현할 수 있습니다.

커맨드 패턴 (Command)

객체의 내부 동작 하나 하나보다, 어떤 동작을 한다는 것 자체가 중요할 때가 있습니다. 캐릭터에게 행동을 시킬 때, 어떤 행동을 하는지는 중요하지 않고, 행동을 한다는 그 자체만 중요할 때가 바로 그렇습니다.

이렇게 행동을 일반화하는 것을 커맨드 패턴이라고 부르고, 캡슐화의 구현이라고 보셔도 좋습니다.

다만 일반 캡슐화와는 조금 다른것이, 커맨드 패턴은 한 클래스당 한가지 일만 시키는 경우가 많다는 것입니다. 동작 하나를 하나의 클래스로 관리함으로써, 다양한 동작을 관리하기 쉽게 하겠다는 것이지요.

class ICommand

{

public:

        virtual void Act() = 0;

};

 

class CCharacterParent : public ICommand

{

protected:

        virtual CCharacterParent * SearchTarget() = 0; //타겟찾기

        virtual void Act(CCharacterParent *a_pcCharaterParent) = 0; //공격

public:

        virtual void Act(){

               Act(SearchTarget());

        }

};

 

class CMonster : public CCharacterParent

{

        virtual CCharacterParent * SearchTarget()

        {

               //사정거리내의캐릭터를찾는다.

        }

 

        virtual void Act(CCharacterParent *a_pcCharaterParent)

        {

               Attack(a_pcCharaterParent);

        }

 

        void Attack(CCharacterParent *a_pcCharaterParent)

        {

               //파라미터로넘어온캐릭터를공격한다.

        }

};

 

class CPet : public CCharacterParent

{

        virtual CCharacterParent * SearchTarget()

        {

               //가장약한아군캐릭터를찾는다

        }

 

        virtual void Act(CCharacterParent *a_pcCharaterParent)

        {

               Heal(a_pcCharaterParent);

        }

 

        void Heal(CCharacterParent *a_pcCharaterParent)

        {

               //파라미터로넘어온캐릭터를회복시킨다

        }

};

 

void Act(ICommand *a_piCommand)

{

        a_piCommand->Act();

}

외부에서는 캐릭터가 공격을 하던, 회복을 시켜주던 상관없습니다. 해당 캐릭터가 행동한다는 그 자체가 중요하죠.

이렇듯 객체의 내부 행동을 숨기고, 외부 인터페이스를 일반화하는 것을 커맨드 패턴이라 합니다.

 

원작자 엘키

http://elky.tistory.com/124

프록시 패턴 (Proxy)

어떤 객체가 수행하는 기능을 그대로 수행하면서, 부가적인 기능을 수행하거나 기존 역할을 대행하기 위해 새 클래스를 정의하고, 새로 정의된 클래스를 통해서 외부와 통신하는 것을 프록시 패턴이라고 합니다.

class IObject

{

public:

        virtual void Use() = 0;

};

 

class CQuickSlot

{

        IObject *m_piObejct;

public:

        bool isExist(){

               return m_piObejct != NULL ? true : false;

        }

 

        void Use(){

               if(m_piObejct)

                       m_piObejct->Use();

        }

};

 

class CQuickSlotProxy

{

        CQuickSlot *m_pcQuickSlot;

public:

        CQuickSlotProxy() : m_pcQuickSlot(NULL)

        {

 

        }

 

        ~CQuickSlotProxy()

        {

               if(m_pcQuickSlot)

                       delete m_pcQuickSlot;

        }

 

        bool isExist(){

               if(m_pcQuickSlot)

                       return m_pcQuickSlot->isExist();

 

               return false;

        }

 

        bool Use(){

               if(m_pcQuickSlot){

                       m_pcQuickSlot->Use();

                       return true;

               }

 

               //bool형으로바뀌면서실제사용했는지여부를알수있게되었다.

               return false;

        }

};

void형에서 bool형으로 바뀐 use함수에서는 아이템 사용을 성공 여부를 알릴 수 있도록 기능이 확장되었으며, CQuickSlot 클래스에 변화를 가하지 않고도 기능 확장이 가능해졌습니다. 예를 들어, CQuickSlot의 Use함수 호출 횟수 계산 기능을 추가한다 했을 때에도 CQuickSlot 클래스를 변경할 필요 없게 되는 잇점이 생기게 되는 것이죠.

 

원작자 엘키

http://elky.tistory.com/124

Microsoft C/C++ 컴파일러를 이용하여 명령행(command-line)에서 DLL을 컴파일-링크하는 과정을 설명하려고 한다.

 

다음의 소스 파일들이 있다.

 

dlllib.h: dlllib.c와 dlltest.c에서 include하는 헤더

dlllib.c: DLL 소스 파일

dlltest.c: 애플리케이션 소스 파일

 

1. DLL 만들기

 

cl /c dlllib.c

link dlllib.obj /dll

 

 

2. 실행 파일 만들기

 

cl dlltest.c dlllib.lib

 

 

3. 샘플 코드

 

3-1. dlllib.h

/*
dlllib.h: dlllib DLL 관련 헤더

dlllib.c와 dlltest.c에서 include해서 사용한다.
*/
#ifdef __cplusplus
#define EXPORT extern "C" __declspec(dllexport)
#else
#define EXPORT __declspec(dllexport)
#endif

 

EXPORT void print();

 

 

3-2. dlllib.c

/*
dlllib.c: dlllib.dll을 만들기 위한 소스 코드

컴파일: cl /c dlllib.c
링크: link dlllib.obj /dll
*/
#include <windows.h>
#include <stdio.h>
#include "dlllib.h"

 

int WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, PVOID pvReserved)
{
    return TRUE;
}

 

void print()
{
    printf("print()\n");
}

 

 

3-3. dlltest.c

/*
dlltest.c: dlltest.exe를 만들기 위한 소스 코드

컴파일: cl dlltest.c dlllib.lib
*/
#include <windows.h>
#include "dlllib.h"

 

int main()
{
    print();
}

  Memory leakage... C++, C를 사용하는 사람들은 늘 자주자주 보는 문제입니다. 사실 분명히

내가 짠 코드는 이런 누수현상이 없다라구 하구 또 많은 Tool들을 통해서 찾는다고 하지만 사실 해법이 마땅히 없는 것은 사실입니다.

 

   최근에는 DevPartner에서 코드오류를 낸다는 부분 때문에 delete하는 부분을 지웠다가 옴팡지게 Memory leakage의 오명을 뒤집어 쓰고 결국 Team전체에 케익을 사는 엄청난 댓가를 치루고 말았습니다. 결국 DevPartner같은 Tool들도 못믿는다는 이야기입니다. 게다가 이런 Tool들은 무지하니 많은 Message를 내보내기는 합니다만 궁극적으로 중요한 정보를 찾을 수 없게 할 경우도 있습니다.

 

  http://www.codeproject.com/cpp/MemFailTest.asp에 보면 이것들을 매우 저렴하게(?)잘 해결할 수 있는 방법이 나옵니다. 어떻게 하는 것일까요?

 

 우선 우리가 만드는 new, malloc같은 것들이 잡는 것은 전체 메모리 영역에서 만드는 것이 아니라 heap이라는 영역에서 메모리를 잡는 것입니다. 일반적으로 Win32 system은 4G memory를 잡고 있습니다. 2G는 OS가 미리 점유하고 있고 나머지 2G를 사용하는 것입니다. 이 안에 사용자가 잡았다가 날렸다가 하는 영역이 바로 heap입니다.

 

 이 영역들을 분석해주는 함수들을 이용하면 됩니다. 단 아래 소개하는 Function들은 Windows에 해당하는 이야기입니다. UNIX에서는 다른 것으로 알고 있습니다.

 

 아래 예제로 설명해 드리겠습니다.

 

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

#include <stdio.h>
#include <string.h>
#include <crtdbg.h>
#ifndef _CRTBLD
#define _CRTBLD
#include <dbgint.h>
#endif  //꼭꼭 위의 헤더파이들을 추가해 주세요. 

int main(void)
{
   _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
   _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDOUT );


   _CrtMemState memstate1, memstate2, memstate3 ; // 메모리 상태를 알려주는 구조체들입니다. 
   _CrtMemCheckpoint(&memstate1) ; // 메모리 상태를  memstate1에 기록합니다. 


    int *x = new int(1177) ;// allocated
    char *f = new char[50] ; // allocated
    strcpy(f, "Hi Naren") ;
    delete x ; // freed, 
    _CrtMemCheckpoint(&memstate2) ; // 메모리 상태를  memstate2에 기록합니다.


    //두개의 Memory 상태 구조체를 비교합니다. 현재 char* f block을 지우지 않았습니다.

   //그러므로 이것 떄문에 debug heap에 의해 error를 잡아내야 합니다.

   if(_CrtMemDifference(&memstate3, &memstate1, &memstate2))  //문제가 있으면 true를 return 합니다. 
   {    
      printf("\nOOps! Memory leak detected\n") ;
      _CrtDumpMemoryLeaks() ; 
      //대신에 _CrtMemDumpAllObjectsSince()를 이용할 수도 있습니다.

   }
   else
      printf("\nNo memory leaks") ;
      return 0 ;
}

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

 

 

위의 예제를 돌리면 Visual Studio Output 윈도우에 아래처럼 뜹니다.

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

OOps! Memory leak detected
Detected memory leaks!
Dumping objects ->
{42} normal block at 0x002F07E0, 50 bytes long.
Data: <Hi Naren        > 48 69 20 4E 61 72 65 6E 00 CD CD CD CD CD CD CD
Object dump complete.

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

 

위의 data값들을 가지고 찾아보면 됩니다. 한데 맨날 이렇게 어렵게어렵게 해야 될까요?

아래 코드들은 위의 방법들을 또 잘 정리해놓은 것들입니다. 더 자세하게 해서 Blog에 올려놓을께요.

 

http://www.codeproject.com/tools/visualleakdetector.asp

http://www.codeproject.com/cpp/MemLeakDetect.asp

http://www.codeproject.com/debug/Memory_leak_finder.asp

첨부파일 내용

 

 

설치 방법

 

1. 라이브러리 파일(vld.lib, vldmt.lib, vldmtdll.lib)을

C:\Program Files\Microsoft Visual Studio\VC98\Lib 에 복사한다.

 

2. 헤더 파일(vld.h, vldapi.h)을

C:\Program Files\Microsoft Visual Studio\VC98\Include 에 복사한다.

 

3. 프로그램이 시작하는 소스 파일 (App.h나 main함수가 있는 파일)에 vld.h 파일을 포함한다.

#include "vld.h"   을 최상위 라인에 적는다. 단, stdafx.h 파일을 포함할 경우, 그 다음 라인에 적는다.

 

4. 만약 운영 프로그램이 Windows2000 또는 이하 버전이라면 dbghelp.dll 파일을 프로그램의 Debug 폴더에 복사한다. 

 

5. 프로그램을 디버그 버전으로 빌드한다.

 

* 유의 사항 *

 

1. 사용자의 프로그램 경로에 한글이 있을경우 추적경로가 잘려서 제대로 나오지 않은 경우도 있음.

  ==> 이럴경우 폴더나 프로그램 이름을 영문으로 수정해야 함.

 

2. static, extern 선언 변수와 같이 프로그램이 종료되기 직전에 메모리를 해제하는 변수는 메모리릭으로 보여지는 경우도 있다.

 ==> vld가 실행되는 시점이 변수 해제 전이기 때문이다. app 파일에 최상위 라인에 적어도 해결이 안될 경우가 있으므로, 추적라인의 메모리 해제 부분이 있을 경우 브레이크를 걸어서 프로그램이 종료되고 들어오는 경우 그냥 넘어가도 될듯하다..;

+ Recent posts