C, C++

[C++][추후 추가해야 함] More Effective C++ Item 26 : 클래스 인스턴스의 개수를 의도대로 제한하는 방법

Binceline 2014. 3. 16. 13:02

객체를 전혀 생성하지 않거나 단 한 개만 생성하는 방법

특정한 클래스의 객체가 생성되지 않도록 막는 방법 중 하나는 생성자를 private으로 선언하는 것이다.

class PrintJob;

class Printer

{

public:

friend Printer& thePrinter();

private:

Printer();

Printer(const Printer& rhs);

};


Printer& thePrinter()

{

static Printer p;

return p;

}


이렇게 하면 하나로 제약을 넣을 수 있다. 언제든지 thePrinter() 함수를 호출하면 된다.

이 코드 설계에 필요한 키 포인트는 세 가지이다.

- Printer클래스의 생성자가 private이다. : 다른 곳에서는 객체 생성 불가

- thePrinter 함수가 friend로 선언되어 있다. : thePrinter에서는 Printer 클래스의 private멤버까지 접근 가능

- thePrinter에서 Printer 객체가 static으로 선언되어 있다.


Printer 객체를 클래스의 정적 멤버로 정의하는 것은 좋지 않다. 그 이유는 객체 초기화 시점과 관련된 것인데, 함수에 선언되었다면 함수가 호출될 시 초기화가 이루어 지는데, 클래스에 선언된 정적 객체는 그 초기화 시점이 정확하게 정의되어 있지 않다. C++은 하나의 컴파일 단위 안에 선언된 정적 객체들에 대해서는 초기화 순서를 보장하지만, 서로 다른 컴파일 단위에 있는 것들의 순서에 대해서는 알 수 없다. 이걸 피해가는 방법은 정적 객체를 함수에 선언하는 것이라고 생각할 수 있다.

그리고 inline을 쓰지 않은 이유가 있다. inline이란 컴파일러에게 그 함수가 호출된 부분 대신 함수 내용을 끼워 넣고 즉시 처리하라고 알려주는 것인데, 비 멤버 함수에 있어서의 inline은 *internal linkage(내부 연결)을 가진다는 뜻이다.

internal linkage을 가지는 함수는 한 프로그램 안에서 중복될 수 있다. 중복된 함수 코드마다 정적 객체가 따로 생성되게 되고, 결론은 한 프로그램에 그 정적 객체가 2개 이상 생성될 수 있다는 것이다. 

즉 비 멤버 함수는 절대로 inline으로 선언하면 안 된다.

*Internal Linkage : 컴파일 단위 사이에 객체나 함수의 이름이 공유되는 방식. 어떤 객체의 이름 혹은 함수의 이름이 주어진 컴파일 단위 안에서만 의미를 가진다는 뜻. 즉 어떤 함수 b가 Internal linkage를 가지면 목적 코드 a에 들어 있는 함수 b와 목적 코드 c에 들어 있는 함수 b는 동일한 코드이지만 별개의 함수로 인식되어 코드가 중복된다.


객체 생성 수를 제한하는 방법은 매우 간단하다. 일정한 개수에 도달하게 되면 예외 발생을 이용하는 것이다.

class Printer 

{

public:

class TooManyObjects{};

Printer();

~Printer();

...

private:

static size_t numObjects;

};


Printer::Printer()

{

if (numObjects >= 1)

{

throw TooManyObjects();

}

++numObjects;

}

Printer::~Printer()

{

--numObjects;

}


이렇게 말이다.

물론 다른 방법도 있을 것이지만 이 방법의 장점은 다음과 같다.

- 직관적이고 단순하다.

- 객체의 개수를 한 개 이외의 다른 개수로 일반화할 수 있다.

반응형