深入分析C++中声明与定义的区别

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

深⼊分析C++中声明与定义的区别⾸先谈下声明与定义的区别。

声明是将⼀个名称引⼊程序。

定义提供了⼀个实体在程序中的唯⼀描述。

声明和定义有时是同时存在的。

如int a;
extern int b=1;
只有当extern中不存在初始化式是才是声明。

其他情况既是定义也是声明。

但是在下列情况下,声明仅仅是声明:
1:仅仅提供函数原型。

如void func(int,int);
2: extern int a;
3:class A;
4:typedef声明
5:在类中定义的静态数据成员的声明
如:
class A
{
public:
static int a;//声明。

};
下列情况下,定义仅仅是定义:
1:在类定义之外,定义并初始化⼀个静态数据成员。

如 A::a=0;
2:在类外定义⾮内联成员函数。

声明仅仅是将⼀个符号引⼊到⼀个作⽤域。

⽽定义提供了⼀个实体在程序中的唯⼀描述。

在⼀个给定的定义域中重复声明⼀个符号是可以的,但是却不能重复定义,否则将会引起编译错误。

但是在类中的成员函数和静态数据成员却是例外,虽然在类内它们都是声明,但是也不能有多个。

如:
明⽩了声明与定义的区别,还需要明⽩内部链接、外部链接。

只有明⽩了它们你才会知道开头提出的问题。

在编译时,编译器只检测程序语法和函数、变量是否被声明。

如果函数未被声明,编译器会给出⼀个警告,但可以⽣成⽬标⽂件。

⽽在链接程序时,链接器会在所有的⽬标⽂件中找寻函数的实现。

如果找不到,那到就会报链接错误码(Linker Error)。

在VC下,这种错误⼀般是:Link 2001错误,意思说是说,链接器未能找到函数的实现。

链接把不同编译单元产⽣的符号联系起来。

有两种链接⽅式:内部链接和外部链接。

如果⼀个符号名对于它的编译单元来说是局部的,并且在链接时不可能与其他编译单元中的同样的名称相冲突,那个这个符号就是内部链接。

内部链接意味着对此符号的访问仅限于当前的编译单元中,对其他编译单元都是不可见的。

static关键字作⽤在全局变量时,表⽰静态全局变量。

但是作⽤域仅仅在当前⽂件作⽤域内。

其他⽂件中即使使⽤extern声明也是⽆法使⽤的。

const也类似。

带有static、const关键字和枚举类型的连接是内部的。

具有内部链接的符号⽆法作⽤于当前⽂件外部,要让其影响程序的其他部分,可以将其放在.h⽂件中。

此时在所有包含此.h⽂件的源⽂件都有⾃⼰的定义且互不影响。

类的定义具有内部链接,由于它是定义,因此在同⼀编译单元中不能重复出现。

如果需要在其他编译单元使⽤,类必须被定义在头⽂件且被其他⽂件包含。

仅仅在其他⽂件中使⽤class a;声明是不⾏的,原因就是类的定义是内部链接,不会在⽬标⽂件导出符号。

也就不会被其他单元解析它们的未定义符号。

理解这⼀点很重要。

内联函数也具有内部链接。

在⼀个多⽂件的程序中,如果⼀个符号在链接时可以和其他编译单元交互,那么这个名称就有外部链接。

外部链接意味着该定义不仅仅局限在单个编译单元中。

它可以在.o⽂件中产⽣外部符号。

可以被其他编译单元访问⽤来解析它们未定义的符
号。

因此它们在整个程序中必须是唯⼀的,否则将会导致重复定义。

⾮内联成员函数、⾮内联函数、⾮静态⾃由函数都具有外部链接。

内联函数之所有具有内部链接,因为编译器在可能的时候,会将所有对函数的调⽤替换为函数体,不将任何符号写⼊.o⽂件。

判断⼀个符号是内部链接还是外部链接的⼀个很好的⽅法就是看该符号是否被写⼊.o⽂件。

前⾯说的是定义对链接⽅式的影响,接下来说下声明对链接⽅式的影响。

由于声明只对当前编译单元有⽤,因此声明并不将任何东西写⼊.o⽂件。

如extern int a;
int func();
这些声明本⾝不会影响到.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⽂件中,不会影响全局命名空间。

typedef 为⼀个已存在的类型创建⼀个别名。

⽽不是创建⼀个新的类型。

它不提供类型安全。


typedef int IntA;
typedef int InB; 
在需要IntA的地⽅使⽤IntB是不会报错的。

它们可以互相替换。

因为此我们称它不提供类型安全。

但是在定义函数类型时typedef经常使⽤,可以使定义更清晰。

标准c库提供⼀个assert宏,⽤以保证给定的表达式值⾮零。

否则便会输出错误信息并终⽌程序执⾏。

只有在程序中没有定义NDEBUG时,assert才会⼯作。

⼀旦定义NDEBUG ,assert语句将会被忽略。

注意与VC中的ASSERT相区别。

ASSERT 是vc提供的。

当_DEBUG被定义时才会起作⽤。

在vc的DEBUG模式下_DEBUG会被定义。

⽽在RELEASE模式下NDEBUG会被定义。

好了,相信⼤家都会明⽩开头提出的问题了。

如果有不明⽩的,请务必留⾔哦。

如有错误,也请不吝指正!!
以上内容参考⾃《Large Scale C++ software design》。

相关文档
最新文档