C++被称为“完美的程序设计语言”,在chromium内核中应用非常广泛,之前没有系统学习过C++相关的知识,通过看书来学习相关的知识,现在将《C++ Primer》基本知识提取出来,供大家学习。
1.输入输出
2.变量与基本类型2.1 引用与指针
2.1.1 获取对象地址
2.1.2 指针值
2.1.3 利用指针访问对象
2.1.4 空指针
2.1.5 void* 指针2.2 const符号
2.2.1 constexpr
2.3 处理类型
2.3.1 类型别名
2.3.2 auto类型说明
2.3.3 decltype类型指示符2.4 结构体
3.字符串、向量、数组
3.1 string标准库
3.1.1 初始化string对象
3.1.2 string执行操作
3.1.3 string中字符处理操作3.2 vector标准库
3.2.1 向vector添加元素
3.2.2 其他vector操作3.3 迭代器
3.3.1 迭代器运算
3.4 数组
3.4.1 指针和数组
1.输入输出
标准输入输出是一个语言的重要功能。
cin是C++中的标准输入流对象,主要用于从标准输入读取数据,这里的标准输入,是指键盘。
cout是输入流对象,即ostream类的对象。
在理解cin功能时,不得不提标准输入缓冲区。当我们从键盘输入字符串的时候需要敲一下回车键才能够将这个字符串送入到缓冲区中,那么敲入的这个回车键(\r)会被转换为一个换行符\n,这个换行符\n也会被存储在cin的缓冲区中并且被当成一个字符来计算!比如我们在键盘上敲下了123456这个字符串,然后敲一下回车键(\r)将这个字符串送入了缓冲区中,那么此时缓冲区中的字节个数是7 ,而不是6。
下面看下简单的例子:
#include
using namespace std;
int main() {
cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
double val;
cin >> val;
cout << "The val is " << val << endl;
return 0;
}
输出如下:
jeffmony@jeffmony-OptiPlex-7050:~/workspace/cppProjects/test$ ./a.out
!!!Hello World!!!
12.3
The val is 12.3
2.变量与基本类型
2.1 引用与指针
C++中有两种复合类型:引用与指针。
#include
using namespace std;
int main() {
int ival = 1024;
int &refVal = ival;
cout << "refVal is " << refVal <
输出的结果如下:
jeffmony@jeffmony-OptiPlex-7050:~/workspace/cppProjects/test$ ./a.out
refVal is 1024
注意:一般在初始化变量时,初始值会被拷贝到新建的对象中。然而定义引用时,程序把引用和它的初始值绑定在一起,而不是讲初始值拷贝给引用。一旦初始化完成,引用将和它的初始值对象一直绑定在一起。因为无法令引用重新绑定到另外一个对象,因此引用必须初始化。
指针是指向另外一种类型的复合类型。与引用类似,指针也实现了对其他对象的间接访问。与引用不同点:
1.指针本身就是一个对象,允许对指针赋值与拷贝,而且在指针的生命周期内可以指向几个不同的对象;
2.指针无须在定义时赋初始值,块作用域内定义的指针如果没有初始化,将拥有一个不确定的值。
2.1.1 获取对象地址
指针存放某个对象的地址,要想获取该地址,需要使用取地址符(& )
2.1.2 指针值
指针的值会取如下的四种情况:
1.指向一个对象
2.指向紧邻对象所占空间的下一个位置
3.空指针,指针没有指向任何对象
4.无效指针
2.1.3 利用指针访问对象
2.1.4 空指针
空指针不指向任何对象,在使用一个指针之前要判断一下当前指针是否为nullptr
2.1.5 void* 指针
void* 是一种特殊的指针类型,可以用于存放任意对象的地址。一个void*指针存放着一个地址,这一点和其他的指针类似。但是我们不知道改地址中到底是个什么类型的对象,所以操作的时候要十分小心,基本上也不能进行有效操作。
2.2 const符号
1.const用来定义常量,一旦定义了,就不能改变。
2.const默认情况下仅在文件内有效,如果想在多个文件之间共享const对象,必须在变量的定义之前添加extern关键字。
#include
using namespace std;
int main() {
int i = 42;
const int &r1 = i;
cout << "result is " << r1 << endl;
i++;
cout << "result is " << r1 << endl;
return 0;
}
r1绑定了对象i,但是不允许通过r1修改对象i的值,但是对象i值改变的时候会反映到r1上,这点要记住哦。
2.2.1 constexpr
constexpr是常量表达式,是指值不会改变并且在编译过程就能得到计算结果的表达式。
constexpr int mf = 30;
constexpr int limit = mf + 1;
2.3 处理类型
2.3.1 类型别名
传统的使用typdef,例如:
typedef double wages;
新标准使用using来“别名声明”定义别名:
using SI = Sales_item;
2.3.2 auto类型说明
编程时常常需要把表达式的值赋给变量,这就要求在声明变量的时候清楚地知道表达式的类型,但是有的时候做不到这一点。
C++11引入了auto类型说明符,用它就能让编译器替我们去分析表达式所属的类型。
auto item = val1 +val2; // item初始化为val1 val2相加的结果
2.3.3 decltype类型指示符
有时希望用表达式的类型推断出要定义的变量的类型,但是不想用该表达式的值初始化变量。
2.4 结构体
Sales_data.h
struct Sales_data {
std::string bookNo;
unsigned unints_sold = 0;
double revenue = 0.0;
};
#include
#include "Sales_data.h"
using namespace std;
int main() {
Sales_data data1,data2;
double price = 0.0;
cin >> data1.bookNo >> data1.unints_sold >> price;
data1.revenue = price * data1.unints_sold;
cin >> data2.bookNo >> data2.unints_sold >> price;
data2.revenue = price * data2.unints_sold;
cout << data1.bookNo << ", " << data2.bookNo <
输出的结果如下:
jeffmony@jeffmony-OptiPlex-7050:~/workspace/cppProjects/test$ ./a.out
0-201-78345-X 3 20.00
0-201-78435-P 2 15.00
0-201-78345-X, 0-201-78435-P
3.字符串、向量、数组
3.1 string标准库
标准库string表示可变长的字符序列,使用string必须包括string头文件:
#include
using std::string;
3.1.1 初始化string对象
string s1;
string s2 = s1;
string s3 = "hello";
string s4(s3); //等同于s4 = s3;
string s5("hello"); //等同于s5 = "hello";
string s6(10, 'c'); //把s6初始化为10个字符c组成的串
3.1.2 string执行操作
操作语句 | 含义 |
---|---|
os << s | 将s写到输出流中,返回os |
is >> s | 从is中读取字符串赋给s,字符串以空白分隔,返回is |
getline(is, s) | 从is中读取一行赋给s,返回is |
s.empty() | s为空返回true,否则返回false |
s.size() | 返回s中字符的个数 |
s[n] | 返回s中第n个字符的引用,位置n从0计算 |
s1 + s2 | 返回s1和s2连接后的结果 |
s1 = s2 | 用s2的副本代替s1中原来的字符 |
s1 == s2 | 如果s1包含的字符和s2完全一样,则相等;相等判断对大小写敏感 |
s1 != s2 | 不等性判断 |
< , <= , > , >= | 利用字符在字典序中的顺序进行比较,对大小写敏感 |
标准输入cin如果输入一个string对象,string对象会自动忽略开头的空白(空格符、换行符、制表符),并从第一个真正的字符开始读起,直到遇见下一处空白为止。
#include
using namespace std;
int main() {
string s;
cin >> s;
cout << s << endl;
return 0;
}
输出结果如下:
jeffmony@jeffmony-OptiPlex-7050:~/workspace/cppProjects/test$ ./a.out
hello, world;
hello,
可以很明显的看到cin的主要区别。
使用getline读取一整行,我们希望在最终得到的字符串中保留输入时的空白符,这时应该用getline函数替代原来的>>运算符,getline直到遇到一行换行符为止。getline一遇到换行符就结束读取操作并返回结果。
注意:触发getline函数返回的那个换行符实际上被丢弃掉了,得到的string对象并不包括该换行符。
#include
using namespace std;
int main() {
string line;
while(getline(cin, line)) {
cout << line << endl;
}
return 0;
}
输出结果如下:
jeffmony@jeffmony-OptiPlex-7050:~/workspace/cppProjects/test$ ./a.out
hello, world;
hello, world;
^C
字符串中+操作不能将两个字面值相加,不然会产生错误。下面的例子就是因为s3中有两个字符串的字面值相加了,所以导致编译错误。
#include
using namespace std;
int main() {
string s1 = "hello";
string s2 = "world";
string s3 = "hello" + "," + "world";
return 0;
}
jeffmony@jeffmony-OptiPlex-7050:~/workspace/cppProjects/test$ g++ helloworld.cpp
helloworld.cpp: In function ‘int main()’:
helloworld.cpp:7:22: error: invalid operands of types ‘const char [6]’ and ‘const char [2]’ to binary ‘operator+’
string s3 = "hello" + "," + "world";
~~~~~~~~^~~~~
3.1.3 string中字符处理操作
cctype头文件中有一些操作字符的库函数,和C语言中ctype.h头文件的内容是一样的,cctype头文件从明明规范上更加符合C++语言的要求。
字符串遍历操作:
#include
using namespace std;
int main() {
string s1 = "hello";
for(auto c : s1) {
cout << c << endl;
}
return 0;
}
输出结果如下:
jeffmony@jeffmony-OptiPlex-7050:~/workspace/cppProjects/test$ ./a.out
h
e
l
l
o
C++中string可以默认当成数组,可以使用下标,但是下标范围必须在0和s.size() 之间,在范围之外会产生未知的影响。
3.2 vector标准库
标准库vector表示对象的集合,其中所有的类型都相同,集合中每个对象都有一个与之对应的索引,索引用于访问对象,vector被成为容器。使用vector需要加上头文件。
#include
using std::vector;
vector能容纳绝大多数类型的对象作为其元素,引用不是对象,所以不存在包含引用的vector。
表达式 | 含义 |
---|---|
vector |
v1是一个空vector,它潜在的元素是T类型的,执行默认初始化操作 |
vector |
v2中包含有v1所有元素的副本 |
vector |
等价于v2(v1),v2中包含有v1所有元素的副本 |
vector |
v3包含了n个重复的元素,每个元素都是val |
vector |
v4包含了n个重复地执行了值初始化操作的对象 |
vector |
v5包含了初始值个数的元素,每个元素被赋予相应的初始值 |
vector |
等价于v5{a,b,c,...} |
3.2.1 向vector添加元素
使用push_back函数向vector中添加元素,看看下面的例子:
#include
#include
using namespace std;
int main() {
vector vec;
for(int i=0;i<100;i++) {
vec.push_back(i);
}
cout << vec.size() << endl;
return 0;
}
输出结果如下:
jeffmony@jeffmony-OptiPlex-7050:~/workspace/cppProjects/test$ ./a.out
100
一般情况下,我们可以在初始化vector的时候不初始化vector的大小,因为vector会动态扩张,但是vector的动态扩张比较耗时,如果在知道vector大小的情况下,建议还是直接在初始化的时候定义vector大小较好,这样减少之后的扩张操作。Java中的ArrayList和HashMap也是同样的思路。
3.2.2 其他vector操作
表达式 | 含义 |
---|---|
v.empty() | 如果v不含有任何缘故;返回真;否则返回假 |
v.push_back(t) | 向v的尾端添加一个值为t的元素 |
v[n] | 返回v中第n个位置上的元素的引用 |
v1 = v2 | 替换 |
v1={a,b,c,...} | |
v1 == v2 | v1与v2相等当且仅当它们的元素数量相同且对应位置的元素值都相同 |
v1 != v2 | |
< , <=, >, >= | 字典序进行比较 |
3.3 迭代器
我们已知使用下标运算符来访问string对象或者vector对象,还有另外的机制也可以实现,就是迭代器,除了vector之外,还有几种标准容器,所有标准容器库都可以使用迭代器,但是只有少数几种支持下标运算符。
string对象不属于容器类型,但是string支持迭代器,vector支持下标,这点要注意了。
#include
#include
using namespace std;
int main() {
vector vec;
for(int i=0;i<10;i++) {
vec.push_back(i);
}
for(auto it = vec.begin(); it != vec.end(); it++) {
cout << *it << endl;
}
return 0;
}
输出结果如下:
0
1
2
3
4
5
6
7
8
9
C++语言中习惯使用 != 符号,而不是 < 符号,这和我们理解的Java或者其他的语言有点不同,记住就行了,因为部分标准容器库的迭代器都定义了 == 和!=,没有定义 < 符号,所以建议使用定义好的符号。
迭代器类型:
vector
string::iterator it2; // it2能对string对象中元素
vector
string::const_iterator it4; //it4只能读字符,不能写字符
对于const_iterator类型,C++11新标准还引入两个新函数,分别是cbegin 和cend,分别表示const_iterator 和const_end
但凡是使用迭代器的循环,都不要向迭代器所属的容器中添加元素,这会造成迭代器失效,这样注意一点。
3.3.1 迭代器运算
表达式 | 含义 |
---|---|
iter + n | 相对原来的位置,移动了若干个位置,这儿要注意,所以操作的iter一定要在正常的范围之内,否则发生问题。 |
iter - n | |
iter += n | |
iter -= n | |
iter1 - iter2 | |
>, >=, <, <= |
3.4 数组
数组类似与vector的数据结构。与vector不同的是:数组大小确定不变,不能随意向数组中增加元素。因为数组大大小固定,所以实时访问性能较好。
初始化数组:
int arr[10];
string str[10];
int *par[10]; //定义含有10个整型指针的数组
定义字符数组的时候,一定要记住字符数组末尾的地方有一个空字符:'\0'
char a3 = "C++"; \自动添加表示字符串结束的空字符
注意:不允许把一个数组直接赋值给另一个数组。
3.4.1 指针和数组
C++中指针和数组的关键非常密切,使用数组的时候编译器一般默认将它转换为指针。
string nums[] = {"one", "two", "three"};
string *p = &nums[0]; // p指向nums的第一个元素
string *p2 = nums; // *p2 = &nums[0],默认转化为指向数组首元素的指针。
- 指针本身也是迭代器,可以直接迭代。这一点学过C语言的应该都很清楚。
数组中的标准库函数begin 和end也用到了指针相关的。
int ia[] = {0,1,2,3,4,5,6,7,8,9,10};
int *beg = begin(ia); //指向ia首元素的指针
int *last = end(ia); //指向ia尾元素的下一个元素的指针。
#include
using namespace std;
int main() {
int arr[] = {1,2,3,4,5,-7,-9,10};
int *pbeg = begin(arr);
int *pend = end(arr);
while(pbeg != pend && *pbeg > 0) {
pbeg++;
}
cout << *pbeg << endl;
return 0;
}
找出数组中第一个为负数的数:
jeffmony@jeffmony-OptiPlex-7050:~/workspace/cppProjects/test$ ./a.out
-7
C语言中导入string.h头文件,C++中导入cstring
函数 | 含义 |
---|---|
strlen(p) | 返回p的长度,空字符不算在内 |
strcmp(p1, p2) | 比较p1与p2, p1==p2,返回0,p1>p2,返回正数,p1 |
strcat(p1, p2) | p2拼接到p1之后,返回p1 |
strcpy(p1, p2) | p2拷贝给p1, 返回p1 |