C++的前瞻性声明 - 什么时候重要?
Forward declarations in C++ - when it's matter?
我认为这是c++的一种精神——你不为你没有的东西付出代价Want(你明确地为你需要的东西付费):
// a.h
#include <iosfwd>
template< class T >
class QVector;
struct A
{
void process( QVector<int> );
void print( std::ostream& );
};
// some.cpp
#include "a.h"
#include <iostream> // I need only A::print() in this module, not full interface
...
A().print( std::cout );
...
这就是为什么我认为禁止开发人员工作是不公平的(c++ 11 STL有前向声明文件吗?).
但是我也看到有一件不好的事情:模块A的依赖会在外部环境中扩散(#include
指令的重复),并可能导致硬重构当接口改变时(例如用QList替换QVector -现在你需要用<QList>
替换所有出现的<QVector>
)
#include <iostream>
#include <QVector>
struct A
{
void process( QVector<int> );
void print( std::ostream& );
};
我们应该称它为"接口的基本类型"-模块吗接口的类型应该像基本类型(总是)定义和可用)?这也说得通,但仍不清楚哪种方法更好(例如Qt混合两种方法)。
我的个人决定 -总是提供更好的模块化(当我们有足够的依赖时):
// a_decl.h
#include <iosfwd>
template< class T >
class QVector;
struct A
{
void process( QVector<int> );
void print( std::ostream& );
};
// a.h
// Include this file if you want to use most interface methods
// and don't want to write a lot of `#include`
#include <iostream>
#include <QVector>
#include "a_decl.h"
,让开发人员选择包含什么。
你对这些方法有什么看法?哪种方式对你更好,为什么?在所有情况下,我们有一个明确的赢家吗?还是总是取决于具体情况?
来自我与语言创造者的通信(我没有收到最终答复)
更新:在boost 1.48.0中出现了容器库,它允许定义未定义的用户类型的容器(了解更多)
c++是一种给程序员留下了许多自由度的语言,所以在某种程度上,不可避免地会有不同的方法来做同样的事情。
在我看来,你所定义的"解决方案",即在任何。h文件中包含所有必要的include或forward声明,是为了避免"不完整的头文件"而采取的方式,我一直遵循这条规则。
有一本有趣的书,详细讨论了这样做或不这样做的利弊:《大规模c++软件设计》;由John Lakos编写,上面的规则来自哪里。
在具体谈到远期声明时,Lakos区分了"仅以名称命名"answers"仅以名称命名"。和";in-size"类用法;只有在第二种情况下,使用前向声明才是合法的(根据他的观点):
定义:如果编译函数f的函数体需要先看到T的定义,则函数f使用大小为的类型T 。
定义:如果编译f和f可能依赖的任何组件不需要先看到T的定义,则函数f只在名称上使用类型T 。
(源)
具体来说,Lakos的推理围绕着c++编程的某些风格对大型系统的影响,即一定复杂性的系统,但我认为他的建议也非常适合于任何规模的系统。
希望他能帮到你。
我认为这两种方法都是正确的。
创建前向头文件很简单,而且大部分是库维护者的责任。
开发人员/客户端可以根据自己的需要选择使用前向报头或物理报头。
大型项目肯定会在可能的情况下支持并受益于转发头。
包含物理头文件并不能真正解决依赖问题,但在很多情况下会引入新的依赖。如。"如果我从这个头文件中删除#include <string>
,那么一些编译将失败(并且可能因您使用的库及其平台差异而异)。
我认为一个好的库维护者应该提供转发头,因为基于平台、版本等,维护任何差异和更新对其他人来说都是一件痛苦的事情。当项目规模足够大时,有一个明显的赢家——两者都有,你总是有选择的余地。
- 什么时候调用组成单元对象的析构函数
- 什么时候在C++中返回常量引用是个好主意
- 什么时候调用析构函数
- boost odeint什么时候真正调用观测者
- 编译器对数组声明大小的计算。什么时候发生?
- 什么时候最好在子进程中使用 CPU 或 I/O 密集型代码 [ C++ ]
- 您应该在什么时候创建自己的异常类型
- 我什么时候会默认(而不是删除)基类中的复制和移动操作
- 什么时候可以使用常量装饰调用我的重载函数?
- unordered_map什么时候返回 -1?
- QCoreApplication什么时候有效?
- sizeof(size_t) 和 sizeof(ptrdiff_t) 什么时候会有所不同?
- 什么时候用指针调用C++类构造函数
- 我不明白在这个例子中什么时候调用构造函数
- 如果真的需要std::move,我们应该什么时候声明右值refs
- P1008("prohibit aggregates with user-declared constructors")在实践中什么时候有用?
- 程序什么时候会创建多个堆
- 调用boost.asio的异步函数时,线程是什么时候创建的
- Swig/python : 什么时候需要 SWIG_init() ?
- C++的前瞻性声明 - 什么时候重要?