您的位置:首页 > 其它

库,头文件,命名空间,类等概念

2017-03-27 17:30 295 查看

几个基本概念之间的联系:

在一个程序中,一般是命名空间包含类,类中包含函数,函数是某个功能的实现,而头文件是函数的声明,库是一些标准函数(我们经常重复使用的函数)的实现,所以在一个c++程序中,头文件就是用来对所用到的函数进行声明的。在新的C++标准中,标准库文件(函数的实现)和include头文件(函数的声明)都是包含在命名空间std中,所以要使用头文件中声明的函数的话,还应该:using
namespace std;

命名空间的概念:

命名空间内部不仅可以声明或定义变量,对于其它能在命名空间以外声明或定义的名称,同样也都能在命名空间内部进行声明或定义,例如类、函数、typedef、#define 等都可以出现在命名空间中。 C++ 引入了命名空间的概念,计划重新编写库,将类、函数、宏等都统一纳入一个命名空间,这个命名空间的名字就是std。std 是 standard 的缩写,意思是“标准命名空间”。

在编译器的安装目录的include文件夹,你能够看到写程序经常用到的iostream,cstdio等头文件,以iostream为例,其内容如下:

#ifndef _GLIBCXX_IOSTREAM
#define _GLIBCXX_IOSTREAM 1

#pragma GCC system_header

#include <bits/c++config.h>
#include <ostream>
#include <istream>

namespace std
{
/**
*  @name Standard Stream Objects
*
*  The <iostream> header declares the eight <em>standard stream
*  objects</em>.  For other declarations, see
*  http://gcc.gnu.org/onlinedocs/libstdc++/27_io/howto.html#10 and the
*  @link s27_2_iosfwd I/O forward declarations @endlink
*
*  They are required by default to cooperate with the global C library's
*  @c FILE streams, and to be available during program startup and
*  termination.  For more information, see the HOWTO linked to above.
*/
//@{
extern istream cin;		///< Linked to standard input
extern ostream cout;		///< Linked to standard output
extern ostream cerr;		///< Linked to standard error (unbuffered)
extern ostream clog;		///< Linked to standard error (buffered)

#ifdef _GLIBCXX_USE_WCHAR_T
extern wistream wcin;		///< Linked to standard input
extern wostream wcout;	///< Linked to standard output
extern wostream wcerr;	///< Linked to standard error (unbuffered)
extern wostream wclog;	///< Linked to standard error (buffered)
#endif
//@}

// For construction of filebuffers for cout, cin, cerr, clog et. al.
static ios_base::Init __ioinit;
} // namespace std

#endif /* _GLIBCXX_IOSTREAM */

从上面可以清楚的看到,这个头文件的内容都包含在std这个命名空间之中,当然std命名空间不止包含这些内容,因为命名空间是可以不连续的,所以一些常用的.h文件的内容基本结构都和iostream一样:先打开namespace std{...},然后在这个命名空间中增加内容。

iostream和iostream.h的区别:

<iostream>和<iostream.h>不一样,表面上就能看到前者没有后缀。实际上,在编译器include文件夹里面可以看到,二者是两个文件,打开文件就会发现,里面的代码是不一样的。后缀为.h的头文件c++标准已经明确提出不支持了,早些的实现将标准库功能定义在全局空间里,声明在带.h后缀的头文件里。c++标准为了和C区别开,也为了正确使用命名空间,规定头文件不再使用后缀.h。因此,当使用<iostream.h>时,相当于在c中调用库函数,使用的是全局命名空间,也就是早期的c++实现;当使用<iostream>的时候,该头文件没有定义全局命名空间,必须使用using
namespace std;这样才能正确使用cout。

编译器是如何根据头文件的内容找到对应的实现文件的? 

       我们通常把类的定义都放在头文件,而其成员函数以及静态成员变量的定义都放在一个主名相同,扩展名不同的cpp文件——这只是一种风格和传统,而不是C++语言规定的。你也可以把一个类的10个函数分别在10个不同的cpp文件中实现,甚至有4个实现在cpp文件中,另外3个是打包在以前做好的静态库(.lib)中,还有3个内联定义在头文件中——都没问题。只要每个cpp文件都能顺利通过编译,而且链接器在链接时都能找到这些定义就OK了。详细来讲:

       首先编译器是不管头文件的,头文件只是用来被cpp文件包含的,被包含之后,它就成了那个cpp文件的一部分了,而编译器只编译.cpp文件,不会去单独编译一个头文件的。编译器不会查找“确定”的实现文件。它只是编译每个cpp文件,每个cpp文件,把所有包含的东西展开后,其内容都不能自相矛盾(比如类A并没有一个名叫f的方法,后面却用到了这个方法),否则编译根本就通不过。——编译器这样做之后,一般生成
.obj(VC), .o(unix)

       然后连接器把所有这些obj文件连接成一个程序,或者是exe或dll(或做成静态的lib)。如果在连接的过程中,有些实体(比如变量或函数)找不到定义,则会报link错误。或者某个函数f在不同的cpp文件中出现了多次定义,而且都是外连接,那连接器也会报重复定义地错误。

       更加深入的理解那就是:编译器只是负责检查函数的声明,并生成一个假设的函数定义地址的引用,而链接器负责把这个假设的引用指向真实的函数地址。换句话说:编译的时候不需要找对应的库文件,链接时才需要。其实链接的时候它也不找,而是采取如下的办法:

1、 把所有需要的库中的符号都载入内存(这里需要的库是工程里设置好的,而不是根据头文件变化而变化的)

2、 链接时如果需要一个函数,链接程序会到步骤1中的符号表中找,并进行定位。

所以无论在哪个库文件中,只要你在工程中指定它为需要的库,则链接程序都会载入它的符号到符号表。要找到需要的函数,不需要知道它在哪个库文件中,只要符号表有即可。

linux中关于库和头文件的使用详见博文:Linux编译多个不同目录下的文件以及静态库、动态库的使用

附录:

.dll文件:

       dll(Dynamic Link Library)文件为动态链接库文件,又称“应用程序拓展”,是软件文件类型。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即dll文件,放置于系统中。当我们执行某一个程序时,相应的dll文件就会被调用。一个应用程序可使用多个dll文件,一个dll文件也可能被不同的应用程序使用,这样的dll文件被称为共享dll文件。

.lib文件:

       静态编译:将导出声明和实现都放在lib中,编译后所有代码都嵌入到宿主程序,所以链接好了之后,lib文件就没有用了;

动态编译:一般的动态库程序有lib文件(这个lib文件和上面静态编译的lib文件不同)和dll文件。lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。此时lib文件一般是一些索引信息,相当于一个C语言中的h文件,编译后只是将函数地址存在宿主程序中,而函数的具体实现在dll文件中,运行到调用函数是调用dll并载入函数来实现函数的具体操作。
.dll和.lib都是windows中的文件格式。linux中对应的文件格式是 .so和 .a
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: