学习c++的同学大概非常习惯在程序的开头部分加上这句:
using namespace std;
可能由于太熟悉了,反而忽略了这句到底是做什么的。这句中,一共有3个关键字,抛开第三个关键字std,我们只关注于前面的2个:using和namespace。
先说using,using一般来说有2个功能:
1 将std命名空间的内容引入本命名空间,这句话很抽象,但是简单说,就是可以让你少打几个字。原来需要std::string这样才能访问的地方,可以简化为string就可以了。当你的命名空间很长很复杂时,using显的很有用,比如:
namespace abcd
{
namespace efgh
{
namespace ijk
{
class A;
}
}
}
如果你想访问A,那么完整的写法是:abcd::efgh::ijk::A。
当然了,不得不承认,using的存在是和namespace的封装思想相违背的,所以在通常的编程规范里,严禁在头文件中using命名空间。
2 使用父类被隐藏的函数。比如:
class A
{
public:
int fun(int);
void fun();
};
class B : public A
{
public:
using A::fun;
int fun(double);
};
B b;
b.fun();//without using, error
这里面涉及c++继承的隐藏,不展开。
其实using的作用非常有限,而且通常情况下属于那种可有可无的.....
而另一个关键字namespace,就相对来说重要的多,namespace的存在,使得一些大型的项目开发变得相对容易,各个模块可以非常完美的区分开来,再也不用担心和其他人的函数或者类重名。
namespace的实现原理非常简单,在编译时,同一个命名空间的函数或者变量将在名字前增加命名空间名,比如上文中我们的class A,在编译后可能变成: abcd_efgh_ijk_A。
在自己的命名空间内有个函数,它正好和全局范围内的某个函数重名,比如:
int fun(); //global
namespace A
{
int fun()
{
::fun(); //call golbal fun
}
}
前面的这些基本上都是铺垫......
接下来才是重点....
我们在编写一个类的时候,经常会在这个类中用到其他类的一些信息,比如:
// a.h
namespace lbs
{
class A
{
int a;
...
};
}
// b.h
namespace lbs
{
class B
{
A a;
...
}
}
在上文中,如果想在类B中组合类A,那么需要在b.h中#include "a.h",因为编译器需要A的定义,而需要A定义的目的是为了确定A的大小以便在编译时分配空间。
如果在b.h中#include "a.h",那么程序的耦合度会很高,因为你将在a.h中将许多不必要的信息都添加进来,尤其是当B这个类很大时,你需要在b.h中#include非常多的类,程序将显得异常笨重,而且逻辑很混乱。
有没有解决方案呢?
顺着我们刚才的思路,编译器需要的是A的大小,那么只需要将A a,变为A* a或者A& a,就可以了。在b.h中,不再#include "a.h",而是使用一个类的声明替代,如下:
// b.h
namespace lbs
{
class A; //declare
class B
{
A* a;
...
};
}
这样之后,可以使得b.h轻便许多,程序整体的耦合度很低,可是当你需要使用一个其他命名空间的类,该如何做呢?比如下面这样:
// a.h
namespace lbs
{
namespace common
{
class A
{
int a;
...
};
}
}
// b.h
namespace lbs
{
namespace db_mm
{
class common::A;//error, common unknown
class B
{
A* a;
...
};
}
}
这种涉及不同命名空间的声明,如果按照上面的方式写,将会编译错误,提示common命名空间unknown,因为系统不知道common是一个命名空间。所以呢,需要这样写:
// b.h
namespace lbs
{
namespace common
{
class A; //declare
}
namespace db_mm
{
class B //definition
{
common::A* a;
...
};
}
}
当然了,在cpp文件中,还是需要#include所需要的所有头文件。