2011-01-24 220 views
0

我有一個C++庫我試圖寫一個問題。這是通常的設置,一個cpp文件,一個頭文件。我想讓頭文件只公開要使用的部分(例如我有一個抽象基類,我不想在頭文件中)。到目前爲止,我只是用一個單一的文件工作(我想這應該沒有什麼區別,因爲包括預處理器,不計較什麼都做)。C++類的聲明和命名空間

你會注意到,「頭文件」分佈在兩個點,前後頭實現文件之後。

#include <stdio.h> 

// lib.h 
namespace foo { 
    template <class T> class A; 
} 

// lib.cpp 
namespace foo { 
    template <class T> class A { 
     private: 
     T i; 
     public: 
     A(T i) { 
      this->i = i; 
     } 

     T returnT() { 
      return i; 
     } 
    }; 
}; 

// lib.h 
namespace foo { 
    template <class T> T A<T>::returnT(); 
} 

// foo.cpp 
void main() { 
    foo::A<int> a = foo::A<int>(42); 
    printf("a = %d",a.returnT()); 
} 

因此,很自然,我想我的頭文件僅包含

namespace foo { 
    template <class T> class A; 
    template <class T> T A<T>::returnT(); 
} 

但我的編譯器不喜歡這個(它抱怨說returnT不是foo::A<T>一員。我之所以不希望將類聲明本身放在標題中,那麼它會(根據我的理解)包含所有隱私和類似的東西,我想隱藏它們。

也許這只是我,但下面的頭文件似乎「不好」,至少作爲一個「接口規範」。它暴露了一些A的內部結構,其下的lib的用戶不需要知道的。

// lib.h 
namespace foo { 
    template <class T> class A { 
     private: 
     int i; 
     public: 
     A(T); 
     T returnT(); 
    }; 
} 

// lib.cpp 
namespace foo { 
    template <class T> A<T>::A(T i) { 
     this->i = i; 
    } 
    template <class T> T A<T>::returnT() { 
     return i; 
    } 
}; 

這是公認的做法嗎?如果可能的話,我想要一個更抽象的頭文件。

+0

是整個代碼第一個塊的頭文件的代碼?我很困惑,因爲有兩個標有「lib.h」的塊,但是那時你有一個`main`函數... – 2011-01-24 18:36:41

+0

當你寫單詞`template`時,習慣於公開你的實現細節。這是它的工作原理,時期。你可以將`#include`的「實現頭文件」製作成你的主頭文件,但你必須運送源代碼。 – 2011-01-24 18:37:26

+0

@詹姆斯麥克奈利斯:第一個代碼段(灰色塊)只是一個測試文件,我用它來保存所有文件。我們的想法是,這將被分成三個文件,`lib.h`,`lib.cpp`和`main.cpp`,最後一個就是「用戶」測試,調用`lib`。問題只是爲了得到看起來像我想要的東西,我需要將頭部拆分爲實現代碼上面的一個部分和下面的一個部分。那有意義嗎? – Svend 2011-01-24 19:44:39

回答

2

有兩個問題.cpp文件你在這裏處理:

一 如果你想要把這個類的一個實例堆棧上的編譯器的需求(如你在你的main()一樣)知道類的大小(分配足夠的內存)。爲此,它需要知道成員和完整的聲明。

將類的佈局隱藏起來的唯一方法是構建一個接口和一個工廠方法/函數,並將該實例放在工廠的堆中。

爲例(沒有模板;見下文知道爲什麼):

namespace foo { 
    class IA { 
    public: 
     virtual ~IA(); 
     virtual int returnT() = 0; 

     static IA *Create(); 
    }; 
} 

在你。CPP你然後做:

namespace foo { 
    class A : public IA { 
    private: 
     int i; 
    public: 
     A() : 
     i(0) { 
     } 
     virtual ~A() { 
     } 
     virtual int returnT() { 
     return i; 
     } 
    }; 
    IA::~IA() { 
    } 

    IA *IA::Create() { 
    return new A(); 
    } 
} 

BTW:使用智能指針將建議...

II。 由於您正在使用模板,所以方法定義必須通過頭文件可見或爲特定的一組類型顯式實例化。

所以你可以在你的代碼拆分成lib.h和lib_impl.h:

lib.h:

namespace foo { 
    template <typename T> class IA { 
    public: 
     virtual ~IA() { 
     } 
     virtual T returnT() = 0; 

     static IA *Create(); 
    }; 
} 

lib_impl.h:

namespace foo { 
    template <typename T> class A : public IA<T> { 
    private: 
     T i; 
    public: 
     A() : 
     i(T()) { 
     } 
     virtual ~A() { 
     } 
     virtual T returnT() { 
     return i; 
     } 
    }; 

    template <typename T> IA<T> *IA<T>::Create() { 
    return new A<T>(); 
    } 
} 

所以你包括lib_impl.h在哪裏你需要的實現。 要使用顯式實例添加lib.cpp,只是讓該文件允許包括lib_impl.h:

lib.cpp:

#include <lib_impl.h> 
namespace foo { 
    template class IA<int>; 
    template class A<int>; 
    template class IA<float>; 
    template class A<float>; 
    template class IA<char>; 
    template class A<char>; 
    // ... 
} 
5

不能從它的聲明模板的定義分開。他們都必須一起進入頭文件。

對於 「爲什麼?」我推薦閱讀"Why can't I separate the definition of my templates class from its declaration and put it inside a .cpp file?"


我可能誤解了你的問題。爲了解決什麼也可能是你的問題,這是無效的:

namespace foo { 
    template <class T> class A;  
    template <class T> T A<T>::returnT(); 
} 

它是無效出於同樣的原因,這是無效的:

namespace foo { 
    class A; 
    int A::returnT(); 
} 

成員函數必須在定義中聲明的類。