2011-01-24 186 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(); 
} 

成员函数必须在定义中声明的类。