7

我有一个 Foo 类,用于一个小型独立项目。它在 Foo.h 中有一个类定义,在实现文件 Foo.cpp 中实现了类的成员函数。

第一个问题 - 类 Foo 的成员函数之一是模板方法 Foo::doSomething(),这个方法的实现应该出现在 Foo.h 中的函数声明中是否正确?

Foo::doSomething() 将被实例化的模板参数是两种 Functor 类型之一 - 类 CalcA 和 CalcB。

我是不是该:

  • (A) 将两个 Functor 类的定义和实现放在一起放在 Foo.cpp 中(它们实际上被其他 Foo 成员函数的实现用来调用 Foo::doSomething)。
  • (B) 把这两个Functor类的定义和实现放在Foo.h中。
  • (C) 我是否应该像使用普通类一样在 Foo.h 和 Foo.cpp 中拆分两个 Functor 的定义和实现?
4

4 回答 4

8

首先你必须了解模板机制。模板不被编译,它们在使用时被实例化,然后它们的实例被编译。因此编译器需要使用模板函数在每个模块中拥有完整的模板定义,以便首先根据您传递的参数实例化它们。

为了解决您的问题,有三种解决方案,但您会发现它们都导致相同的结果。您可以在类定义中的头文件中实现整个模板(我们使用 .hxx 而不是 .h 为它们添加后缀,以准确说明它们包含模板定义):

// Foo.hxx

#ifndef __FOO_HXX__
#define __FOO_HXX__

class Foo {
  public:
   template <class T>    
   void bar(const T& t) {
      t.doSomething();
   }
 };
#endif

或者你可以从类中外部化定义,但仍然在头文件中:

// Foo.hxx

#ifndef __FOO_HXX__
#define __FOO_HXX__

class Foo {
    public:
       template <class T>    
       void bar(const T&);
};

template <class T>
void Foo::bar(const T& t) {
   t.doSomething();
}
#endif

最后,您可以在外部文件中实现模板方法主体(出于相同原因,以 .cxx 为前缀)。它将包含方法的主体,但不包括“Foo.hxx”。相反,它是“Foo.hxx”,它将在类定义之后包含“Foo.cxx”。这样,当编译器解析 #include 指令时,它会在同一个模块中找到整个模板定义,并允许它实例化它:

// Foo.hxx

#ifndef __FOO_HXX__
#define __FOO_HXX__
class Foo {
    public:
       template <class T>    
       void bar(const T&);
};

#include "Foo.cxx"

#endif

// Foo.cxx
template <class T>
void Foo::bar(const T& t) {
   t.doSomething();
}

这 3 种实现模板的方法之间的选择是一个可读性(和品味)的问题。
第二和第三在生成的代码方面是等效的,但我宁愿不使用 cxx 文件解决方案,因为当您忘记反转包含时,它通常会导致愚蠢的错误。

此外,STL 或 Boost 等知名 C++ 库仅在头文件中提出其代码,这是良好设计的标志。通过在标题中使用外部定义,您可以阐明类的定义。您还可以防止编译器自动内联方法,根据 Herb Sutter http://www.gotw.ca/gotw/033.htm,这有时会导致结果不佳

于 2010-11-30T17:11:07.863 回答
7

一般规则:

如果 foo::doSomething() 在 foo.cpp 之外使用(即通常是公共的或受保护的),它必须放在标题中。

如果没有,放入 cpp 文件是完全可以的,甚至是一个好主意(因为它可以让头文件远离混乱)。

所以,如果函子只在 cpp 文件中使用,那么一定要把模板函数也放在那里。如果这种情况发生变化,人们总是可以在以后重构事情。

于 2010-11-30T16:29:51.043 回答
1

我的默认设置是将成员函数模板的定义直接放在 .h 文件中,如下所示:

class Foo
{
public: 
  template<typename T> void DoSomething(T t);
};

// ... later...

template<typename T>
void Foo::DoSomething(T t)
{
  // ...
}

如果这对于特定情况来说不是最理想的,那么我会采取更多英勇的措施。从#include.h 文件末尾定义的 .inc 文件开始,或者甚至可能在需要使用成员函数模板的 .cpp 文件中进行显式实例化。

于 2010-11-30T16:29:35.273 回答
1

模板方法定义确实应该在它所属的类的头文件中。

像这样:

class MyClass
{
    template <typename T>
    void foo(const T&)
    {
        // Definition
    }
};

或者像这样(请注意,模板方法定义可以在类声明之后从单独的文件中包含)

class MyClass
{
    template <typename T> void foo(const T&);
};

template <typename T>
void MyClass::foo(const T&)
{
    // Definition
}

其余的取决于您同意的样式和您的需求。

如果我不仅在 Foo 中使用它们,或者如果 Foo 将它们作为类成员,我会将仿函数声明(或者甚至是简单的定义)放入标题中。

于 2010-11-30T16:32:14.733 回答