C++;类声明和名称空间 我正试图编写一个C++库。这是通常的设置,一个cpp文件,一个头文件。我希望头文件只公开要使用的部分(例如,我有一个抽象基类,我不希望在头文件中)。到目前为止,我只处理一个文件(我认为这应该没有什么区别,因为包含是由预处理器完成的,它不关心任何事情)

C++;类声明和名称空间 我正试图编写一个C++库。这是通常的设置,一个cpp文件,一个头文件。我希望头文件只公开要使用的部分(例如,我有一个抽象基类,我不希望在头文件中)。到目前为止,我只处理一个文件(我认为这应该没有什么区别,因为包含是由预处理器完成的,它不关心任何事情),c++,templates,class,namespaces,header-files,C++,Templates,Class,Namespaces,Header Files,您将注意到,“头文件”分布在头实现文件之前和之后的两个点上 #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) {

您将注意到,“头文件”分布在头实现文件之前和之后的两个点上

#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(); 
} 
namespace foo {
    class A;
    int A::returnT();
} 

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

此处处理的.cpp文件存在两个问题:

一,。 如果要将该类的实例放在堆栈上(就像在main()中那样),编译器需要知道该类的大小(以分配足够的内存)。为此,它需要了解成员,并以此了解完整的声明

隐藏类布局的唯一方法是建立一个接口和一个工厂方法/函数,并将实例放在工厂的堆上

例如(没有模板;请参见下文了解原因):

然后在.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();
  }
}
顺便说一句:建议使用智能指针

二,。 由于您使用的是模板,因此方法定义必须通过头文件可见,或者为特定类型集显式实例化

因此,您可以将代码拆分为lib.h和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.h:

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

      static IA *Create();
  };
}
名称空间foo{
模板类IA{
公众:
虚拟~IA(){
}
虚拟T returnT()=0;
静态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>();
  }
}
名称空间foo{
模板类别A:公共IA{
私人:
TⅠ;
公众:
A():
i(T()){
}
虚拟~A(){
}
虚拟T returnT(){
返回i;
}
};
模板IA*IA::创建(){
返回新的A();
}
}
因此,在需要请求的地方包括lib_impl.h。 要使用显式实例化,请添加lib.cpp并允许该文件包含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.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>;
  // ...
}
#包括
名称空间foo{
模板类IA;
甲级模板;
模板类IA;
甲级模板;
模板类IA;
甲级模板;
// ...
}

整个第一块代码是头文件代码吗?我很困惑,因为有两个标记为“lib.h”的块,但是在那里有一个
main
函数……在编写单词
template
时习惯于披露实现细节。这就是它的工作原理,句号。您可以制作“实现头文件”,将其
#包括在主头文件中,但必须提供源文件。@James McNellis:第一个代码段(灰色块)只是一个测试文件,我用来将所有内容保存在一个文件中。其想法是将其分为三个文件,
lib.h
lib.cpp
main.cpp
,最后一个只是“用户”测试,调用
lib
。问题在于,为了得到我想要的东西,我需要将头部拆分为实现代码上方的一部分和下方的一部分。这有意义吗?为了补充这个伟大的答案,您还可以在lib.h的末尾包含lib#u impl.h。这样,从用户的角度来看,快速查看lib.h将揭示基本内容(界面),而不需要查看lib_impl.h中的详细信息,也不需要做任何特殊工作来确定何时应该包含lib_impl.h。这种方法(也在常见问题解答中概述)就是我最终采用的方法。然而,智能指针并不是我所处的位置;)我有个问题。据我所知,lib.cpp被用作编译器的一种“暗示”,告诉编译器要生成哪些模板。常见问题解答也提到了这种方法(第35.13项),但是这种语法具体做了什么?例如,我可以在Stroustrups的书中找到一些东西?@Svend:显式实例化在lib.cpp的编译单元中为特定的实例化模板生成所有方法。因此,模板类的使用者可以使用,即a,而不包括lib_impl.h,因为链接器可以从lib.dll/lib.So解析方法。优点是:隐藏实现,如果经常使用,编译速度更快。缺点:您不能使用…@Svend:来稍微澄清一下“更快的编译时间”:如果实现在lib.h中,或者如果lib_impl.h包含在所有地方,那么编译器将在其使用的每个编译单元中实例化所使用的方法(可能是相同方法的一百倍)。由链接器稍后删除所有冗余实例化…这需要时间。。。。
#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>;
  // ...
}