C++中前置声明介绍
前置声明是指对类、函数、模板或者结构体进行声明,仅仅是声明,不包含相关具体的定义。在很多场合我们可以用前置声明来代替#include语句。
类的前置声明只是告诉编译器这是一个类型,但无法告知类型的大小,成员等具体内容。在未提供完整的类之前,不能定义该类的对象,也不能在内联成员函数中使用该类的对象。而头文件则一一告之。
如:class Screen;
前置声明,也称前向声明(forward declaration)。在声明之后,定义之前,类Screen是个不完整类型(incomplete type),即已知Screen是一个类型,但是不知道包含哪些成员。
不完全类型只能以有限方式使用。不能定义该类型的对象。不完全类型只能用于定义指向该类型的指针及引用,或者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数。
可以通过前置声明配合指针或引用类型声明来减少编译依赖。
Never #include a header when a forward declaration will suffice.
前置声明的作用:
(1)、可以减少编译依赖、减少编译时间(如果头文件被修改,会导致多次重新编译);
(2)、可以隐藏细节;
(3)、可以减少类大小(前置声明会告诉这个类的存在,而不用提供类定义的所有细节);
(4)、减少include,防止类间相互引用形成依赖,造成编译不通过.
以下是在Google C++风格指南中对前置声明的介绍:
尽可能地避免使用前置声明。使用#include 包含需要的头文件即可。
所谓前置声明(forward declaration)是类、函数和模板的纯粹声明,没伴随着其定义.
优点:
(1)、前置声明能够节省编译时间,多余的 #include 会迫使编译器展开更多的文件,处理更多的输入。
(2)、前置声明能够节省不必要的重新编译的时间。 #include 使代码因为头文件中无关的改动而被重新编译多次。
缺点:
(1)、前置声明隐藏了依赖关系,头文件改动时,用户的代码会跳过必要的重新编译过程。
(2)、前置声明可能会被库的后续更改所破坏。前置声明函数或模板有时会妨碍头文件开发者变动其 API.例如扩大形参类型,加个自带默认参数的模板形参等等。
(3)、前置声明来自命名空间std:: 的 symbol 时,其行为未定义。
(4)、很难判断什么时候该用前置声明,什么时候该用 #include 。极端情况下,用前置声明代替 includes 甚至都会暗暗地改变代码的含义.
结论:
(1)、尽量避免前置声明那些定义在其他项目中的实体.
(2)、函数:总是使用#include.
(3)、类模板:优先使用#include.
以下摘自《Using Incomplete(Forward) Declarations》:
An incomplete declaration(an incomplete declaration is often called a forward declaration) is the keyword class or struct followed by the name of a class or structure type.It tells the compiler that the named class or struct type exists, but doesn't say anything at all about the member functions or variables of the class or struct; this omission means that it is a (seriously) incomplete declaration of the type. Since an incomplete declaration doesn't tell the compiler what is in the class or struct, until the compiler gets the complete declaration, it won't be able to compile code that refers to the members of the class or struct, or requires knowing the size of a class or struct object (to know the size requires knowing the types of the member variables).
Use an incomplete declaration in a header file whenever possible. By using an incomplete declaration in a header file, we can eliminate the need to #include the header file for the class or struct, which reduces the coupling, or dependencies,between modules, resulting in faster compilations and easier development. If the .cpp file needs to access the members of the class or struct, it will then #include the header containing the complete declaration.
When will an incomplete declaration work in a header file:
(1)、If the class type X appears only as the type of a parameter or a return type in a function prototype.
class X; X foo(X x);
(2)、If the class type X is referred to only by pointer (X*) or reference (X&), even as a member variable of a class declared in A.h.
class X;
class A {
/* other members */
private:
X* x_ptr;
X& x_ref;
};
(3)、If you are using an opaque type X as a member variable of a class declared in A.h.This is a type referred to only through a pointer,and whose complete declaration is not supposed to be available, and is not in any header file. Thus an incomplete declaration of the type is the only declaration your code will ever make or need either in A.h or A.cpp.
When will an incomplete declaration not work in a header file:
(1)、If your A.h header file declares a class A in which the incompletely declared type X appears as the type of a member variable.
class X;
class A {
private:
X x_member; // error- can't declare a member variable of incomplete type!
};
(2)、If your A.h header file declares a class A in which the incompletely declared type X is abase class (A inherits from X).
class X;
class A : public X { // error - baseclass is incomplete type!
(3)、If you don't actually know the name of the type. You can't forward declare a type unless you know its correct name. This can be a problem with some of the types defined in the Standard Library, where the normal name of the type is actually a typedef for a particular template instantiated with some other type, usually with multiple template parameters. For example, the following will not work to incompletely declare the std::string class:
class std::string;
在Google C++风格指南中,指出尽可能地避免使用前置声明。而在《Using Incomplete(Forward) Declarations》中,指出能用前置声明代替#include的时候,应尽量用前置声明。
以下的内容是摘自:http://stackoverflow.com/questions/553682/when-can-i-use-a-forward-declaration
#ifndef FBC_MESSY_TEST_FORWARD_DECLARATION_HPP_
#define FBC_MESSY_TEST_FORWARD_DECLARATION_HPP_
// reference: http://stackoverflow.com/questions/553682/when-can-i-use-a-forward-declaration
/*
Put yourself in the compiler's position: when you forward declare a type,
all the compiler knows is that this type exists; it knows nothing about
its size, members, or methods. This is why it's called an incomplete type.
Therefore, you cannot use the type to declare a member, or a base class,
since the compiler would need to know the layout of the type.
*/
// Assuming the following forward declaration.
class X;
// Here's what you can and cannot do.
// 1. What you can do with an incomplete type:
// 1.1 Declare a member to be a pointer or a reference to the incomplete type:
class Foo_1 {
X *pt1;
X &pt2;
};
// 1.2 Declare functions or methods which accept/return incomplete types:
void f1(X);
X f2();
/* 1.3 Define functions or methods which accept/return pointers/references to
the incomplete type (but without using its members): */
void f3(X*, X&) {}
X& f4() { X* x = nullptr; return *x; }
X* f5() { X* x = nullptr; return x; }
// 2. What you cannot do with an incomplete type:
// 2.1 Use it as a base class
// class Foo_2 : X {} // compiler error!
// 2.2 Use it to declare a member:
/* class Foo_2 {
X m; // compiler error!
}; */
// 2.3 Define functions or methods using this type
// void f6(X x) {} // compiler error!
// X f7() {} // compiler error!
/* 2.4 Use its methods or fields,
in fact trying to dereference a variable with incomplete type */
/* class Foo_3 {
X *m;
void method() {
m->someMethod(); // compiler error!
int i = m->someField; // compiler error!
}
}; */
/*
When it comes to templates, there is no absolute rule:
whether you can use an incomplete type as a template parameter is
dependent on the way the type is used in the template.
*/
/*
"In computer programming, a forward declaration is a declaration of an identifier
(denoting an entity such as a type, a variable, or a function) for which the
programmer has not yet given a complete definition."
In C++, you should forward declare classes instead of including headers.
Don’t use an #include when a forward declaration would suffice.
When you include a header file you introduce a dependency
that will cause your code to be recompiled whenever the header file changes.
If your header file includes other header files, any change to those files will
cause any code that includes your header to be recompiled.
Therefore, you should prefer to minimize includes,
particularly includes of header files in other header files.
You can significantly reduce the number of header files
you need to include in your own header files by using forward declarations.
*/
#endif // FBC_MESSY_TEST_FORWARD_DECLARATION_HPP_
GitHub:https://github.com/fengbingchun/Messy_Test
C++中前置声明介绍的更多相关文章
- C++中前置声明的应用与陷阱
前置声明的使用 有一定C++开发经验的朋友可能会遇到这样的场景:两个类A与B是强耦合关系,类A要引用B的对象,类B也要引用类A的对象.好的,不难,我的第一直觉让我写出这样的代码: // A.h #in ...
- C++类型前置声明
前言 本文总结了c++中前置声明的写法及注意事项,列举了哪些情况可以用前置声明来降低编译依赖. 前置声明的概念 前置声明:(forward declaration), 跟普通的声明一样,就是个声明, ...
- C++中头文件相互包含与前置声明
一.类嵌套的疑问 C++头文件重复包含实在是一个令人头痛的问题,前一段时间在做一个简单的数据结构演示程序的时候,不只一次的遇到这种问题.假设我们有两个类A和B,分别定义在各自的有文件A.h和B.h中, ...
- 关于C++中的前置声明(附程序运行图)
实验于华中农业大学逸夫楼2017.3.10 在编写C++程序的时候,偶尔需要用到前置声明(Forward declaration).下面的程序中,带注释的那行就是类B的前置说明.这是必须的,因为类A中 ...
- c++中的前置声明
引用google c++编码规范: When you include a header file you introduce a dependency that will cause your cod ...
- 【原创】SystemVerilog中的typedef前置声明方式
SystemVerilog中,为了是代码简洁.易记,允许用户根据个人需要使用typedef自定义数据类型名,常用的使用方法可参见"define和typedef区别".但是在Syst ...
- C++ 类的前置声明
http://www.2cto.com/kf/201311/260705.html 今天在研究C++”接口与实现分离“的时候遇到了一个问题,看似很小,然后背后的东西确值得让人深思!感觉在学习的过 ...
- C++ 前置声明 和 包含头文件 如何选择
假设有一个Date类 Date.h class Date { private: int year, month, day; }; 如果有个Task类的定义要用到Date类,有两种写法 其一 Task1 ...
- C++_前置声明
为什么要有前置声明? eg: -定义一个类 class A,这个类里面使用了类B的对象b,然后定义了一个类B,里面也包含了一个类A的对象a,就成了这样: //a.h #include "b. ...
随机推荐
- Python笔记(六):推导数据
(一) 准备工作 创建1个文件记录运动员的跑步成绩 james.txt 2-34,3:21,2.34,2.45,3.01,2:01,2:01,3:10,2-22 (二) 要求 在屏幕上输出运动员最 ...
- 容器监控—阿里云&容器内部服务监控
目前Docker的使用越来越离不开对容器的监控,阿里云最近上线了容器服务,不但提供了核心的容器和宿主机监控能力,而且支持集成 Cloud Insight 监控,下面会介绍如何集成. 首先介绍一下阿里云 ...
- Java为什么不支持多继承
类定义属性和方法 描述某一类事物的抽象 而接口定义的是行为 并不限于任何具体意向 按照逻辑上来说 单继承更明确 一个子类就应该是其父类代表的事物中的某个更具体的类别 不应该即是这种东西又是那种东西 而 ...
- 有关 Azure IaaS VM 磁盘以及托管和非托管高级磁盘的常见问题解答
本文将对有关 Azure 托管磁盘和 Azure 高级存储的一些常见问题进行解答. 托管磁盘 什么是 Azure 托管磁盘? 托管磁盘是一种通过处理存储帐户管理来简化 Azure IaaS VM 的磁 ...
- orcl 复杂查询
测试环境: create table bqh6 (xm varchar2(10),bmbh number(2),bmmc varchar2(15),gz int);insert into bqh6 v ...
- 数据库迁移之从oracle 到 MySQL最简单的方法
数据库迁移之从oracle 到 MySQL最简单的方法 因工作需要将oracle数据库换到MySQL数据库,数据量比较大,百万级别的数据,表也比较多,有没有一种既快捷又安全的方法呢?答案是肯定的,下面 ...
- Docker容器学习与分享10
Docker容器向外提供服务 用分享04中的Nginx服务来试一下. 不过这次我直接用Nginx镜像创建容器,先下载Nginx镜像. [root@promote ~]# docker search n ...
- MySql详解(六)
MySql详解(六) MySql事务 一.含义 事务:一条或多条sql语句组成一个执行单位,一组sql语句要么都执行要么都不执行 二.特点(ACID) A 原子性:一个事务是不可再分割的整体,要么都执 ...
- http的长连接和短连接(数据库也一样)
长连接与短连接 所谓长连接,指在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接,一般需要自己做在线维持. 短连接是指通信双方有数据交互时 ...
- linux添加磁盘空间
首先你要关掉系统,把分配的硬盘空间变大,或者重新建立一个虚拟硬盘(这时下面的就不是sda了,而是sdb1了).这两种方法都可行,我都试过了.其次用root用户登录到你的linux系统,查看你系统的分区 ...