《C++程序设计语言》9_源文件和程序

来源:互联网 发布:gradle mac 环境变量 编辑:程序博客网 时间:2024/05/02 04:57
1、用户将一个源文件提交给编译器后,首先进行的是该文件的预处理,也就是说,完成宏处理,并按照#include指令引进所有头文件。预处理之后的结果被称为编译单位。这种编译单位才是编译器真正的工作对象,也就是C++语言的规则所描述的对象。


2、程序是由一些文件组成的,这种组织通常被称为程序的物理结构。将一个程序物理地划分为一些相互分离的文件的工作应该根据程序的逻辑结构进行。


3、外部链接:一个名字可以在与其定义所在的编译单位不同的地方使用
内部链接:某个名字只能在其定义所在编译单位内部使用。按照默认约定,const 和 typedef 都具有内部链接。通过显示声明可以使const具有外部链接。如下:
//file1.cpp
extern const int a = 77;

//file2.cpp
extern const int a;
void g()
{
cout << a << endl;
}


注意:如果定义在全局作用域或者名字空间作用域里某个变量没有初始式,它就会被按照默认方式初始化。


4、一个inline函数必须在需要它的每个编译单位里定义 ---- 通过完全一样的定义。


//file1.cpp
int x; //具有外部链接,默认初始化为 x = 0;

void NormalFunc()
{
cout << "NormalFunc" << endl;
}

inline void InlineFunc()
{
cout << "InlineFunc" << endl;
}

//file2.cpp
extern int x; //extern指明此处x只是一个声明,不是定义
void NormalFunc();

//如果func2.cpp的InlineFunc不提供定义,链接(是链接,不是编译)将通不过。
//func2.obj : error LNK2019: 无法解析的外部符号 "void __cdecl InlineFunc(void)" (?InlineFunc@@YAXXZ),
//该符号在函数 "void __cdecl g(void)" (?g@@YAXXZ) 中被引用
inline void InlineFunc()
{
cout << "InlineFunc" << endl;
}

void g()
{
NormalFunc();
InlineFunc();
}


5、无名名字空间可以用于使一些名字局部于一个编译单位。无名名字空间的效果很像是内部链接。


/////////////////////////////////////////头文件


6、头文件里绝不该有:
常规的函数定义:   char get(char *p){return *p++;}
数据定义: int a;
聚集量(aggregate)定义:short tbl[] = {1,2,3};
无名名字空间: namespace{/* ... */}
导出的模板定义: export template <class T> f(T t){/* ... */}

/*
关于无名名字空间的解说:
//CSDN fenghou1st:
无名名字空间的作用最主要是对其他模块隐藏一些名字。如果无名的放在了.h中,那么所有include这个.h文件的模块都可以看见这个无名名字空间了,那么无名就没有意义了。

//CSDN kiffa:
无名空间除了防止名字冲突,更主要的还是进行局部化,进行信息隐藏。无名空间主要用来限定一些辅助函数,这些辅助函数用来帮助类完成工作,客户并不需要得知其存在。

// a.h
class A
{
    public:
           void f();
};
 
// a.cc
#include "a.h"
namespace
{
   void g(){ ... };  // 一个辅助函数,用来辅助类A来完成工作;客户代码不需要直接使用这个函数,因此最
                     // 好把它“隐藏”起来,不让客户代码得知。这里如果定义成全局函数就很不好,定义
                    //  成static函数则效果和namespace一样,但个人更喜欢无名空间。其实很多选择冲突 
                    // 都是由于习惯,如果一个做法不常用不习惯,无论是由于个人还是由于周围大环境(大
                   //  家都不用,项目也不做要求),那么自然就不太喜欢这种做法。
}
 
void A::f()
{
  //...
  g(); //调用辅助函数来完成工作。
}

//CSDN Jinhao:
//假设a.cpp里有一个匿名命名空间.
namespace
{
   int i;
}

里面的i仍然是外部连接的。但是不能在其他的编译单元中引用这个i。其原因是编译器为每个编译单元的匿名命名空间默认给定了一个唯一名字。所以里在其他的编译单元中,使用的匿名命名空间并不是a.cpp中定义的那个。
这样看起来就跟static int i很相像。
*/

/////////////////////////////////////////单一定义规则 (One-Definition Rule,ODR)


7、一个类、模板或者在线函数的两个定义能够被接受为同一个惟一定义的实例,当且仅当:
[1]它们出现在不同的编译单位里
[2]它们按一个个单词对应相同
[3] 这些单词的意义在两个编译单位中也完全一样

8、如果模板只存在一个声明,就可以使用一个导出的(export)模板:

//file1.cpp
export template<typename T> T twice(T t){return t+t;}

//file2.cpp
template<typename T> T twice(T t);
int g(int t){return twice(i);}

关键字export的意思就是“在其他编译单位可以访问”。

/////////////////////////////////////////与非C++代码的连接

9、extern "C" char* strcpy(char*, const char*);
extern "C" 中的 C 表示一个连接约定,而不是一种语言,并不影响调用函数的语义。人们也经常将extern "C"用于连接Fortran 和汇编例程,因为它们的要求也正好符合C实现的约定。
特别地,声明为extern "C"的函数仍然要遵守C++的类型检查和参数转换规则,而不是C较弱的规则。

10、连接块:
extern "C"{
... //各函数的声明
}

extern "C"{
#include <string.h>
}

11、任何声明都可以出现在连接块里:
extern "C"{
int g1; //定义
extern int g2;//声明
}

它们的作用域和存储类型都不会受到影响。


12、有着C连接的名字可以在名字空间里定义。名字空间只影响到C++程序里对于该名字的访问方式,但却不会影响连接器看到它的方式。
0 0
原创粉丝点击