C++ Primer第三章!
#include "stdafx.h"
#include<iostream>
#include<vector> //vector可以容纳着其他对象,简称容器。
#include<string> //string表示可变长的字符序列。
#include<algorithm> //泛型算法!
using namespace std;
int main()
{
//定义和初始化string
string s1;
string s2 = s1; //拷贝初始化
string s3 = "hello world"; //c风格字符串
string s4(10, 'a'); //构造函数!
string s5(s1); //直接初始化
//string包含的方法
cout << s3 << endl;
cin >> s2>> s1; //string自动忽略开头空白(空格符、换行、制表),直到遇见下一处空白
getline(cin, s1); //遇见换行符就结束操作,触发返回的换行符实际被丢弃
//s.empty(); s为空则返回true,否则返回false
//s.size(); 返回s中字符的个数,string::size_type为无符号类型,足够放下任何string对象的大小。
//s[n]; 返回第N个字符的引用
//s1+s2; s1和s2连接,也可以将字面值与string相加,但必须确保两侧至少有一个string对象。(在c++中,字面值字符串为字符指针,而与string不同类型)
//s1=s2;
//s1 == s2; 长度和字符相等则相等!
//s1!=s2;
//<,<=,>,>=; 1、长度,2、第一对相异字符。
//cctype头文件,针对字符操作!
//isalnum(c) 当c是字母或数字为真。
//isalpha(c) 字母
//iscntrl(c) 控制字符
//isdigit(c) 数字
//isgraph(c) 不是空格但可打印
//islower(c) 小写字母
//isprint(c) 可打印(空格和可视形式)
//ispunct(c) 标点符号(不是控制、数字、字母、可打印空白)
//isspace(c) 空白(空格,制表符、回车符、换行符、进纸符)
//isupper(c) 大写字母
//isxdigit(c) 十六进制数字
//tolower(c) 大写转小写
//toupper(c) 小写转大写
//范围for
for (auto &c : s3) //c是一个引用,将s3字符串全部改为大写字符串。
c=toupper(c);
cout << s3 << endl;
//string可以使用下标运算符,建议设置string下标的类型为string:size_type(能确保下标不会小于0),而下标值要小于size().
const string hexdigits = "0123456789ABCDEF";
cout << "Enter a series of numbers between 0 and 15:";
string result;
string::size_type n;
cin >> n;
result = hexdigits[n]; // 建议对下标进行检查,必须确保下标合法。
cout << result << endl;
//标准库类型vector,容器
vector<int> ivec;
vector<string> svec1;
vector<string> svec2(svec1);
vector<string> svec3 = svec1;
vector<string> svec4(10, "love");
vector<string> svec5 = { "hello","world","!" };
vector<string> svec6{ "hello","world","!" }; //提供初始元素值的列表,只能使用花括号,列表初始化。
//向vector对象中添加元素
svec6.push_back(" hello c++!");
//其他vector操作
//v.empty(); 如果v中不含任何元素,则返回真。
//v.size(); 返回v中元素个数,类型为vector<内置类型>::size_type。
//v.push_back(t); 尾端插入元素
//v[n]; 下标运算符,对v中第N个位置上元素的引用,而试图访问不存在元素将发生溢出错误。另外,不能使用下标添加元素。
//v1=v2; 拷贝
//v1={...} 列表初始化
//v1 == v2; 元素数量,对应位置元素值
//v1!=v2;
//<,<=,>,>=;
//迭代器,所有标准库容器都可以使用迭代器,但是只有少数才同时支持下标运算符。
auto b = svec6.begin(), e = svec6.end(); //end返回尾后迭代器,如果容器为空,则都为尾后迭代器。
//迭代器运算符
//*iter 解引用迭代器,并返回迭代器所指元素的引用
//iter->mem 解引用并返回该元素的mem成员,等价于(*iter).mem
//++iter 令迭代器iter指向下一元素
//--iter
//iter1==iter2 两个迭代器是否指向相同元素?
//iter1!=iter2
string siter("hello world");//string也有迭代器操作!
if (siter.begin() != siter.end())
{
auto it = siter.begin();
*it = toupper(*it);
}
cout << siter << endl;
for (auto it = siter.begin(); it != siter.end() && !isspace(*it); ++it)
*it = toupper(*it);
cout << siter << endl;
//迭代器类型
vector<int>::const_iterator it1; //迭代器分为iterator和const_iterator,const类型只能读元素,不能写元素。
vector<int>::iterator it2;
//如果对象只读而不写则应该定义为const_iterator类型
auto it_iterator = svec6.cbegin();
//迭代器运算
//iter+n 加上数值还是迭代器
//iter-n;
//iter += n;
//iter-=n;
//iter1-iter2; 返回类型为difference_type,带符号整型。
//>,>=,<,<=;
//二分法搜索
vector<int> text_num{ 3,1,2,4,5,6,9,8,7 };
int find_num = 9;
sort(text_num.begin(), text_num.end());
auto beg = text_num.cbegin();
auto end = text_num.cend();
auto mid = text_num.cbegin() + (end - beg) / 2;
while(mid != end)
{
if (*mid == find_num)
{
cout << "Find numbers!" << endl; // 二分法搜索,如果找到数值就输出find numbers!
break;
}
if (find_num < *mid)
{
end = mid;
}
else
{
beg = mid + 1;
}
mid = beg + (end - beg) / 2;
}
//数组,是一种类似于标准库类型vector,数组大小固定!
int arr1[10]; //含有10个整数的数组
int *parr1[10]; //含有10个整型指针的数组。int (*p)[10]指向含有10个整数数组的指针!主要应用于指向二维数组!
int arr2[] = { 1,2,3 };
int arr3[5] = { 1,2,3 };
char a1[] = { 'c','c' };
char a2[] = "cc";
//char a3[2]="cc"; 没有足够的空间放下'\0'
//int arr4 = arr2; 不能用一个数组初始化令一个数组。数组会自动转为首元素指针!
//arr1=arr2; 不能把一个数组直接赋值给另一个数组
int (*parr2)[10]= &arr1; //parr2指向一个包含10个整数的数组。
int (&parr3)[10] = arr1; //parr3引用一个含有10个整数的数组。
int *(&parr4)[10] = parr1; //parr4是数组的引用,该数组包含10个指针
//默认情况,类型修饰符从右至左依次绑定(而括号则改为由内向外),数组必须指定类型,不允许用auto推断类型!
constexpr unsigned sz = 10;
string strs[sz]; //sz必须为常量表达式!
for (auto i : arr2)
{
cout << i << " "; //arr2数组也支持下标运算符,下标运算符类型为size_t,无符号类型。建议检查下标的值,避免缓冲区溢出错误。
}
cout << endl;
//指针和数组关系密切,在c++中,编译时会将数组转换成指针。
string nums[] = { "one","two","three" };
string *p1_nums = &nums[0];
string *p2_nums = nums; //p1等价于p2
//数组有,标准库函数begin和end。类似于容器的迭代器操作!
int ia[] = { 0,1,2,3,4,5,6,7,8,9 };
int *beg_ia = begin(ia);
int *end_ia = std::end(ia); //由于前文定义了迭代器变量end,因此此处只有使用作用域符!(避免使用c++内置声明符)
//指针也是迭代器,指针相减返回类型ptrdiff_t,带符号类型,定义在头文件cstddef中。
//c++支持c风格字符串,也定义了c_str等转换函数,但是最好不要用c风格字符串!
//使用指针和数组容易出错,推荐使用vector和迭代器,还有string!
//多维数组,是数组的数组!
int ia_multi1[3][4]; //大小为3的数组,每个元素是含有4个整数的数组。
int ia_mulit2[3][4] = { 0 }; //将所有元素初始化为0
int ia_mulit3[3][4] = { {1,2,3,4},{5,6,7,8} };
int ia_mulit4[3][4] = { 1,2,3,4,5,6,7,8 };
int(&row)[4] = ia_multi1[1]; //把row绑定到ia的第二个4元素数组上
decltype(ia_multi1) ia_mulit5; //decltype返回类型是多维数组,而不是指针!auto推断为指针,所以数组嵌套for外层必须为引用!
//范围for处理多维数组,也可以用for嵌套
for (const auto &row : ia_mulit3) //除了最内层循环,其他所有循环的控制变量都应该是引用!如果非引用auto &row=ia_mulit3,则auto将自动推断为指针!
{
for (auto col : row)
cout << col << "-";
cout << endl;
}
//指针和多维数组
int(*p_mulit2)[4] = ia_mulit3; //*p_mulit2为指针,指向含有4个整数的数组
int *ip[4]; //4个指向整型对象的数组!
using int_arry = int[4]; //类型别名
int_arry *ia_mulit6 = ia_mulit3; //等价于*p_mulit2
//for循环,使用auto,可以不用在数组前面加指针类型声明!
for (auto p = ia_mulit2; p != ia_mulit2 + 3; ++p)
{
for (auto q = *p; q != *p + 4; ++q)
{
cout << *q << " ";
}
cout << endl;
}
//使用begin和end函数,更加简洁易懂
for (auto p = begin(ia_mulit2); p != std::end(ia_mulit2); ++p)
{
for (auto q = begin(*p); q != std::end(*p); ++q)
{
cout << *q << "+";
}
cout << endl;
}
cin.ignore();
return 0;
}
//1、使用拷贝初始化时,只能提供一个初始值;2、如果类提供了类内初始值,则只能用拷贝初始化或者列表初始化(直接和拷贝);3、如果容器提供初始元素值的列表,则只能用列表初始化。
//1、不能在范围for循环中向vector对象添加元素,另一个限制是任何可能改变vector对象容量的操作,如push_back,都会使vector对象的迭代器失效。
//第三章主要介绍vector,string,数组,顺带提到了C++对于C的支持(C风格字符串,及相互转换方法)。在介绍vector和string时引出了迭代器的概念,讲解了其基本用法。在介绍数组时引出了指针的概念和用法,同时穿插讲解了auto、decltype、范围for等等!作者重点提到,推荐使用vector、string、迭代器,避免繁琐和错误!