【问题标题】:C++ generic classes - separating interface and implementationC++ 泛型类 - 分离接口和实现
【发布时间】:2013-09-20 12:53:53
【问题描述】:

一个非常常见的编码实践是通过在每个类的基础上使用.h.cpp 文件将类的接口与其成员函数的实现分开。所以class Foo 将通过Foo.h 头文件和相应的Foo.cpp 文件来实现。

在泛型类的特殊情况下,这通常被抛到窗外,而是使用header-only 库来保持编译器满意,即使它确实会使接口文件与实现细节混淆。

我最近遇到了一些如下编写的代码。 .h 文件包含接口和#include 到包含通用成员函数实现的.hpp 文件。

例如对于类型 T 的简单容器 Value.h

#ifndef VALUE_H
#define VALUE_H

template <typename T>
class Value
{
public:
    Value(T value);
    void set(T value);
    T get() const;
private:
    T data;
};

#include "Value.hpp"

#endif

以及对应的Value.hpp

#ifndef VALUE_HPP
#define VALUE_HPP

template <typename T>
Value<T>::Value(T value) : data(value)
{
}

template <typename T>
void Value<T>::set(T value)
{
    data = value;
}

template <typename T>
T Value<T>::get() const
{
    return data;
}

#endif

这具有更好地分离接口和实现的优点,以及实际编译的进一步好处(在我有限的测试中)。

然后我的问题是采用此约定是否有任何隐藏的陷阱

【问题讨论】:

  • 我认为你实现的分离是一种幻觉。但是,如果您对此感到满意,那就去吧。顺便说一句,我相信您仍然需要将您的实现方法标记为inline,模板与否它们仍在头文件中。
  • AFAIK 不,但我会选择更常用的文件扩展名,如 .tcc,并且实现文件的包含保护是多余的。
  • 在我看来这曾经是一个 .h 和一个 .cpp,但后来程序员意识到模板方法不会在 .cpp 文件中实例化,因此它们被移至一个 .hpp 代替。像这样拆分它对你没有任何帮助。
  • @Peter 在易用性方面是否有一些优势,因为使用该类的客户端程序员可以更轻松地将类的可观察语义与其实现细节分开?

标签: c++ class generics interface header-files


【解决方案1】:

由于您需要在每个使用 Value 类的文件中同时包含“value.h”和“value.hpp”,因此编译时间没有任何好处。但是,如果您的意思是“它可以编译”,而不是您将实现放在 .cpp 文件中的解决方案,那么是的,这是有好处的。

当然有一个好处是您可以轻松查看该类提供的接口,而无需让文件被一堆实现代码弄得一团糟。

我不确定“value.h”和“value.hpp”的命名约定是否是“最佳”选择。我认为“value.inl”是第二个文件的更好名称。

【讨论】:

  • 客户端代码只包含Value.h,自动拉入Value.hpp
  • 其中又包含“value.hpp”,因此这两个文件都包含在构建过​​程中,在每个使用“value.h”的文件中
  • 哦,你说的是编译时间,而不是用户代码。那没关系。
  • 是的,我的意思是编译为“编译不同于.h.cpp 组合”。我知道好处纯粹是化妆品,但这也很重要。我会避开.hpp,因为人们可能只想包含它。
【解决方案2】:

不,此解决方案没有特殊缺陷。 .hpp 文件只是另一个头文件,其中包含模板类声明的方法的定义。由于模板类的方法需要在头文件中定义,这是将声明与定义分开的便捷方法。扩展名.hpp表明它是头文件和实现文件的混合体,常用。

【讨论】:

  • 抱歉,您对 twitter 一词的使用让我有点困惑。这是什么意思?
  • @Duncan 那是翻译哎呀。
猜你喜欢
  • 1970-01-01
  • 2016-10-16
  • 1970-01-01
  • 1970-01-01
  • 2015-04-17
  • 1970-01-01
  • 2020-05-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多