T.E*_*.D. 183 c++ coding-style code-separation
我使用C++的个人风格总是把类声明放在一个包含文件中,并在.cpp文件中定义,非常类似于Loki对C++头文件,代码分离的回答.不可否认,我喜欢这种风格的部分原因可能与我花费编码Modula-2和Ada的所有年份有关,两者都有与规范文件和正文文件类似的方案.
我有一个同事,比我更了解C++,他坚持认为所有的C++声明应尽可能在头文件中包含定义.他并不是说这是一种有效的替代风格,甚至是一种稍微好一点的风格,而是这是每个人现在都用于C++的新普遍接受的风格.
我不像以前那样柔软,所以我并不急于拼抢他的这个潮流,直到我看到更多的人和他在一起.那个成语真的有多常见?
只是给出答案的一些结构:它现在是The Way,非常常见,有点普遍,不常见,还是出错?
Eva*_*ran 198
您的同事是错误的,常见的方法是始终将代码放入.cpp文件(或您喜欢的任何扩展名)和标题中的声明.
将代码放入标题中偶尔会有一些优点,这可以让编译器更加巧妙地进行内联.但与此同时,它可能会破坏您的编译时间,因为每次编译时都必须处理所有代码.
最后,当所有代码都是标题时,拥有循环对象关系(有时是期望的)通常很烦人.
最重要的是,你是对的,他错了.
编辑:我一直在考虑你的问题.有一种情况他说的是真的.模板.许多较新的"现代"库(例如boost)大量使用模板,并且通常只是"标题".但是,这只应在处理模板时完成,因为它是处理模板时唯一的方法.
编辑:有些人想要更多的澄清,这里有一些关于编写"仅标题"代码的缺点的想法:
如果你四处搜索,你会看到很多人试图找到一种方法来减少处理boost时的编译时间.例如:如何使用Boost Asio减少编译时间,Boost Asio看到包含boost的单个1K文件的14s编译.14s可能似乎没有"爆炸",但它肯定比典型的更长,并且可以很快加起来.在处理大型项目时.仅头文件库确实以一种非常可测量的方式影响编译时间.我们只是容忍它,因为助推是如此有用.
另外,有很多东西只能在头文件中完成(甚至boost也需要链接到某些部分的库,如线程,文件系统等).一个主要的例子是你不能在头文件库中有简单的全局对象(除非你诉诸于单身的憎恶),因为你会遇到多个定义错误.注意: C++ 17的内联变量将使这个特定的例子在将来可行.
最后一点,当使用boost作为仅标题代码的示例时,通常会遗漏一个巨大的细节.
Boost是库,而不是用户级代码.所以它不会经常改变.在用户代码中,如果将所有内容放在标题中,每次稍微更改都会导致您必须重新编译整个项目.这是一个巨大的浪费时间(对于不会从编译变为编译的库不是这种情况).当你在header/source和更好的方法之间进行分割时,使用forward声明来减少包含,你可以在一天内添加时节省数小时的重新编译.
Yes*_*ke. 148
C++编码人员同意The Way,羔羊将与狮子躺下,巴勒斯坦人将拥抱以色列人,猫狗将被允许结婚.
此时.h和.cpp文件之间的分离大多是任意的,这是编译器优化的遗迹.在我看来,声明属于标题,定义属于实现文件.但是,这只是习惯,而不是宗教.
Las*_*lan 25
标头中的代码通常是一个坏主意,因为它会在您更改实际代码而不是声明时强制重新编译包含标头的所有文件.它还会减慢编译速度,因为您需要解析包含标头的每个文件中的代码.
在头文件中包含代码的一个原因是,通常需要关键字内联才能正常工作以及使用在其他cpp文件中实例化的模板.
Joh*_*McG 19
什么可能告诉你同事是一个概念,大多数C++代码应该被模板化,以允许最大的可用性.如果它是模板化的,那么一切都需要在头文件中,以便客户端代码可以看到它并实例化它.如果它对Boost和STL来说足够好,那对我们来说已经足够了.
我不同意这种观点,但它可能是它的来源.
小智 13
我认为你的同事很聪明,你也是对的.
我发现将所有内容放入标题中的有用信息是:
无需编写和同步标头和来源.
结构很简单,并且没有循环依赖性迫使编码器形成"更好"的结构.
便携,易于嵌入到新项目中.
我同意编译时间问题,但我认为我们应该注意到:
源文件的更改很可能会更改头文件,从而导致整个项目再次重新编译.
编译速度比以前快得多.如果你有一个长时间高频率的项目,它可能表明你的项目设计有缺陷.将任务分成不同的项目和模块可以避免这个问题.
最后,我只是想以你个人的观点支持你的同事.
正如Tuomas所说,你的标题应该是最小的.为了完成,我会稍微扩展一下.
我个人在我的C++项目中使用了4种类型的文件:
此外,我将此与另一条规则相结合:不要定义您可以向前声明的内容.虽然我当然合情合理(使用Pimpl到处都很麻烦).
这意味着#include每当我可以逃脱它时,我更喜欢在我的标题中使用指令的前向声明.
最后,我还使用了可见性规则:我尽可能地限制符号的范围,以便它们不会污染外部范围.
完全放在一起:
// example_fwd.hpp
// Here necessary to forward declare the template class,
// you don't want people to declare them in case you wish to add
// another template symbol (with a default) later on
class MyClass;
template <class T> class MyClassT;
// example.hpp
#include "project/example_fwd.hpp"
// Those can't really be skipped
#include <string>
#include <vector>
#include "project/pimpl.hpp"
// Those can be forward declared easily
#include "project/foo_fwd.hpp"
namespace project { class Bar; }
namespace project
{
class MyClass
{
public:
struct Color // Limiting scope of enum
{
enum type { Red, Orange, Green };
};
typedef Color::type Color_t;
public:
MyClass(); // because of pimpl, I need to define the constructor
private:
struct Impl;
pimpl<Impl> mImpl; // I won't describe pimpl here :p
};
template <class T> class MyClassT: public MyClass {};
} // namespace project
// example_impl.hpp (not visible to clients)
#include "project/example.hpp"
#include "project/bar.hpp"
template <class T> void check(MyClass<T> const& c) { }
// example.cpp
#include "example_impl.hpp"
// MyClass definition
Run Code Online (Sandbox Code Playgroud)
这里的救生员是大多数时候前向头是无用的:只有在typedef或者情况下是必需template的实现头;)
我个人在我的头文件中这样做:
// class-declaration
// inline-method-declarations
Run Code Online (Sandbox Code Playgroud)
我不喜欢将方法的代码与类混合,因为我发现快速查找内容很痛苦。
我不会把所有的方法都放在头文件中。编译器将(通常)无法内联虚拟方法,并且(可能)只会内联没有循环的小方法(完全取决于编译器)。
在类中执行方法是有效的……但从可读性的角度来看,我不喜欢它。将方法放在标题中确实意味着,如果可能,它们将被内联。
为了增加乐趣,您可以添加.ipp包含模板实现(包含在内.hpp)的文件,同时.hpp包含界面.
除了模板化的代码(取决于项目,这可能是大多数或少数文件),有正常的代码,这里最好分隔声明和定义.在需要时提供前向声明 - 这可能会影响编译时间.
我认为将所有函数定义放入头文件中绝对是荒谬的。为什么?因为头文件用作类的 PUBLIC 接口。这是“黑匣子”的外面。
当你需要查看一个类来引用如何使用它时,你应该查看头文件。头文件应该给出它可以做什么的列表(注释以描述如何使用每个函数的细节),并且它应该包括成员变量的列表。它不应该包括每个单独的函数是如何实现的,因为这是大量不必要的信息,只会使头文件变得混乱。
| 归档时间: |
|
| 查看次数: |
111302 次 |
| 最近记录: |