C++中的“include”,“namespace”以及“前置声明
1.include
“#include***”表⽰包含C/C++的头⽂件。其包含的指令不仅仅限于“.h”的头⽂件,还可以包含任何编译器能识别的C/C++代码⽂件,包
括“.c”,“.cpp”,“.txt”,“.abc”都可以。张瑶老公
我们注意到include后⾯有两种表⽰形式,⼀个是“#include<***>”,另⼀个是“#include"***"”。
“<***>”引⽤的是编译器的类库路径⾥⾯的头⽂件;
““***””引⽤的是程序⽬录的相对路径中的头⽂件,在程序⽬录的相对路径中不到该头⽂件时会继续在类库路径⾥搜寻该头⽂件。
但⼀般⽽⾔,我们在编程的时候,⼀般⽤“***”来include⾃⼰定义的头⽂件。
2.namespace
命名空间的定义格式为:
named-namespace-definition:
namespace identifier { namespace-body }
unnamed-namespace-definition:
namespace { namespace-body }
namespace-body:
declaration-seqopt
namespace实际上就是⼀个由程序设计者命名的内存区域,程序设计者可以根据需要指定⼀些有名字的空间域,把⼀些全局实体分别放在各个命名空间中,从⽽与其他全局实体区分开来。
从上⾯⼀段从标准⽂档中摘取的代码可以看到,namespace可以声明为有名的命名空间,也可以声明为⽆名的命名空间。
所谓的C++中的namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于⼀个为std的namespace中。
具体⽤法可以通过下⾯的代码来阐明(来源:)
#include <iostream>
using namespace std;
// first name space
namespace first_space{
void func(){
cout << "Inside first_space" << endl;
}
}
// second name space
namespace second_space{
void func(){
cout << "Inside second_space" << endl;
}
}
using namespace first_space;
int main () {
// This calls function from first name space.
func();
return 0;
}
运⾏结果可想⽽知。我们在两个不同的namespace⾥⾯定义了名字相同的函数,main函数中使⽤了第⼀个命名空间,故调⽤的是第⼀个namespace中的fun()
3.前置声明(该部分转载⾃【作者:清林,博客名:飞空静渡】)
定义⼀个类 class A,这个类⾥⾯使⽤了类B的对象b,然后定义了⼀个类B,⾥⾯也包含了⼀个类A的对象a,就成了这样:
//a.h
#include "b.h"
class A
{
....
private:
B b;
};
//b.h
#include "a.h"
class B
{
....
private:
A a;
};
⼀编译,就出现了⼀个互包含的问题了,这时就有⼈跳出来说,这个问题的解决办法可以这样,在a.h⽂件中声明类B,然后使⽤B的指针:
//a.h
//#include "b.h"
class B;
class A
{
....
private:
B b;
};
//b.h
#include "a.h"
class B
{
....
private:
A a;
};
然后,问题就解决了。
但是,有⼈知道问题是为什么就被解决的吗,也就是说,加了个前置声明为什么就解决了这样的问题。下⾯,让我来探讨⼀下这个前置声明。
类的前置声明是有许多的好处的。
我们使⽤前置声明的⼀个好处是,从上⾯看到,当我们在类A使⽤类B的前置声明时,我们修改类B时,只需要重新编译类B,⽽不需要重新编译a.h的(当然,在真正使⽤类B时,必须包含b.h)。
另外⼀个好处是减⼩类A的⼤⼩,上⾯的代码没有体现,那么我们来看下:
//a.h
class B;
class A
{
....
private:
B *b;
....
};
//b.h
class B
{
....
private:
int a;
int b;
int c;
};
我们看上⾯的代码,类B的⼤⼩是12(在32位机⼦上)。
如果我们在类A中包含的是B的对象,那么类A的⼤⼩就是12(假设没有其它成员变量和虚函数)。如果包含的是类B的指针*b变量,那么类A的⼤⼩就是4,所以这样是可以减少类A的⼤⼩的,特别是对于在STL的容器⾥包含的是类的对象⽽不是指针的时候,这个就特别有⽤了。
在前置声明时,我们只能使⽤的就是类的指针和引⽤(因为引⽤也是居于指针的实现的)。
那么,我问你⼀个问题,为什么我们前置声明时,只能使⽤类型的指针和引⽤呢?
如果你回答到:那是因为指针是固定⼤⼩,并且可以表⽰任意的类型,那么可以给你80分了。为什么
只有80分,因为还没有完全回答到。想要更详细的答案,我们看下⾯这个类:
class A
{
public:
A(int a):_a(a),_b(_a){} // _b is new add
int get_a() const {return _a;}
int get_b() const {return _b;} // new add
private:
int _b; // new add
int _a;
};
我们看下上⾯定义的这个类A,其中_b变量和get_b()函数是新增加进这个类的。
那么我问你,在增加进_b变量和get_b()成员函数后这个类发⽣了什么改变,思考⼀下再回答。
好了,我们来列举这些改变:
第⼀个改变当然是增加了_b变量和get_b()成员函数;
第⼆个改变是这个类的⼤⼩改变了,原来是4,现在是8。
第三个改变是成员_a的偏移地址改变了,原来相对于类的偏移是0,现在是4了。
上⾯的改变都是我们显式的、看得到的改变。还有⼀个隐藏的改变,想想是什么。。。
这个隐藏的改变是类A的默认构造函数和默认拷贝构造函数发⽣了改变。
由上⾯的改变可以看到,任何调⽤类A的成员变量或成员函数的⾏为都需要改变,因此,我们的a.h需要重新编译。
如果我们的b.h是这样的:
//b.h
#include "a.h"
class B
{
...
private:
A a;
};
那么我们的b.h也需要重新编译。
如果是这样的:
//b.h
冯德伦的个人资料简介class A;
class B
电信查流量
{
...
private:
A *a;
};
那么我们的b.h就不需要重新编译。
像我们这样前置声明类A:
class A;
任嘉伦婚纱照是⼀种不完整的声明,只要类B中没有执⾏需要了解类A的⼤⼩或者成员的操作,则这样的不完整声明允许声明指向A的指针和引⽤。
张凯丽个人资料⽽在前⼀个代码中的语句
A a;
祝福教师节的话是需要了解A的⼤⼩的,不然是不可能知道如果给类B分配内存⼤⼩的,因此不完整的前置声明就不⾏,必须要包含a.h来获得类A的⼤⼩,同时也要重新编译类B。
再回到前⾯的问题,使⽤前置声明只允许的声明是指针或引⽤的⼀个原因是只要这个声明没有执⾏需要了解类A的⼤⼩或者成员的操作就可以了,所以声明成指针或引⽤是没有执⾏需要了解类A的⼤⼩或者成员的操作的。