目录
C++的 strlen() 被包含在 cstring 头文件中
1前言
本系列文章作为笔者学习C++的一个记录,希望可以坚持下去。目前使用C++Primer Plus这本书。
本系列文章适合有C语言基础的读者阅读。
2学习内容
-
using编译指令与名称空间
using namespace std; //名称空间编译指令
使用名称空间编译指令来使iostream中的定义对程序可用
名称空间是为了便于组织大型的程序以及移植他人代码时避免函数重名等造成的冲突。
如:现在有两个Init()函数,但是他们属于不同的命名空间时(例如HHH和NNN),全称不同,所以不会造成冲突。全称为:
HHH::Init();
NNN::Init();
当然,可以选择不using名称空间,而直接使用函数的全称。
书中推荐了更好的方法是,不要声明使用整个名称空间,而是需要什么用什么,如下:
using std::cout;
using std::endl; //书中推荐,需要什么用什么
using namespace std;//偷懒做法,使用整个名称空间
-
cout
在C++中,将输入和输出都看作一种流。cout对象表示一种输出流,其对象属性包括一个插入运算符<<,可以将右侧的信息插入到流中。
cout << "NNN"; //将字符串"NNN"插入到输出流中
PS:<< 也属于一种运算符的重载
控制符endl
类似于C语言中的\n换行符,不同的是\n应该被包含在字符串中,而endl使用cout<<插入到输出流中。
cout << endl;
控制符dec、hex、oct
改变cout显示整数的方式,在变量插入流前先于变量插入。
变量
也使用cout<<插入到输出流中。
-
赋值
从右往左依次赋值
a = b = c = 5;
-
cin
将输入看成流向程序的字符流。
cin >> nnn; //输入将流入变量nnn中
-
类
类定义描述的是数据格式及其用法,对象是根据这些描述创建的特定实体。
类描述指定了可对类对象执行的所有操作。要对特定对象执行这些操作时,需要通知该对象。如<<告诉cout要“显示消息”。通知对象要执行的操作有两种方法:
- 使用类方法(本质上就是调用函数);
- 重新定义运算符(如 <<)。
-
变量
命名
变量名不要以两个下划线开头或者一个下划线加大写字母开头,这样的名称是被保留给实现的(指编译器及其使用的资源),以这种名称命名变量不会报错,但有可能导致程序潜在的错误。
以一个下划线开头的变量名被保留给实现,用作全局标识符。
bool
新增布尔类型, true 为 1 , false 为 0 。
auto
自动推断类型,常用于函数的改写以便其能处理更为复杂的类型。
auto nnn = 100; //自动声明 int 型变量,并赋值为100
-
常量
#define
替换时,预处理器寻找独立的标识符,嵌入的单词不会被替换,如:
#define CMM 220207
此时程序中的 CMM 标识符将被替换为220207,而 ICMM 并不会被替换为 I220207。
const
使用 const 限定符表示常量更好,一是可以明确指定类型,二是可以限制其作用域。
使用 const 时,常量标识符首字母一般大写。
-
成员函数 cout.put()
成员函数归类所有,描述了操纵类数据的方法,
在此例子中,类 ostream 有一个 put() 成员函数,只能通过类的特定对象 cout 来使用成员函数。将对象名和函数名称连接起来:cout.put() ,这里的 . 称为成员运算符
-
cout.setf()
使用下面的代码强制输出使用定点表示法,防止程序把较大的值切换为 E 表示法,并且它可以使输出显示到小数点后 6 位。
括号内的参数是 iostream 提供的常量。
cout.setf(ios_base::fixed, ios_base::floatfield);
如果编译器不支持 ios_base,请使用 ios。
-
类型转换
一些注意
- 参数传递给函数时,将对参数以函数参数类型声明为标准进行转换;
- 浮点数转换为整型时,小数部分会被截掉。
使用{ } 进行列表初始化时进行的转换(C++11)
列表初始化不允许缩窄,也就是“大转小”是不被允许的,例如 浮点型转换为整型;
而整型之间的转换或者整型转换为浮点型可能可以,只要不发生精度损失。
使用 static_cast<> 进行类型转换
相比于传统的 (int)a 或者 int(a) ,static_case<int>(a)更严格。
-
C++的 strlen() 被包含在 cstring 头文件中
-
读取一行字符串输入
忽略空格,以换行符作为结束标志。
使用 istream 中的类成员函数:getline() 和 get()。
getline()
使用 cin.getline() 使用这种方法。该函数有两个参数,第一个是字符串存储的数组位置,第二个是要读取的字符数目。如果第二个参数为20,那么该函数最多可以读取19个字符,剩下的空间用来存储结尾的空字符 '\0'。
该函数有两种结束的情况:
- 遇到换行符(遇到换行符后换行符将从输入队列中被丢弃,连续使用两次 getline() 时,第二个个 getline() 将不会遇到换行符);
- 字符数量超过指定的读取数量。
注意,该函数不保存换行符,这是它和下面的 get() 函数的最大区别。
get()
get() 有多种变体,当它拥有和 getline() 相同的两个参数时,它的行为与 getline() 类似,此时 cin.get() 在遇到换行符时停止,但是它会把换行符留在输入队列中而不丢弃换行符,这导致了连续两次调用 get() 时,第二个 get() 将读取到输入队列中的换行符,此时 get() 认为已经到达行尾,这导致连续使用两次带两个参数的 get() 时,get() 无法跨过该换行符。如下:
cin.get(carray1, 20);
cin.get(carray2, 20);
此时 carray2 将为空数组。
解决方法:
使用不带参数的 get(),这是 get() 的另一种变体(函数重载),它将读取换行符,从而使换行符在输入队列中消失。
cin.get(carray1, 20);
cin.get();
cin.get(carray2, 20);
这样可以正确读取输入的两个字符串。
也可以使用下面的写法,之所以可以这样做,是由于 cin.get(carray1, 20) 返回一个 cin 对象,该对象随后被用来调用 get() 函数。
cin.get(carray1, 20).get();
cin.get(carray2, 20);
读取空行和其他的问题
读取空行时,get() 将在读取空行后将设置失效位(failbit),接下来的输入将被阻断,但是可以使用 cin.clear() 来恢复输入。
当输入的字符串比分配的空间长时,剩下的字符将被留在输入队列中,而 getline 会设置失效位,关闭后面的输入。
读取一行字符串输入总结
可以看到,使用 get() 比使用 getline() 要写更多的程序,但是这样繁琐的操作也有一定的好处。当使用 getline() 时,停止读取可能是由于已经整行读取或者数组已经被填满,但具体是哪一种原因不得而知;而使用 get() 时,当停止读取,只需要查看下一个输入字符,如果是换行符,说明已经读取了整行,否则就说明是数组已满,该行中还有其他输入。