(2020.11.15 Sun)
C++命名空间namespace(ns)
命名空间的引入是为了避免变量或函数重名的问题。
C++中的名称(name)可以是符号常量、变量、宏、函数、结构、枚等。大规模程序设计中可能会出现标识符的命名发生冲突。解决办法是将变量定义在一个不同名字的命名空间中。
命名空间有两种:有名与无名,他们的定义格式如下
namespace 命名空间名
{声明序列}
//有名
namespace
{声明序列}
//无名
命名空间的成员可以在命名空间的定义内为其命名(内部定义),也可在命名空间外定义成员(外部定义)。但是不能再外部定义子命名空间。
namespace outer
{
int i;
namespace inner //子命名空间
{
void f() {i++;}
}
void f();
}
void outer::f() //在命名空间外部定义成员
{
i++;
}
- 命名空间只能定义在头文件中head file
- 命名空间开放,可以随时更新加入新的成员
//前面已经定义了outer
namespace outer // 给命名空间outer加入新的成员
{
int k;
void g() {
printf('g');
}
}
- 引用命名空间中的成员,使用作用域运算符::
outer::k = 1; //引用命名空间的成员并赋值时不需要指定数据类型
- 在预编译部分中会出现include <iostream>和include <iostream.h>。在新的C++标准中,生成头文件的方法仅仅是将现有头文件名中的.h去掉。以前的C++头文件名.h会继续支持,但头文件的内容不在命名空间std中。新的头文件和旧文件相同,但是头文件的内容在命名空间std(一个类)中。因此调用std,即using namespace std前需要做预处理,即#include <iostream>,才可以使用std内的函数等内容。如果不调用std,not using namespace std,则在调用cin/cout时需要使用std::cin/cout。
#include <iostream>
using namespace std; //using编译指令
int main() {
cout << 'this is a C++ p.' << endl;
return 0;
}
//or
#include <iostream>
int main() {
std::cout <<' this is also a C++ p.' << endl;
return 0;
}
//或只使用命名空间的某个成员
#include <iostream>
using outer::g; //using声明,针对命名空间中名称的使用
void main () {
g(); //调用outer的g成员
}
- 上面的例子中有using namespace outer和using outer::g两种用法,前一种称为using编译指令,后一种称为using声明。声明只把ns中特定成员的名称添加到所在的区域,使得该成员可以不需要采用命名空间的作用域解析运算符来定位,而直接被使用。但是使用using声明更安全,因其只会导入指定的名称。如果该名称与局部名称发生冲突,则编译器会报错。而using指令导入整个ns中所有成员的名称,如果其中有名称与局部名称发生冲突,编译器不会报错,只是用局部名称自动覆盖ns中的同名成员。当需要反复使用一个ns中的多个函数时,使用using编译指令;当只需要使用一个ns中的特定几个函数时,建议使用ns声明。
(2020.11.17 Tues)
命名空间和类的区别
一般程序的开发都由多人完成,为防止不同模块的类和函数重名,采用命名空间来区分避免混淆。
只要两个类在不同的命名空间中,他们就可以拥有相同的类名称。把类封装进命名空间的优势在于方便调用甚至其他应用程序的调用。
#include <iostream>
using namespace std;
namespace s1
{
class S_Class
{
public:
void show()
{
cout << '调用命名空间S1中类S_Class的函数show().' << endl;
}
};
}
namespace s2
{
class S_Class
{
public:
void show()
{
cout << '调用命名空间s2的类S_Class的函数show()' << endl;
}
};
}
int main() {
s1::S_Class x; //声明一个s1中类S_Class的实例x
x.show(); //调用类实例x中的show(),输出结果
s2::S_Class y; //声明一个s2中类S_Class的实例y
y.show(); //调用类实例y中的show(),输出结果
return 0;
}
作用域限定符Scope resolution operator ::
在三种情况中使用作用域限定符
- 在命名空间的使用过程中,使用::来引用指定命名空间中的成员。另一种引用方法是使用using指令。
- 在不同作用域内声明的变量可以重名,但如果局部变量和全局变量重名,在局部变量的作用域内可以使用::来引用全局变量。
#include <iostream>
using namespace std;
int x = 100; //定义全局变量
int main ()
{
int x = 150;
cout << '全局变量x=' << ::x << endl; //输出全局变量
cout << '局部变量x=' << x << endl; //输出局部变量
::x = 750;
cout << '全局变量x=' << ::x << endl; //输出全局变量
cout << '局部变量x=' << x << endl; //输出局部变量
return 0;
}
另外,::只能用来访问全局变量,不能访问一个在语句块外生命的同名局部变量。下面是一个错误的使用方式。
//这是一个错误的使用方式
void main() {
int x = 11; //x是局部变量
{
int x = 22;
::x = 33; //错误的修改方式,因x也是局部变量
}
}
- 在类外定义类成员函数时,需要使用scope resolution operator。比如声明一个类A,里面声明了一个成员函数void fun();,如果类中没有给出该成员定义,在类外定义该成员时,要写成void A::fun(),表示函数fun()是类A的成员函数。
#include <iostream>
#include <string.h>
class cbook
{
private:
char * m_pczname;
int m_npages;
int m_nedition;
public:
void getbookname(char *pname);
int getbookedition();
private:
void setbookname(char * pname);
void settotalpages(int npages);
public:
cbook();
}; //定义一个类和结构体的结尾处的大括号后面有个分号
void cbook::getbookname(char * pname)
{
strcpy(pname, m_pczname); //定义成员函数
}
int cbook::getbookedition()
{
return m_nedition; //成员函数中引用类成员变量,直接写名字,不需要加self
}
void cbook::setbookname(char * pname)
{
if (m_pczname != 0)
delete[] p_pczname;
m_pczname = new char[strlen(pname)+1]; //重新分配存储空间
strcpy(m_pczname,pname); //复制字符串
}
void cbook::settotalpages(int npages)
{
m_npages = npages; //成员函数中引用类成员变量,直接写名字,不需要加self
}
void main ()
{
cbook op1; //声明该类的对象
int i;
i = op1.getbookedition();
cout << i << endl;
}
这里注意到,类说明的程序内容较多,应该放在独立的文件中。例如cbook的类说明可以放在头文件xxx.h中,类的定义放在以.cpp为扩展名的文件中,称为类的实现文件。在文件开始部分应该用include将类说明文件包含进来(类实现可以不include?)
Reference
1 刘蕾编著,21天学通C++,中国工信出版集团,电子工业出版社
2 聚慕课教育研发中心编著,C++从入门到项目实践(超值版),清华大学出版社
网友评论