深入分析C++中声明与定义的区别(2)
这些声明本身不会影响到.o文件的内容。每一个都只是命名一个外部符号,使当前的编译单元在需要的时候可以访问相应的全局定义。
函数调用会导致一个未定义的符号被写入到.o文件。如果a在该文件中没有被使用,那么没有被写入到.o文件。而func函数有对此函数的调用。也就会将此符号写入目标文件。此后此.o文件与定义此符号的.o文件被连接在一起,前面未定义的符号被解析。
上述声明有可能导致该符号被写入目标文件中。但是以下声明并不会导致该符号写入到目标文件中。
如:
typedef int Int; Class A; struct s; union point;
它们的链接也是内部的。
类声明和类定义都是内部链接。只是为当前编译单元所用。
静态的类数据成员的定义具有外部链接。如
class A { static int a;//声明。具有内部链接。 };
静态数据成员a仅仅是一个声明,但是它的定义A::a=0;却具有外部链接。
C++对类和枚举类型的处理方式是不一样的。比如:在不定义类时可以声明一个类。但是不能未经定义就声明一个枚举类型。
基于以上的分析,我们可以知道:将具有外部链接的定义放在头文件中几乎都是编程错误。因为如果该头文件中被多个源文件包含,那么就会存在多个定义,链接时就会出错。
在头文件中放置内部链接的定义却是合法的,但不推荐使用的。因为头文件被包含到多个源文件中时,不仅仅会污染全局命名空间,而且会在每个编译单元中有自己的实体存在。大量消耗内存空间,还会影响机器性能。
const和static修饰的全局变量仅仅在当前文件作用域内有效。它们具有内部链接属性。
下面列出一些应该或是不应该写入头文件的定义:
//test.h #ifndef TEST_H #define TEST_H int a; //a有外部链接,不能在头文件中定义。 extern int b=10;//同上。 const int c=2;//c具有内部链接,可以定在头文件中但应该避免。 static int d=3;//同上。 static void func(){} //同上。 void func2(){} //同a。 void func3();//可以。仅仅是声明。并不会导致符号名被写入目标文件。 class A { public: static int e;//可以,具有内部链接。 int f;//可以,同上。 void func4();//声明,内部链接。同上。 }; A::e=10;//不可以在头文件中包含具有外部链接的定义。符号名别写入目标文件。 void A:func4()//不可以,类成员函数。外部连接。 { //,...... } #endif
相信大家现在明白为什么只在类型声明成员函数,而不实现它是合法的了。也可以回答为什么类的定义可以放在.h文件中。而类的实现可以放在同名的cpp文件中。老师以前的介绍是说编译器会自动寻找同名的cpp文件。其实是因为由于cpp文件中存储的是成员函数的实现,而成员函数具有外部链接特性,会在目标文件产生符号。在此文件中此符号是定义过的。其他调用此成员函数的目标文件也会产生一个未定的符号。两目标文件连接后此符号就被解析。注意static数据成员应该放在cpp文件中。而不能放在.h文件。
有内部链接的定义可以定义在cpp文件中,并不会影响全局的符号空间 。但是在cpp文件作用域中要避免定义(并不禁止)没有声明为静态的数据和函数,因为它们具有外部链接。
如
int a; void func() { ...... }
上述定义具有外部链接可能会与全局命名空间的其他符号名称存在潜在冲突。如果确实需要使用全局的变量或函数。可以为它们加上static关键字。使其作用域局限在当前文件内,具有内部链接也就不会对全局命名空间产生影响。因为内联函数和静态自由函数、枚举以及const类型的数据都具有内部链接,所以它们可以定义在cpp文件中,而不会影响全局命名空间。
typedef和宏定义不会将符号引入.o文件,它们也可以出现在cpp文件中,不会影响全局命名空间。
- 上一篇:C语言入门之指针用法教程
- 下一篇:C++中引用的区别分析