메모리 동적 할당과 메모리 해제

 메모리를 동적으로 할당한 뒤에 메모리를 해제시키지 않으면 생성된 메모리는 계속해서 시스템에 남게 된다. 따라서 해당 메모리의 사용이 끝나면 delete나 free를 통하여 메모리를 해제시켜야 한다. 예를 들어 다음 메소드의 경우 메소드가 실행될 때마다 메모리를 새오 할당하게 된다.

void allocateMem(void)
{
  AClass *aClass = new AClass();
  aClass->aMethod();
}

 메소드의 실행이 끝나도 aClass 객체는 메모리에 남게 되는데, aClass 객체를 접근할 길은 끊어지기 때문에 이는 향후 메모리상에 쓰레기가 된다. 따라서 객체의 해제를 확실하게 하거나, 새롭게 생성한 객체를 리턴하여 생성된 객체에 대해 접근할 수 있는 길이 없어지지 않도록 하는 것이 좋다. 예를 들면 다음과 같다.

AClass* allocateMem(void)
{
  AClass *aClass = new AClass();
  aClass->aMethod();
  return aClass;
}

GCC 컴파일러에서 라이브러리 파일을 사용하는 법

 라이브러리 파일을 이용하여 실행파일을 만들 때 컴파일러의 옵션을 이용하는 방법이 있다. 이때 사용되는 옵션이 ‘-L’과 ‘-l’ 인데, -L 의 경우에는 사용하고자 하는 라이브러리가 포함된 디렉토리 명을 명시하는 옵션이고, -l 은 라이브러리의 이름을 적어주는 옵션이다. 이때 라이브러리의 이름은 lib 라는 말과 확장자 ‘.a’ 를 생략하여 사용할 수 있다. 즉, libMsg.a 의 경우 그냥 Msg 라고 쓰면 된다.

 실제 예를 보자. 임의의 컴파일 ‘% c++ -o useMsg UseMsg.cpp libMsg.a’ 의 명령을 아래와 같이 대체할 수 있다.

 % c++ -o useMsg UseMsg.cpp -L. -lMsg

 이때 만일 라이브러리 파일이 /home/pchero 디렉토리 밑에 있다면 -L 옵션을 다음과 같이 주면된다.

 -L/home/pchero

GCC 컴파일러를 이용한 라이브러리 생성

 유닉스 상에서 GCC 컴파일러를 이용하여 라이브러리를 만들려면 먼저 다음과 같이 컴파일 과정을 통해 오브젝트 파일을 만들도록 한다.

 % C++ -c Message.cpp

 컴파일이 제대로 끝났으면 확장자로 ‘o’를 가진 Message.o 파일이 생성될 것이다. 이번에는 생성된 오브젝트 파일을 이용하여 라이브러리를 만들도록 한다. 이때 사용하는 명령어는 ar로 라이브러리 작성에 사용된다. 아래와 같이 ar 명령어와 옵션 그리고 생성하고자 하는 라이브러리의 이름, 마지막으로 오브젝트 파일들의 이름들을 나열한 후 실행한다.

 % ar crv libMsg.a Message.o

객체의 스코프

 메모리상에 있는 객체에 접근할 길이 없어졌을 때 객체는 생명을 다했다고 말할 수 있다. 접근할 길을 잃은 객체는 어떤 방법으로든 메모리를 실행환경에 되돌려줘야 한다. 객체의 소멸에서 한 가지 주의해야 할 점은 객체는 자기의 스코프가 끝나게 되면 소멸된다는 점이다. 다시 말해, 그 객체가 사용되는 범위를 넘어가게 되면 객체의 사용주기가 끝나게 된다는 것이다. 아래의 예제를 보도록 하자.

class A {};

// A 객체를 사용하는 여러 클래스
class B
{
  void use_A()
  {
    A a;
    a.method();
  }
};

 B 클래스를 보면, A 객체는 A 객체를 포함하고 있는 중과로 혹(use_A())에서만 접근이 가능하다. 따라서 중괄호({})를 벗어나게 되면 이미 A 객체가 소멸(접근불가)되었기 때문에 사용할 수가 없게 된다.

 객체의 스포크를 간단히 정의하면, 객체의 액세스(접근)가 가능한 범위를 의미한다. 따라서 스코프를 벗어나면 접근을 할 수가 없게 된다. 이때 접근할 수가 없다는 것은 사용할 수가 없다는 것을 뜻한다.

 엄밀한 의미의 스코프란 ‘QN(Qualified Name) 없이 접근할 수 있는 영역’을 의미한다. 예를 들어, 객체 A가 temp라는 변수를 가지고 있을 때 스포크 내에선 temp  변수를 바로 사용할 수 있지만, 스코프 밖에선 A.temp 라는 QN을 이용하여 접근을 하게 된다.

 스코프 내에서는 동일한 이름의 변수 – 클래스 포함 – 를 사용할 수 없게 되어있다. 그런데 만일 역할은 다르지만 동일한 이름의 클래스를 가져야 한다면, C++에서는 네임스페이스를 이용하여 이 문제를 해결하고 있으며, 자바에서는 패키지를 이용하고 있다.

헤더파일 속의 #ifndef와 #endif 선언

 헤더파일을 보면 다음과 같이 #ifndef와 #endif가 나온다. 이는 3-1.h 헤더파일을 다른 소스파일이 여러 번 include 했을 때 발생하는 문제를 미연에 방지하기 위해서이다.

#ifndef __3_1_h__
#define __3_1_h__
… … …
#endif /* ifndef __3_1_h__ */

 예를 들어, 3-1.h 헤더파일을 include한 또 다른 헤더파일인 3-2.h 파일이 존재하는데, 3-2.cpp 파일에서 다음과 같이 include 했다고 하자.

#include “3-1.h”
#include “3-2.h”

 이와 같이 했을 때, 3-2.cpp 파일에는 3-1.h 파일이 두번 include 된 것과 마찬가지가 된다. 왜냐하면 3-2.h 파일내부에서 3-1.h 파일에 대한 include가 이루어졌기 때문에 3-2.cpp 파일에는 3-1.h 파일이 두 번 include 된 것과 동일한 것이다. 이럴 경우 많은 에러가 발생한다. 변수 선언이 있다면 동일한 이름의 변수가 두 번 이상 선언된 것이고, 함수 선언이 있다면 동일한 함수가 두 번 이상 선언된 것이기 때문에 에러를 피할 수 없다.

 이때 #ifndef과 #define 그리고 #endif 구문을 사용하면 동일한 헤더파일이 두 번 include 되는 것을 방지할 수 있다. 즉, #define 의 내용이 실행되지 않았으면 #endif 구문까지를 실행하고, #define의 내용이 실행되었으면 이 구문이 실행되지 않는다. 따라서 언제나 한번만 실행이 되도록 만들 수 있는 것이다. 참고로 #ifndef(“If not define”)과 반대로 행동하는 것은 #ifdef 구문이다.