공유 라이브러리에는 순수 가상이 할 수없는 pimpl 관용구가 깔끔하게 우회하는 매우 실제적인 문제가 있습니다. 클래스 사용자가 코드를 다시 컴파일하도록 강요하지 않고는 클래스의 데이터 멤버를 안전하게 수정 / 제거 할 수 없습니다. 어떤 상황에서는 받아 들일 수 있지만 예를 들어 시스템 라이브러리에는 허용되지 않습니다.
문제를 자세히 설명하려면 공유 라이브러리 / 헤더에서 다음 코드를 고려하십시오.
// header
struct A
{
public:
A();
// more public interface, some of which uses the int below
private:
int a;
};
// library
A::A()
: a(0)
{}
컴파일러는 A 객체에 대한 포인터에서 인 것으로 알고있는 A 객체에 대한 특정 오프셋 (이 경우 유일한 멤버이므로 0 일 가능성이 있음)이되도록 초기화 할 정수의 주소를 계산하는 공유 라이브러리의 코드를 내 보냅니다 this
.
코드의 사용자 측에서 a new A
는 먼저 sizeof(A)
메모리 바이트를 할당 한 다음 해당 메모리에 대한 포인터를 A::A()
생성자에 this
.
라이브러리의 이후 개정판에서 정수를 삭제하거나, 더 크게, 더 작게 만들거나, 멤버를 추가하기로 결정하면 사용자의 코드가 할당하는 메모리 양과 생성자 코드가 예상하는 오프셋 사이에 불일치가 발생합니다. 운이 좋으면 충돌이 발생할 가능성이 있습니다. 운이 좋지 않으면 소프트웨어가 이상하게 작동합니다.
공유 라이브러리에서 메모리 할당 및 생성자 호출이 발생하므로 pimpl'ing을 통해 내부 클래스에 데이터 멤버를 안전하게 추가하고 제거 할 수 있습니다.
// header
struct A
{
public:
A();
// more public interface, all of which delegates to the impl
private:
void * impl;
};
// library
A::A()
: impl(new A_impl())
{}
지금해야 할 일은 구현 객체에 대한 포인터 이외의 데이터 멤버가없는 공용 인터페이스를 유지하는 것뿐입니다.이 클래스의 오류로부터 안전합니다.
편집 : 여기서 생성자에 대해 이야기하는 유일한 이유는 더 많은 코드를 제공하고 싶지 않았기 때문일 수 있습니다. 동일한 인수가 데이터 멤버에 액세스하는 모든 함수에 적용됩니다.