왜 header파일에 class 구현 코딩을 넣는걸까요?

emptynote의 이미지

제가 c++ 왕 초보입니다.

c++ 선배?님들의 훌륭한 소스를 보고자 log4cxx를 소소체 가져와서 보는데,

doxygen으로 log4cxx도 되어 있고 해서 특정 class 구현을 어떻게 해나 궁굼해서

찾아보니 헤더파일에 class가 정의 되어있네요.

log4cxx::helpers::ObjectPtrT

이거 정의가 objectptr.h 파일에 정의 되어 있네요.

objectptr.cpp 소스보니 ObjectPtrT 그림자도 안 보여서

헤더파일들을 찾아 읽어보다 알게된 사실입니다

왜 header파일에 class 구현 코딩을 넣는걸까요?

고수님의 숨은 마음이 궁궁해 지네요.

------------------ objectptr.h 파일을 cpp 돌려 얻은 결과물 ------------------------------
# 1 "objectptr.h"
# 1 ""
# 1 ""
# 1 "objectptr.h"
# 21 "objectptr.h"
# 1 "/usr/include/log4cxx/log4cxx.h" 1 3 4
# 38 "/usr/include/log4cxx/log4cxx.h" 3 4
typedef long long log4cxx_int64_t;

typedef log4cxx_int64_t log4cxx_time_t;
typedef int log4cxx_status_t;
typedef unsigned int log4cxx_uint32_t;
# 22 "objectptr.h" 2
# 36 "objectptr.h"
namespace log4cxx
{
namespace helpers
{
class Class;

class ObjectPtrBase {
public:
ObjectPtrBase();
virtual ~ObjectPtrBase();
static void checkNull(const int& null);
static void* exchange(void** destination, void* newValue);
virtual void* cast(const Class& cls) const = 0;
};

template class ObjectPtrT : public ObjectPtrBase
{
public:
ObjectPtrT(const int& null)
: p(0) {
ObjectPtrBase::checkNull(null);
}

ObjectPtrT()
: p(0) {
}

ObjectPtrT(T * p1)
: p(p1) {
if (this->p != 0)
{
this->p->addRef();
}
}

ObjectPtrT(const ObjectPtrT& p1)
: p(p1.p) {
if (this->p != 0)
{
this->p->addRef();
}
}

ObjectPtrT(const ObjectPtrBase& p1)
: p(reinterpret_cast(p1.cast(T::getStaticClass()))) {
if (this->p != 0) {
this->p->addRef();
}
}

ObjectPtrT(ObjectPtrBase& p1)
: p(reinterpret_cast(p1.cast(T::getStaticClass()))) {
if (this->p != 0) {
this->p->addRef();
}
}

~ObjectPtrT()
{
if (p != 0) {
p->releaseRef();
}
}

ObjectPtrT& operator=(const ObjectPtrT& p1) {
T* newPtr = p1.p;
if (newPtr != 0) {
newPtr->addRef();
}
T* oldPtr = exchange(newPtr);
if (oldPtr != 0) {
oldPtr->releaseRef();
}
return *this;
}

ObjectPtrT& operator=(const int& null)
{

ObjectPtrBase::checkNull(null);
T* oldPtr = exchange(0);
if (oldPtr != 0) {
oldPtr->releaseRef();
}
return *this;
}

ObjectPtrT& operator=(T* p1) {
if (p1 != 0) {
p1->addRef();
}
T* oldPtr = exchange(p1);
if (oldPtr != 0) {
oldPtr->releaseRef();
}
return *this;
}

ObjectPtrT& operator=(ObjectPtrBase& p1) {
T* newPtr = reinterpret_cast(p1.cast(T::getStaticClass()));
return operator=(newPtr);
}

ObjectPtrT& operator=(const ObjectPtrBase& p1) {
T* newPtr = reinterpret_cast(p1.cast(T::getStaticClass()));
return operator=(newPtr);
}

bool operator==(const ObjectPtrT& p1) const { return (this->p == p1.p); }
bool operator!=(const ObjectPtrT& p1) const { return (this->p != p1.p); }
bool operator<(const ObjectPtrT& p1) const { return (this->p < p1.p); }
bool operator==(const T* p1) const { return (this->p == p1); }
bool operator!=(const T* p1) const { return (this->p != p1); }
bool operator<(const T* p1) const { return (this->p < p1); }
T* operator->() const {return p; }
T& operator*() const {return *p; }
operator T*() const {return p; }

private:
T * p;
virtual void* cast(const Class& cls) const {
if (p != 0) {
return const_cast(p->cast(cls));
}
return 0;
}
T* exchange(const T* newValue) {
return static_cast(ObjectPtrBase::exchange(
reinterpret_cast(&p),
const_cast(newValue)));
}

};

}
}

ifree의 이미지

template class 가 개입되면 보통의 first compile, then link 의 방식에 문제가 생기게 때문이죠.
즉, template class 를 사용하는 코드를 보고 컴파일러가 template class 를 구체적인 타입을 사용하는 클라스로 instantiation 을 하게 되는데,
포함된 헤더 파일에 클라스가 구현되어 있지 않으면 instantiation 을 하지 않고 프로토타입만 보고 컴파일하게 됩니다.
따라서 컴파일은 되지만 링크 시에 에러가 나게 되죠.

이를 피하기 위해 헤더파일에 cpp 파일을 include 하거나, 다른 부가적인 조치를 해줘야 되는데,
간단한 경우에는 그냥 헤더 파일에 몽땅 넣어 버리는게 편합니다.

emptynote의 이미지

답변 감사합니다.

c++의 길은 멀고도 험한듯합니다. 그런 깊은 뜻이 있을 줄이야 몰랐습니다.

주관적인 생각이지만 template 은 단순한 생각에서 출발한듯하지만,

다른 일반 문법과 다른 심화 공부가 필요한것 같습니다.

복 받을실거에요~

Scarecrow의 이미지

어렵게 생각할거 없이 template를 헤더에 쓰는 이유는
C에서 #define 매크로 함수를 헤더에 쓰는 이유와 같다고 생각하면 됩니다.