C++에서 기본클래스의 생성자에서는 순수가상함수를 호출할 수 없다!?

다소 황당하기도 하고, 언젠가 책에서 본것같기도 한 내용입니다. 하지만 저는 당연이 되리라 생각했던지라… 왜 않되냐며 약간 열 받기도 하였습니다. 어떤 내용인냐면…

먼저 Base라는 클래스가 있고, 이 클래스는 A라는 순수 가상 함수가 있습니다.

class Base {
public:
	Base() { };

	void TTT() {
		A();
	}

public:
	virtual void A() = 0;
};

보시면 TTT라는 함수에서 A 함수를 호출하고 있구요. 이제 Base를 상속받는 Derv라는 함수를 정의해 봅니다.

class Derv : public Base {
public:
	virtual void A() {
		printf("impl");
	}
};

상속받는 클래스는 기본 클래스의 순수 가상 함수를 구현해야할 책임이 있으므로 A 함수를 구현합니다. 이제 Base 함수를 사용해 보면…

int _tmain(int argc, _TCHAR* argv[])
{
	Base *p = new Derv();

	p->TTT();

	delete p;

	return 0;
}

화면상에 “impl”이라는 문자가 찍힙니다. 잘됩니다. 그렇다면 Base 클래스를 아래처럼 수정합니다.

class Base {
public:
	Base() { A(); };

	void TTT() {
		A();
	}

public:
	virtual void A() = 0;
};

변경된 부분은 Base의 생성자에서 순사 가상 함수를 호출하고 있습니다. 실행해보면… 않됩니다. Base::A(void) 외부 기호를 확인할 수 없다는 경고입니다. 원래 C++ 표준도 않되는 것인지.. 아니면 MS의 C++ 만 않되는 것인지는 모르겠지만, 당연히 될줄알고 사용했던지라 매우 난감했습니다. 아무튼 않되는 것을 알았으니 돌아가야겠습니다.

STL map의 비교함수자 제공하는 코드

필요할때마다 잊는지라, 이곳에 메모해 봅니다.. ^^

#include 


using namespace std;

class Standard {
private:
	int a;
public:
	Standard(int a) {
		this->a = a;
	}

	int Get() {
		return a;
	}
};

class Value {
public:
	Standard *pStandard;
	Value(Standard *pStandard) {
		this->pStandard = pStandard;
	}
};

class CustomCompare {
public:
	bool operator()(Standard* s1, Standard* s2) const {
		return s1->Get() == s2->Get();
	}
};

int main(int argc, _TCHAR* argv[])
{
	map container;

	Standard *pStand1 = new Standard(10);
	Value *pValue1 = new Value(pStand1);
	
	Standard *pStand2 = new Standard(20);
	Value *pValue2 = new Value(pStand2);

	container[pStand1] = pValue1;
	container[pStand2] = pValue2;

	Standard *pStand3 = new Standard(20);

	printf("%d\n", container[pStand3]->pStandard->Get());

	delete pStand3;
	delete pStand1;
	delete pStand2;
	delete pValue1;
	delete pValue2;

	return 0;
}

CString을 WCHAR로 변환

아무리 MultiByteToWideChar를 사용해 변환을 해보아도 않되어서, 결국 찾은 방법입니다. 일단 변환은 유니코드 문자열 프로젝트 환경에서 수행해 확인한 것입니다. MFC 기반입니다.

CString sProgID;

    .
    .
    .

USES_CONVERSION;
WCHAR *wszProgID = T2W(sProgID.GetBuffer());

    .
    .
    .

sProgID.ReleaseBuffer();

정의되지 않은 클래스 맴버 함수 호출하기

당연이 인스턴스로 생성된 클래스의 맴버함수를 호출하기 위해서는 해당 클래스를 정의하고 있는 헤더 파일을 포함해야 하지만, 헤더 파일을 포함하지 않고 클래스의 맴버함수를 호출해야할 경우가 있다. 즉 정의되지 않는 클래스 인스턴스의 맴버 함수를 호출해야한다.

어떻게 할 수 있을까? 해답은 클래스 맴버 함수 포인터를 사용하는 방법이다.

먼저 사용하는 코드부분에서 호출해야하는 클래스를 선언하고 그 클래스에서 사용해야할 맴버 함수의 포인터를 선언한다.

C사용하는클래스의 헤더파일에…

class C사용될클래스;
typedef void (C사용될클래스::*맴버함수)(void);

그리고 C사용하는클래스의 맴버변수로써 C사용될클래스의 인스턴스와 맴버함수에 대한 변수를 추가한다.

private:
    맴버함수 funcPtr;
    C사용될클래스* p사용될클래스인스턴스;

public:
    void Set(맴버함수 funcPtr, C사용될클래스* pInstance) {
        this.funcPtr = funcPtr;
        p사용될클래스인스턴스 = pInstance;
}

이제 실제로 C사용될클래스의 멤버함수를 사용하는 방법은 아래와 같다.

((*p사용될클래스인스턴스).*funcPtr)();

여기서 주목할 것은 앞에서도 언급했지만 어디서도 C사용될클래스에 대한 헤더파일을 포함하지 않았다는 점이다. 즉, C사용하는클래스는 단지 C사용할클래스를 모르며, 단지 C사용할클래스에서 꼭 필요한 것만 알고 있다는 것이다.

꼼꼼한 독자라면 직감했겠지만, C사용하는클래스의 funcPtr과 p사용될클래스인스턴스는 어떤식으로 값을 설정해야하는가라는 문제가 생긴다.

이것은 C사용될클래스에서 C사용할클래스의 Set함수를 호출해주면 된다. 즉 C사용될클래스의 정의부 어디에선가 다음과 같은 형식의 코드(한가지 예일뿐..)를 호출한다.

p사용하는클래스 = new C사용하는클래스();
p사용하는클래스->Set(&C사용될클래스::Function, this);

사실, 이런 기술(정확히, 솔직이 말한다면 편법)이 필요한 이유는 두개의 클래스가 서로 상호참조를 하는 경우이다. 이런 경우는 서로의 헤더파일을 포함해서 쉽게 구현할 수 있는 경우가 대부분이지만, 만약…. 이 두개의 클래스가 서로 완전이 다른 개념의 개발 프로젝트인 경우에는 예외인데, 예를 들어서 하나의 클래스는 ATL 프로젝트에 있고, 다른 하나는 일반적인 Generic C/C++ 프로젝트인 경우, 단순이 헤더파일을 포함할 경우 서로의 개발환경을 이해하지 못하므로 엄청나게 많은 에러 메세지를 쏟아 낼 것이고, 머리속이 하얗게  되는 것을 경험할 것이다. 이 편법을 사용하지 않는 것이 제대로 개발하고 있다는 증거인지라, 사용하지 않기를 바라지만 꼭 필요한 경우라면 요긴할 것으로 판단되어 글로 정리하여 남긴다.