命名空间的using声明
作用域操作符::
的含义是:编译器从操作符左侧名字所示的作用域中寻找右侧那个名字。
//有using声明,就无须专门前缀也能使用所需的名字
//格式为:using namespace :: name;
// 每个name都需要独立的using声明
#include <iostream>
using std::cin;
int main(){
int a;
cin >> a;
cout << a; //错误
}
标准库类型string
定义和初始化string对象
表示可变长的字符序列。
#include <string>
using std::using; //务必std
初始化string对象最常用的一些方法:
string s1;
string s2 = s1;
string s2(s1);
string s3 = "hiya";
string se("hiya");
string s4(10,'c'); //"cccccccccc"
对于字符串字面值的赋值过程,只将除\0
外的值传递给变量。
"hiya"是字面值,在赋值时,先在内存中创建一个对象,再拷贝给s3,其内存存在于整个程序。
直接初始化和拷贝初始化
string s2 = s1; //拷贝初始化
string s2(s1); //直接初始化
string对象上的操作
std::cout << s;
std::cin >> s;
getline(std::cin,s); //作用等同于cin,读取一行,返回cin
s.empty() //s为空返回True,否则返回False
s.size() //返回s中字符的个数
s[n] //返回s中字符的个数
s1 + s2 //返回两者连接后的结果
s1 = s2 // 用s2的副本代替s1中原来的结果
s1 == s2
s1 != s2 // 等性判断,对字母大小敏感
<,<=,>,>= //利用字符在字典中的顺序进行比较,且对字母的大小敏感
读写string对象
string s1,s2;
std::cin >> s1 >> s2;
std::cout << s1 << s2 << std::endl;
- 可以连续输入/输出两个字符串;
- 执行读取操作时,string对象会自动忽略开头的空白,直到遇见下一个空白。
getline从输入流中读入内容,直到遇到换行符。如果一开始遇到换行符,返回空白。
普通的输入操作,也是遇到换行符就停止读取。
比较string对象
- 如果两个string对象长度不同,且较短的string对象的每个字符都与较长string对象对应位置上的字符相同,就说较短string对象小与较长string对象;
- 两个string对象在某些对应位置上不一致,则string对象比较的结果其实是string对象中第一对相异字符比较的结果。
字面值和string对象相加
- 字面值和string对象相加得到string对象;
- 必须保证有一个string,因为普通字符串无法相加。
处理字符
基于for语句处理每个字符串
for(declaration:expression)
statement
/*
expression是一个对象,用于表示一个序列
declaration定义一个变量,用于访问序列中的基础元素
*/
//for example
string str("some string");
for (auto c : str) // auto让系统来决定c的类型
cout << c << endl;
基于for语句改变每个字符串
必须把循环变量定义成引用类型。
for(auto &r : s)
r = toupper(r);
标准库类型vector
表示对象的集合,其中所有对象的类型相同。vector是一个类模版。
#include <vector>
using std::vector;
编译器根据模版创建类或函数的过程称为实例化。当使用模版时,需要指出编译器应该把类或函数实例化为何种类型。
对于类模版,需要提供一些额外的信息来指定模版应该实例化为什么样的类。
vector<int> ivec; // ivec保存int类型的对象
vector<Sales_item> Sales_vec; // 保存Sales_item类型的对象
vector是模版而非类型,由vector生成的类型必须包含vector中元素的类型,例如vector<int>
。
vector定义的对象是一个类,其属性都是int。
定义和初始化vector对象
vector<T> v1;
vector<T> v2(v1);
vector<T> v2=v1;
vector<T> v3(n,val); // 包含n个重复的元素,每个元素都是val
vector<T> v4(n); // 包含n个重复执行值初始化的对象
vector<T> v5{a,b,c,...};
vector<T> v5 = {a,b,c,...};
初始化分为值初始化(括号或等号,只给一个值)、列表初始化(花括号或花括号等号,给一组值)和默认初始化(不给值,系统给默认值)。 如果vector的类型为内置类型,比如int,就会给0;如果是类类型,就会给类默认初始化。
对于int类型:
单一元素:
vector<int> v1(10); // 10个元素,每个元素都是0
vector<int> v2{10};// 元素10
两个元素:
vector<int> v1(10,1); // 10个元素,每个元素都是1
vector<int> v2{10,1};// 元素10,1
对于非int类型:
单一元素:
vector<string> v1(10); // 10个元素
vector<string> v2{10};// 10个元素
两个元素:
vector<string> v1(10,“hi”); // 10个元素,每个元素都是hi
vector<string> v2{10,“hi”};// 10个元素,每个元素都是hi
向vector对象中添加元素
vector类型的变量与python中的list很相似。
vector<int> v2;
for(int i=0;i!=100;i++){
v2.push_back(i) //把一个值当成vector对象的尾元素“压到”vector对象的尾端
}
如果在创建vector对象时,就已经知道其大小,那么可以采取为其赋值的操作。
其他vector操作
除了push_back之外,vector还提供了其他几种操作,大多数和string类似。
v.empty()
v.size()
v.push_back(t)
v[n]
v1 = v2
v1 = {a,b,c,...}
v1 == v2
v1 != v2
<,<=,>,>=
访问vector中的每个元素:
vector<int> v{1,2,3,4,5};
for(auto i:v){
cout << i << endl;
}
vector对象的下标运算可用于访问已存在的元素,而不能用于添加元素。
练习
问题:读入一组词,将其转化为大写。
#include <iostream>
#include <vector>
#include <string>
#include <cctype>
using std::cin;using std::cout;using std::vector;using std::string;using std::endl;
using std::toupper;
int main(){
vector<string> strs;
int n;
string w;
cout << "the number of words :" << endl;
cin >> n;
for(int i=0;i<n;i++){
cout << "the word is :"<< endl;
cin >> w;
strs.push_back(w);
}
for(auto str : strs){
for(int i=0;i<str.size();i++){
for(auto &s : str){
s = toupper(s);
}
cout << str << endl;
}
}
迭代器介绍
迭代器是一种访问对象的机制。
所有标准库定义的容器都可以使用迭代器。
string不属于容器类型,但string也可以执行容器的大部分功能。
push_back会使得迭代器失效。
使用迭代器
与指针类型类似,迭代器也提供了对对象的间接访问。和指针不一样的是,获取迭代器不是使用取地址符。
*iter //返回迭代器iter所指元素的引用
iter->mem //解引用iter并获取元素名为mem的成员,等价于(*iter).mem
++iter //令iter指示容器中的下一个元素
--iter //令iter指示容器中的上一个元素
iter1 == iter2
iter1 != iter2
iter.begin() // 指向容器的第一个元素
iter.end() // 指向容器的最后一个对象的下一个位置,又称为尾后迭代器
值得强调的是,我们不知道迭代器的类型是什么,因此采用auto关键字定义变量。
将迭代器从一个元素移动到另一个元素
int main(){
string str="abcdefgh";
for(auto it=str.begin() ; it != str.end() && !isspace(*it) ; ++it){
cout << *it << endl;
}
}
str.end()
并不实际指示某个元素,所以不能对他进行递增或者解引用的操作。
迭代器的类型
实际上,拥有迭代器的标准库类型使用iterator
和const_iterator
来表示迭代器类型。
定义迭代器
vector<int>::iterator it1; //it1只能读写vector<int>的元素
string::iterator it2; // it2只能读写string的元素
vector<int>::const_iterator it3; // 只能读
string::iterator it4; // 只能读
上述定义的类型也是iter的类型。
begin和end运算符
如果对象是const类型,返回const_iterator
。
iter.cbegin() //只返回const_iterator类型
iter.cend()
练习
int main(){
vector<int> integers{1,2,3,4,5,6,7,8,9,10};
int integer;
for(auto it=integers.begin();it != integers.end();++it){
integer = 2 * (*it);
cout << integer <<endl;
}
}
迭代器的运算
iter + n // 结果迭代器或者指示容器内的一个元素,或指示尾元素的下一个位置
iter - n
iter += n
iter -= n
iter1 - iter2 // 返回的两个迭代器之间的距离
>,>=,<,<=
数组
类似于vector的数据结构。
数组也是存放类型相同对象的容器,与vector不同的是,数组大小确定不变,不能随意向数组中增加元素。
因此,在一些情况下,得到性能的同时失去了灵活性。
定义和初始化内置数组
复合类型:一条声明语句由一个基本数据类型和紧随其后的一个声明符列表组成。每个声明符号命名了一个变量并指定该变量为基本数据类型有关的某种类型。
数组是一个复合类型。
unsigned cnt = 42;
constexpr unsigned sz = 42;
int arr[10]
int arr[cnt] // 错误,必须为常量表达式
int arr[sz]
和内置类型一样,在函数内部定义了某种内置类型的数组,那么默认初始化会令数组含有未定义的值。
显式初始化数组元素
int a[3] = {1,2,3};
数组是不允许拷贝和赋值的。
字符数组的特殊性
字符数组通过字符串初始化时,会把'\0'
中也放进去。
char a[3] = {'a','b','c'};
char a[4] = "abc"
char a[4] = {'a','b','c','\0'} //与上者等价
理解复杂的数组声明
int *ptrs[10] // 含有10个整型指针的数组
int (*ptrs)[10] // ptrs指向一个含有10个整型的数组
int (&ptrs)[10] // ptrs引用一个含有10个整型的数组
对于数组而言,从内向外读要好于从右向左,首先ptrs是一个指针或者引用,后指向/引向数组。
访问数组元素
int integers[10] = 10;
integer = integers[1]
for(auto integer : integers){
cout << integer << endl;
}
指针和数组
数组类型的对象其实就是一个指向该数组首元素的指针。
string nums[] = {"one","two","three"};
strng *p = nums;
auto p(nums) // p默认是一个指针
指针也是迭代器
迭代器的运算方式,指针也能使用。
迭代器有尾后迭代器,可以使用end()
来提取,但是对于指针而言口,可以通过:
int a[10]
int *p = &a[10]
标准库函数begin和end
由于数组不是类类型,所以不存在成员函数,于是可以将数组作为他们的参数。
int ia[] = {1,2,3,4,5}
int *beg = begin(ia)
int *last = end(ia) //与vector不同的是,返回的是指针
现代的c++程序中,尽量使用vector和迭代器,而不是数组和指针;尽量使用string,而不是基于数组的字符串。
多维数组
int ia[3][4];
理解为数组的数组。
对于二维数组,第一个维度称为行,第二个维度称为列。
多维数组的初始化
int ia[3][4]={
{0,1,2,3},
{4,5,6,7},
{8,9,10,11}
};
int ia[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
int ia[3][4]={{0},{4},{8}}; //显式初始化第一列元素,其余元素进行值初始化
ia是第一个内层数组的地址。
int (*p)[4] // 数组指针
int *p[4] //指针数组
int main(){
int a[3][4];
for(int i=0;i<3;i++){
for(int j=0;j<4;j++){
a[i][j]= i*j;
}
}
cout << *(*(a+1)+1) << endl; //内层取了a[1]的首地址,移动后,取了a[1][1]的值
}