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. ...
随机推荐
- 标准JSF的生命周期
JavaServer Faces (JSF) 是一种用于构建Java Web 应用程序的标准框架.它提供了一种以组件为中心的用户界面(UI)构建方法,从而简化了Java服务器端应用程序的开发.它的生命 ...
- Java的8种基本数据类型和3种引用数据类型
背景 最近被一个问题难倒:问到Java的基本数据类型有8种,具体是哪几个?一起复习下: Java数据类型概述 变量就是申请内存来存储值,即当创建变量的时候,需要在内存中申请空间. 内存管理系统根据变量 ...
- 使用 Azure PowerShell 模块创建和管理 Windows VM
Azure 虚拟机提供完全可配置的灵活计算环境. 本教程介绍 Azure 虚拟机的基本部署项目,例如选择 VM 大小.选择 VM 映像和部署 VM. 你将学习如何执行以下操作: 创建并连接到 VM 选 ...
- Oracle EBS INV 查询物料无值 ECO
查找物料的时候报错 没有输入值 解决方法: 针对FORM做trace 多查看几个生成的trace 搜索 MTL_SYSTEM_ITEMS_b 的信息 查看到最后面的语句(一般可直接查看) 看SQL 哪 ...
- Oracle EBS INV更新保留
CREATE or REPPLACE PROCEDURE UpdateReservation AS -- Common Declarations l_api_version NUMBER := 1.0 ...
- Asp连接Oracle (包含绿色版12.2客户端和ODBC驱动安装)
我能操作的终端电脑是一台linux系统可以上互联网 ,服务器在部署在独立的私网上,不方便上互联网.服务器是2008R2.安装vs不是很方便.其所linux下作开发不是不可以,java php mono ...
- Linux 补丁生成与使用
我们在升级Linux 内核的时候,难免会接触到补丁的知识.下面对如何生成补丁和如何打补丁作讲解. 生成补丁: 制作 hello.c 和 hello_new.c 两个文件如如下所示. ➜ diff ls ...
- SSH批量分发管理
ssh服务认证类型主要有两个: 基于口令的安全验证: 基于口令的安全验证的方式就是大家一直在用的,只要知道服务器的ssh连接账户.口令.IP及开发的端口,默认22,就可以通过ssh客户端登陆到这台远程 ...
- 2.3Python基础语法(三)之输入输出
返回总目录 目录: 1.input输入 2.print输出 (一)input输入 1.input的处理方式 # 输入input string = input("请输入一个字符串:" ...
- IE 8 下sharepoint 2013 难看的字体的解决方案
将 corev15.css 中的有关"Segoe UI","Segoe",Tahoma,移除即可. 一共二处 C:\Program Files\Common F ...