author:&Carlton
book:《C++ Primer 第五版》
tags:C++、暑期实践
topics:命名空间的using声明、指示和输入输出流及其常用标准库<iostream>
date:2023年7月9日
目录
using声明
从命名空间使用某个库函数往往需要显式地标示出来,例如:std::cin,此处使用作用域操作符(::)表示从作用域操作符左侧名字所示的作用域中寻找右侧的那个名字,即要使用命名空间std中的名字cin。
cin和cout是频繁使用的输入输出函数,如果每一次使用都要标示其命名空间会有些烦琐,因此可以使用using声明(using declaration),在开头声明一次后,从声明的位置起,到声明语句作用域结束的范围内,都不需要额外添加前缀。(即using声明的名字的作用域与using声明语句本身的作用域一致)
using namespace::name;
e.g
using std::cout;
using std::endl;
cout << "hello world" << endl
//不需要再添加前缀,写成std::cout << "hello world" << endl
注意:
每个名字都需要独立的using声明,头文件不应包含using声明(防止意外的名字冲突)
可以出现在全局作用域、局部作用域、命名空间作用域以及类的作用域中。
using指示
using namespace name;
using指示(using directive)无法控制命名空间内哪些名字可见,而是所有名字都可见。
①using指示令整个命名空间的所有内容变得有效,具有将命名空间成员提升到包含命名空间本身和using指示语句所在作用域的能力。
②而通常情况下,命名空间中会含有一些不能出现在局部作用域中的定义(因为这些定义的变量在using指示语句所在的局部作用域的最近外部作用域已经定义过,会造成二义性错误)
因此:using指示的命名空间的作用域一般被看作是在指示语句所在的这个作用域的最近外层作用域中,且排放在语句所在的作用域前头。
(具体例子请看下面案例)
注意:
可以出现在全局作用域,局部作用域和空间作用域中,但是不能出现在类的作用域里。
C++允许存在冲突,但需要做出特殊的限定避免二义性错误,需要明确指出名字的版本,使用作用域运算符。
应该避免using指示,由于它使得相应库中的名字全部变得可见
①会带来全局命名空间污染问题
②如果新版本引入一个目前正在使用的名字会起冲突
③同时其引发的二义性错误只有在使用了冲突名字的地方才能被发现,检测具有延后性,错误具有暴发性。
但在命名空间本身的实现文件可以使用正常使用using指示
平常为了使得代码更简洁,核心更突出,我们常在开头使用using namespace std来简便使用cin,cout等输入输出库函数。
案例分析
//命名空间blip
namespace blip
{
int i=16,j=15,k=23;
}
//全局作用域
int j=0; //正确:blip的j隐藏在命名空间中
//局部作用域manip
void manip()
{
//using指示,blip中的名字被”添加“到全局作用域中(一种理解方式)
using namespace blip; //如果使用了j,则将在 ::j 和 blip::j 之间产生冲突
++i; //将 blip::i 设定为17
++j; //二义性错误:是全局的j还是blip::j?
++::j; //正确:将全局的j设定为1
++blip::j; //正确:将blip::j设定为16
int k=97; //当前局部的k隐藏了blip::k
++k; //将当前局部的k设定为98
}
①函数manip的using指示使得manip的代码可以直接使用命名空间blip中的名字而不加前缀,如” ++i “
②但blip命名空间的作用域在函数manip进行using指示后扩展至本身加上manip函数的范围,在manip函数里使用其名字时可能会和manip函数本身所在的最近外作用域里定义的变量其冲突,可以从manip出发,blip作用域和最近外部作用域都是“外部的”,地位平等(如变量 j 上存在二义性矛盾:是指全局变量 j 还是命名空间blip定义的变量 j )
③存在冲突时不能省略前缀,需要严格按照限定使用名字。
④在manip定义的局部变量k可以隐藏命名空间blip中的k成员名字,因为manip作用域和blip作用域不同,主函数/最近外部作用域没有定义k。
输入输出流
iostream标准库
C++没有定义任何输入输出(IO)语句,但包含了一个标准库(standard library)来提供IO机制— —iostream库。
包含两个基础类型istream,ostream,分别表示输入流和输出流。
流(stream)的含义是:一个流是一个字符序列,随着时间的推移,字符是顺序生成或消耗的。
istream:cin(标准输入)
ostream:cout(标准输出),cerr(标准错误),clog(输出程序运行时的一般性信息)
使用#include <iostream> 调用iostream标准库
向流写入数据
std::cout << "Enter two numbers:" << std::endl
①是一个表达式,在C++中一个表达式会产生一个计算结果(也就是最后的值,不影响原表达式应有的计算过程)
② << 是输出运算符,左侧的运算对象是一个ostream对象,右侧的运算对象是要打印的值,作用是将给定的值写到给定的ostream对象中,输出运算符计算结果是其左侧运算对象,这里是std::cout
③endl是操纵符(manipulator),效果是结束当前行,并将与设备关联的缓冲区(buffer)中的内容刷到设备中。使用打印语句时“一直”刷新流可以保证程序所产生的所有输出都真正写入输出流中,而不是仅停留在内存中等待写入流,防止后续程序崩溃位置判断错误等等一系列问题。
从流读取数据
int v1=0,v2=0;
std::cin >> v1 >> v2;
>> 是输入运算符,接受一个istream对象作为其左侧运算对象,接受一个对象作为其右侧运算对象,作用是从给定的istream读入数据,并存入给定对象中,其左侧运算对象是计算结果,这里是std::cin
面向对象
std::cout << "The sum of " << v1 << "and" << v2 << "is" << v1+v2 << std::endl;
运算对象并不都是相同类型的值,有些是字符串字面值常量(string literal),有些是int值。
对此,标准库定义了不同版本的输入输出运算符,来处理这些不同类型的运算对象。
欢迎指正与分享,谢谢!