decltype
int fun()
{
return 5;
}
i = 1
decltype(i) da; //da是int型
decltype((i)) db = i; //db是int&型,必须初始化
decltype(fun()) dc; //dc是int型,因为fun()返回值为int型
decltype(5) dd; // dd是int型,因为5是int型
decltype((5)) de; // de是int型,因为(5)是int型```
注意:只有对变量双括号时才是引用,其他的双括号均为原来的类型。
类型别名
- typedef
- using
- #define
#define myInt2 int
typedef int myInt3;
int main()
{
using myInt = int;
myInt a = 1;
myInt2 b = 2;
myInt3 c = 3;
cout << a << b << c << endl;
return 0;
}
# 输出为 123
using 命名空间
注意:头文件中不应该包含using声明
标准库类型string
1. string在标准命名空间std里,所以代码开头要加入下述代码。
#include<string>
using std::string
2. 初始化方式
- 直接初始化
- 拷贝初始化
string s1("Hello") //s1被直接初始化为"Hello"
string s2(n, "c") //将s2直接初始化为n个“c”的字符串
string s3 = "Hi" //拷贝初始化s3位“Hi”
3. 操作
- os<<s
将s写入到输出流 - is>>s
将输入流is中读取到的字符写入s,以空白字符分割 - getline(is, s)
从输入流is中读取一行,写入s - s.empty()
s为空,返回true,否则返回false - s[n]
下标索引,n=[0,–] - s.size()
输出s的长度,也即字符个数。
其实,s.size()返回的类型为string::size_type,最好使用unsigned int,因为vs2019是这么处理的。
4. cctype头文件中的函数
- isalnum(c)
当c是字母或数字时为真 - isalpha(c)
当c是字母时为真 - iscntrl(c)
当c是控制字符时为真 - isdigit(c)
当c是数字时为真 - isgraph(c)
当c不是空格但可打印时为真 - islower(c)
当c是小写字母时为真 - isprint(c)
当c是可打印字符时为真(即c是空格或c具有可视形式) - ispunct(c)
当c是标点符号时为真(即c不是控制字符、数字、字母、可打印空白中的一种) - isspace(c)
当c是空白时为真(即c是空格、横向制表符、纵向制表符、回车符、换行符、进纸符中的一种) - isupper(c)
当c是大写字母时为真 - isxdigit(c)
当c是十六进制数字时为真 - tolower(c)
如果c是大写字母,输出对应的小写字母;否则原样输出c - toupper(c)
如果c是小写字母,输出对应的大写字母;否则原样输出c
注意,字符串加法时,必须保证至少含有一个字符串变量,两个字面值字符串是无法进行加法的。
vector
1. string在标准命名空间std里,所以代码开头要加入下述代码。
#include<vector>
using std::vector
2. 初始化
- vector v1 = (10, 1)
v1里面有十个1 - vector v2 = {10, 1}
v2里面有两个元素,10和1 - vector v3 = {5, “Hi”}
注意,虽然v3是大括号,但是因为5不是string类型,所以v3会被初始化为5个“Hi”
3. 操作
- v.push_back(i)
把i存入v的尾部
4. Tips
1. 因为vector会自动扩展大小,所以,除非是里面的元素完全一样,否则不用创建时设定大小
2. vector对象的类型总是包含着元素的类型
vector<int>: :size_type //正确
vector::size_type //错误
迭代器
字符串string是一个支持迭代器的容器,vector也是一个支持迭代器的容器
- auto it = name.begin()
得到一个指向容器开头元素的迭代器,大概可以认为是指针 - auto it++会让迭代器指向容器内下一个元素
- (*it)则是对迭代器的解引用,获取到当前位置的元素
- auto it = name.end()
得到一个指向容器尾部元素再往后一个位置的迭代器,所以end()这个迭代器并没有什么实际意义,仅仅作为一个标记,表明我们处理完了容器内所有元素。
auto it = name.end(), it– 后会获得容器内最后一个元素的迭代器
- auto it = name.cbegin()
cbegin()会获得容器的常量迭代器,效果等同于string::const_iterator it
string s("Hello");
auto it1 = s.begin()
string::iterator it2 = s.begin();
任何改变容器大小的操作均会使迭代器失效!
数组
定义数组时必须指定数据的类型,不允许使用auto,数组的元素必须是对象,不存在引用的数组
数组不支持拷贝赋值,也即不支持a2=a1这种写法
下面是重点
int arr[10];
int (*parr)[10] = &arr;
显然,parr是个指针,指向的什么呢?指向的是个数组,数组有10个元素,数组是int型的,这个数组是arr。又因为左边是个指针,所以右边也得是,所以就是&arr,而不能是arr
int (&refarr)[10] = arr;
refarr是个引用,引用的是一个数组,数组有10个元素,每个元素都是int型的,这个数组是arr,又因为引用就是引用,所以右边是arr本身,不需要是指针。
int *p[10];
p是一个数组,含有十个元素,每个元素都是整型指针。
代码示例
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
int* p[10];
int i = 0;
for (auto& adr : p)
{
adr = &arr[i];
i++;
}
这段代码把arr这个数组的每个元素的地址都存到了p这个指针数组里面
使用数组初始化vector
int int_arr[] = {1,2,3,4,5}
vector<int> v(begin(int_arr), end(int_arr));
运算
C++11开始商一律向下取整。
m/(-n) = (-m)/n = -(m/n)
m%(-n) = m%n
(-m)%n = -(m%n)
分支语句
switch的case语句中,最好在最后一个case上加上break,这样如果以后要增加分支语句,也不用在前面补充break了,会更安全,防止程序员出错。
即使不准备在default标签下做任何工作,定义一个default标签也是有用的。其目的是告诉程序的读者,我们已经考虑到了默认的情况,只是目前什么都没做。
!分支内可以声明变量,但是不能直接初始化
char a;
cin >> a;
switch (a)
{
case 'a':
int ival;
ival = 2;
cout << a << endl;
break;
case 'b':
ival = 1;
cout << ival << endl;
cout << a << endl;
break;
default:
cout << "Default." << endl;
break;
}
!但是,加上作用域大括号后就可以,注意看case ‘a’
char a;
cin >> a;
switch (a)
{
case 'a':
{
int ival = 2;
cout << ival << endl;
cout << a << endl;
break;
}
case 'b':
cout << a << endl;
break;
default:
cout << "Default." << endl;
break;
}
try语句块和异常处理
- throw表达式
- try语句块
- 一套异常类
1. throw表达式
真实的程序中,应该把对象相加的代码和用户交互的代码分离开,比如,如果两本书的ISBN不一样,抛出异常。
#include<stdexcept>
int isbn1,isbn2;
cin >> isbn1 >> isbn2;
if (isbn1 != isbn2)
{
throw std::runtime_error::runtime_error("isbn");
}
cout << isbn1 + isbn2 << endl;
函数
局部静态对象
将局部变量定义成static类型,局部静态对象在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁。
分离式编译
CC -c a.c
CC -c b.cc
CC a.o b.o -o main # 生成main.exe
参数传递
- 传值
- 指针
void fun1(int* a)
{
(*a)++;
a++;
}
int m = 5;
int* pm = &m;
fun1(&m);
fun1(pm);
//执行完毕后m会变成7
虽然fun1()中改变了a这个指针的值,但是指针的值,或者说地址,是值传递,所以a是个局部临时变量,a的改变不会改变pm的指针地址
- 传引用
void fun2(int& a)
{
a++;
}
int m = 5;
int& rm = m;
fun2(m);
fun2(rm);
//执行完毕后m变成7
含有可变形参的函数
C++11提供了一种initializer_list的标准库类型
如果实参的类型均相等但是个数未知,可以使用initializer_list类型的实参,initializer_list 是一-种标准库类型,用于表示某种特定类型的值的数组(参见3.5节,第101页)。initializer_list类型定义在同名的头文件中,并且需要使用std,也即using std::initializer_list。
操作 | 含义 |
---|---|
initializer_list 1st | 默认初始化: T类型元素的空列表 |
initializer_list lst{a,b,c…} | lst的元素数量和初始值一样多, lst的元素是对应初始值的副本:列表中的元素是const |
lst2(lst) | 拷贝或赋值一个 initializer_ list 对象不会拷贝列表中的元素;拷贝后, 原始列表和副本共享元素 |
lst2 = lst | 同上 |
lst.size() | 列表中的元素数量 |
lst.begin() | 返回指向 lst 中首元素的指针 |
lst.end() | 返回指向 lst 中尾元素下一位置的指针 |
注意,initializer_list接收的参数为用大括号括起来的一堆数据!
void fun(initializer_list<string> lst)
{
for (auto beg = lst.begin(); beg != lst.end(); beg++)
cout << *beg << endl;
}
int main(int argc, char* argv[])
{
string s = "sss";
fun({ "hello","aaa",s });
return 0;
}
输出为:
hello
aaa
sss
不要返回局部对象的引用或指针
返回局部对象的引用是错误的;同样,返回局部对象的指针也是错误的,一旦函数完成,局部对象被释放,指针将指向一个不存在的对象。
声明一个返回指针数组的函数
Type (*function (parameter_list)) [dimension]
int (*func(int i)) [10]
func(int i) 意为调用func函数时需要一个int类型的实参
(* func(int i)) 意为我们可以对函数调用的结果执行解引用操作
(*func(int i)) [10] 表示解引用操作会得到一个大小是10的数组
int (*func(int i)) [10]表示数组中的元素是int类型