객체의 스코프

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

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 구문이다.

서로 연결된 스트림이 있다.

 스트림에서 사용하는 버퍼들이 서로 연결되어 있는 것이 있다. 예를 들어, cin이 수행될 때 cout의 버퍼가 비워진다던지 하면 cin과 cout의 스트림은 서로 연결되었다고 할 수 있다. 파일 ios.h를 살펴보면 tie 메소드가 제공되는 것을 볼 수 있다.이 tie 메소드를 이용하면 어떻게 연결되어 있는지 알 수 있다. 예를 들어, cin과  cout 사이가 연결되어 있는지 체크해 보려면 다음과 같이 실행하도록 한다.

 if(*(cin.tie()) == cout)

 만일 연결되어 있다면 if 구문은 참이 될 것이다. tie 메소드로 연결을 확인할 수도 있지만 직접 연결을 시도할 수도 있다. 예를 들어, 버퍼 스트림을 사용한다면 스트림 속의 버퍼가 차야 연산이 수행될 텐데, 이때 다른 스트림과 연결되어 있다면 해당 스트림이 수행될 때 버퍼가 비워질 것이다.

 그러면 아래의 예제를 보자. 아래는 화면 콜솔을 ofstream 으로 설정을 하였고, cin과 tie 메소드를 이용하여 연결을 맺었다.

 // 콘솔을 사용하기 위해 ofstream 설정
ofstream console(“CON”);
// console과 cin을 연결
cin.tie(&console);

 이 상태에서는 다음과 같은 문장을 실행시키면 console에 문장이 들어갈 것이다. 하지만 아직 console에 있는 문장이 화면에 출력되지는 않는다.

 // 콘솔에 문장을 출력
 console << “콘솔에 문장 입력!”;

 이 상태에서 cin과 관련된 작업이 수행되면 console에 있는 버퍼가 비워지면서 화면에 출력이 이루어진다.

cin.get();
console.close(); 

한 문자씩 버퍼에 출력하는 put 메소드

 출력 스트링 스트림에서도 put 메소드를 이용하여 버퍼에 하나의 문자식 입력할 수 있다. 간단한 예를 보면 다음과 같다.

#include <strstrea>
/* 우분투 10.04, g++-4.3.3 버전에서는 다음의 헤더파일을 사용하도록 한다.
 * #include <iostream>
 * #include <backward/strstream>
*/

using namespace std;

void main(void)
{
  ostrstream intStream;
  char intData = ‘0’;

  // 1~10 까지 숫자와 공백을 버퍼에 출력
  for(int count = 0; count < 10; count++, intData++) {
    intStream.put(intData);
    intStream.put(‘ ‘);
    if(intStream.fail()) {
      cerr << “버퍼로의 출력 에러 발생” << endl;
      return;
    }
  }

  // 마지막에 NULL 문자 입력후 화면에 출력
  intStream << ends;
  cout intStream.rdbuf() << endl;
}

출력 스트림에서 한문자씩 출력하기.

 cin 이 제공하는 메소드 중 하나의 문자를 입력받는 get 메소드가 존재하는 것처럼 cout에는 하나의 문자를 출력하는 put 메소드가 존재한다. put 메소드의 사용에는 제약이 따르게 되는데, 이는 put 메소드에서 사용 가능한 파라미터는 단지 하나의 문자(character)뿐이라는 것이다.

 물론 숫자를 넘길 수도 있는데, 이 또한 내부에서 문자로 변환하여 화면에 출력하게 된다. 예를 들어 다음을 실행시켜 보자.

int letter = 65;
cout.put(letter);

char clet = ‘B’;
cout.put(clet);

 위의 명령을 실행시키면 화면에 A와 B가 출력된다. put 메소드는 하나의 문자만을 입력받고 출력하는 작업이 반복적으로 이루어져야 하는 경우에 유용하게 사용할 수 있을 것이다.