0625
C++工程师
- 第5章 高频考点与真题精讲
-
- 5.1 指针 & 5.2 函数
- 5.3 面向对象(和5.4、5.5共三次直播课)
- 5.6 内存管理①(结合计算机操作系统笔记)
- 5.7 内存管理②(结合计算机操作系统笔记)
- 5.7 名称空间、模板
- 5.8 STL(标准模板库)
- 5.9 C++新特性
- 5.10-5.13 项目回顾(Linux系统编程、网络编程、计算机网络、操作系统)
第5章 高频考点与真题精讲
5.1 指针 & 5.2 函数
5.3 面向对象(和5.4、5.5共三次直播课)
5.3.1 - 5.3.11
5.3.12-38
5.6 内存管理①(结合计算机操作系统笔记)
5.7 内存管理②(结合计算机操作系统笔记)
5.7 名称空间、模板
5.8 STL(标准模板库)
5.9 C++新特性
视频课5.9的全部加上视频课5.10的前01:14:39都是关于C++新特性的。
5.9.1 新类型、路径表示
C++ 11 新增了类型 long long
和 unsigned long long
,以支持 64 位(或更宽)的整型;
新增了类型 char16_t
和 char32_t
,以支持 16 位和 32 位的字符表示;
还新增了”原始“字符串。
表示路径的时候,因为一般的路径都是用反斜杠隔开的,或者包含一些其他类型的符号(问号、逗号等),而路径是使用双引号括起来的,所以就涉及到转义字符的情况,三种方法:
- 在每个反斜杠前再加个反斜杠;
- 用
R"( 路径 )"
把路径括起来; - 用
R"+*( 路径 )+*"
把路径括起来。
cout << "C:\\Program Files (x86)\\Application Verifier" << endl;
cout << R"(C:\Program Files (x86)\Application Verifier)" << endl;
cout << R"+*(C:\Program Files (x86)"\Application Verifier)+*" << endl;
5.9.2 统一的初始化(初始化列表)
C++ 11 扩大了用大括号括起的列表(初始化列表)的适用范围,使其可用于所有内置类型和用户定义的类型(即类对象)。使用初始化列表时,可添加等号(=),也可不添加:
int x = {
5};
double y {
2.75};
short quar[5] {
1, 2, 3, 4, 5};
int * ar = new int [4] {
2, 3, 4, 5};
class Stump {
private:
int roots;
double weight;
public:
Stump(int r, double w) : roots(r), weight(w) {
}
};
Stump s1(3, 15.6);
Stump s2{
5, 43.4};
Stump s3 = {
4, 32.1};
5.9.3 声明
1.关键字auto
auto:自动类型推断(转换)。
vector<int> v = {
1, 2, 3, 4, 5};
//for(vector<int>::iterator it = v.begin(); it != v.end(); ++it){
for(auto it = v.begin(); it != v.end(); ++it){
cout << *it << ", ";
}
cout << endl;
2.关键字decltype(declare type)☆☆
先看个例子:
int a; float b;
//float c = a * b;
decltype(a * b) c = a * b;//假如不知道c的类型,可用decltype推导出来
double x;
int n;
decltype(x * n) q;//double类型
decltype(&x) pd;//double*类型
template<typename T, typename U>
void ef(T t, U u) {
decltype(T * U) tu;//
}
decltype
将变量的类型声明为表达式指定的类型。一般用在模板中。
(以下内容来自C++ Primer Plus(嵌入式公开课)—第8章 函数探幽中的8.5.6 模板函数的发展—关键字decltype
)
假如有以下模板:
template <class T1, class T2>
void ft(T1 x, T2 y){
...
?type? xpy = x + y;
...
}
考虑xpy是什么类型?
可能是T1、T2或者其他类型。
当T1是double型,T2是int型,x+y是double型,xpy是T1类型;
当T1是short型,T2是int型,x+y是int型,xpy是T2类型;
当T1时short型,T2是char型,x+y的结果自动整型提升,xpy是int型。
C++新增的关键字decltype
提供了解决方案:
template <class T1, class T2>
void ft(T1 x, T2 y){
...
decltype(x + y) xpy = x + y;
...
}
当使用关键字decltype
时,为确定类型,编译器必须遍历一个核对表。假如有如下声明:
decltype(expression) var;
decltype(x) y; // 让y的类型与x相同,x是一个表达式
则核对表的简化版如下:
1.如果expression是一个没有用括号括起的标识符,则var的类型与该标识符的类型相同,包括const等限定符:
double x = 5.5;
double y = 7.9;
double& rx = x;
const double* pd;
decltype(x) w; //w和x的类型相同,double
decltype(rx) u = y; //u和rx的类型相同,double&
decltype(pd) v; //v和pd的类型相同,const double*
2.如果expression是一个函数调用,则var的类型与函数的返回类型相同:
long indeed(int);
decltype(indeed(3)) m;//m和indeed()函数的返回值类型相同,即long
3.如果expression是一个用括号括起来的标识符,则var为指向其类型的引用:
(注意:这里的expression必须是一个左值;如果是个右值,var的类型就是这个右值的类型,而不是它的引用)
expression是左值:
double xx = 4.4;
decltype ((xx)) r2 = xx;//r2是指向括号里的xx的引用,即double&
decltype (xx) w = xx;//w和xx的类型相同,即double
expression是右值:(常量、表达式)
decltype (10) r22;//int
decltype ((10)) r22;//int不是int&,因为10是右值
decltype (xx + 3) ww;//double
decltype ((xx + 3)) ww;//double,不是double&,因为xx + 3是右值
补充:左值&右值
左值:可修改的值,例如普通变量,int m, n;
非左值:不可修改的值,例如常量(10
)和表达式(m+3
);
4.如果前面的条件都不满足,则var的类型与expression的类型相同:
int j = 3;
int& k = j;
int& n = j;
decltype(j + 6) i1;//i1是int
decltype(100L) i2; //i2是long
decltype(k + n) i3;//i3是int
decltype((k + n)) i4;//i4是int,不是int&,因为k+n是右值
请注意,虽然k和n都是引用,但表达式k + n
不是引用,它是两个int的和,是个右值,所以i3
是int
类型;
由于k + n
是右值,所以给它加个括号之后,i4
的类型跟这个右值的类型相同,而不是它的引用。
3.返回类型后置
C++11 新增了一种函数声明语法:
在函数名和参数列表后面(而不是前面)指定返回类型:
double f1(double, int);
auto f2(double, int) -> double;
// -> double 称为后置返回类型,auto是一个占位符,表示后置返回类型提供的类型
注意:这里的auto是一个占位符,它并不能推导出返回值的类型,因为在写返回值的时候还没有使用过形参,根本无法推导出返回值的类型。
有一个相关的问题是 decltype 本身无法解决的:
template<typename T1, typename T2>
??? gt(T1 x, T2 y) {
...
return x + y;
}
用decltype来解决:decltype 在参数声明后面,因此x和y位于作用域内,可以使用
template<typename T1, typename T2)
auto eff(T1 x, T2 y) -> decltype(x*y){
// decltype 在参数声明后面,因此x和y位于作用域内,可以使用
...
return x * y;
}
4.模板别名(using 别名 = )
(这个用的很少,了解下)
对于冗长或复杂的标识符,如果能够创建其别名将很方便。之前,C++ 提供了 typedef:
typedef std::vector<std::string>::iterator itType;
C++ 11 提供了另一种创建别名的语法:
using itType = std::vector<std::string>::iterator;
差别在于,这种方式也可以用于模板部分具体化,但 typedef 不能:
template<typename T>
using arr12 = std::array<T,12>;
上述语句具体化模板array<T,int>
,对于下述声明:
std::array<double,12> a1;
std::array<std::string,12> a2
可将它们替换为如下声明:
arr12<double> a1;
arr12<std::string> a2;
5.关键字nullptr
以前是NULL,C++11中变成了nullptr。
5.9.4 智能指针☆☆☆☆☆
(视频课中从50:20开始,到01:49:49结束)
第一次涉及到智能指针是在笔记②:牛客校招冲刺集训营—C++工程师中的☆☆☆指针运算符重载(* 和 ->)(写个智能指针类),写了个智能指针类,类中重载解引用*
和箭头->
运算符,并且在析构函数中将new的空间释放掉,就可以避免因为忘记手动delete而带来的内存泄漏问题,所以叫智能指针。
为什么要引入智能指针?
如果在程序中使用 new
从堆(自由存储区)分配内存,等到不再需要时,应使用 delete
将其释放。C++ 引入了智能指针 auto_ptr
(C++98), 以帮助自动完成这个过程。智能指针是行为类似于指针的类对象。
在使用时(尤其是使用STL),需要更精致的机制,C++ 11 摒弃了 auto_ptr,并新增了三种智能指针:unique_ptr
、shared_ptr
和 weak_ptr
。(严格意义上来说前两种是智能指针,第三种是为了解决第二种智能指针出现的循环引用的问题)
void remodel(std::string & str) {
std::string * ps = new std::string(str);
// ...
str = *ps;
return;