因为
C++是一种静态强类型的语言,任何变量都要有一个确定的类型,否则就不能用。在声明变量的 时候,必须要明确地给出类
std::string 推导成了 const char[6].

auto 还避免了对类型的“硬编码”,也就是说变量类型不是“写死”的, 而是能够“自动”适应表达式的类型。auto x = 0L; // 自动推导为long
auto y = &x; // 自动推导为long*
auto z {&x};// 自动推导为long*
auto err;// 错误,没有赋值表达式,不知道是什么类型

class X final {
auto a = 10; // 错误,类里不能使用auto推导类型 };
auto的推导规则,保证它能够按照你的意思去工作。


decltype 还可以直接从一个引用类型的变量推导出引用类型,而 auto 就会把引用去掉,推导出值类型。C++14 就又增加了一个“decltype(auto)”的形式,既可以精确推导类型,又能像 auto 一样方便使用。
auto 还有一个“最佳实践”,就是“range-based for”,不需要关心容器元素类型、迭代器返回值和首末位置,就能非常轻松地完成遍历操作。不过,为了保证效率,最好使 用“const auto&”或者“``auto&`”。
C++14 里,auto 还新增了一个应用场合,就是能够推导函数返回值,这样在写复杂函 数的时候,比如返回一个 pair、容器或者迭代器,就会很省事。
auto 的高级形式,更侧重于编译阶段的类型计算,所以常用在泛型编程里,获取各种类型,配合 typedef 或者 using 会更加方便。当你感觉“这里我需要一个特殊类型”的时 候,选它就对了。decltype
+ 在定义类的时候,因为 auto 被禁用了,所以这也是 decltype 可以“显身手”的地方。它 可以搭配别名任意定义类型,再应用到成员变量、成员函数上,变通地实现 `auto 的功能

// 需要加上volatile修饰,运行时才能看到效果
const volatile int MAX_LEN = 1024; // 如果 volatile 就不会修改其内容
auto ptr = (int*)(&MAX_LEN);
*ptr = 2048;
cout << MAX_LEN << endl; // 输出2048
volatile 会禁止编译器做优化,所以除非必要,应当少用 volatile,把 const 理解成 read only(虽然是“只读”,但在运行阶段没有什么是不可以改变的,也可以强制写入),把变量标记成 const 可以让编译器做更好的优化。##+在编译阶段防止有意或者无意的修改。
int x = 100;
const int& rx = x; // 常量引用 用它作为入口参数,一来保证效率,二来保 证安全
const int* px = &x; //常量指针
//常见的用法是,const 放在声明的最左边,表示指 向常量的指针。
string name = "uncharted";
const string* ps1 = &name; // 指向常量
*ps1 = "spiderman"; // 错误,不允许修改
// const 在“*”的右边,表示指针不能被修改,而指向的 变量可以被修改:
//不建议使用
string* const ps2 = &name; // 指向变量,但指针本身不能被修改
*ps2 = "spiderman"; // 正确,允许修改
class DemoClass final {
private:
const long MAX_SIZE = 256; // const 成员变量
int m_value; //成员变量
public:
int get_value() const { // const 成员函数
return m_value;
//函数的执行过程是 const 的,不会修改对象的状态(即成员变量)成员函数 是一个“只读操作”。
}
};

class DemoClass final {
private:
mutable mutex_type m_mutex;
public:
void save_data() const {
// do someting with m_mutex
}
};

Java、Go 那种严格意义上的垃圾回收,而是广义上的垃圾回收,这就是构造 / 析构函数和 RAII 惯用法(Resource Acquisition Is Initialization)RAII。 delete,它会自动管理初始化时的指针,在离开作用域时析构释放内存。
//它也没有定义加减运算,不能随意移动指针地址,这就完全避免了指针越界等危险操 作,可以让代码更安全:
ptr1++; // 导致编译错误
ptr2 += 2;// 导致编译错误
//除了调用 delete、加减运算,初学智能指针还有一个容易犯的错误是把它当成普通对象来 用,不初始化,而是声明后直接使用:
unique_ptr<int> ptr3; // 未初始化智能指针
*ptr3 = 42 ; // 错误!操作了空指针
//未初始化的 unique_ptr 表示空指针,这样就相当于直接操作了空指针,运行时就会产生致命的错误(比如 core dump)
//你可以调用工厂函数 make_unique() ,强制创建智能指针的时候 必须初始化。 C++14
auto ptr3 = make_unique<int>(42); assert(ptr3 && *ptr3 == 42);// 工厂函数创建智能指针
auto ptr4 = make_unique<string>("god of war");
// 工厂函数创建智能指针 assert(!ptr4->empty());
template<class T, class... Args> // 可变参数模板
std::unique_ptr<T> // 返回智能指针
my_make_unique(Args&&... args) { // 可变参数模板的入口参数
return std::unique_ptr<T>(// 构造智能指针
new T(std::forward<Args>(args)...));
}
unique_ptr表示指针的所有权是“唯一”的,不允许共享,任何时候只能有一 个“人”持有它。unique_ptr 应用了C++的“转移”(move)语义,同时禁止了拷贝赋值,所以,在向另一个 unique_ptr 赋值的时候,要特别留意,必须用 std::move() 函数显式地声明所有权转移。
shared_ptr:它的所有权是可以被安 全共享的,也就是说支持拷贝赋值,允许被多个“人”同时持有,就像原始指针一样。
shared_ptr 支持安全共享的秘密在于内部使用了“引用计数”。1,表示只有一个持有者。如果发生拷贝赋值——也就是共享的时候,引用计数就增加,而发生析构销毁的时候,引用计数就减少。只有当引用计数减少到 0,也就是说,没有任何人使用这个指针的时候,它才会真正调用 delete 释放内存。
shared_ptr 具有完整的“值语义”(即可以拷贝赋值),所以,它可以在任何场合替代原始指针,而不用再担心资源回收的问题,比如用于容器存储指针、用于函数安全返回动态创建的对象。
shared_ptr 的引用计数也导致了一个新的问题,就是“循环引用”,这在把 shared_ptr 作为类成员的时候最容易出现,典型的例子就是链表节点。
1,但指针互指(即拷贝赋值)之后,引用计数都变成了 2。shared_ptr 意识不到这是一个循环引用,多算了一次计数,后果就是引用计数无法减到 0,无法调用析构函数执行 delete,最终导致内存泄漏。weak_ptr : 它专门为打破循环引用而设计,只观察指针,不会增 加引用计数(弱引用),但在需要的时候,可以调用成员函数 lock(),获取 shared_ptr(强引用)。
既然你已经理解了智能指针,就尽量不要再使用裸指针、new 和 delete 来操作内存了。



基本的 try-catch 写法:

用 try 把可能发生异常的代码“包”起来,然后编写 catch 块捕获异常并处理。
因为 C++ 已经为处理异常设计了一个配套的异常类型体 系,定义在标准库的 头文件里。标准异常的继承体系如下:



最好不要直接用 throw 关键字,而是要封装成一个函数通过引入一个“中间层”来获得更多 的可读性、安全性和灵活性。
使用 catch 捕获异常的时候也要注意,C++ 允许编写多个 catch 块,捕获不同的异常,再 分别处理。但是,异常只能按照 catch 块在代码里的顺序依次匹配,而不会去找最佳匹配 所以,最好只用一个 catch 块, 绕过这个“坑”。
写 catch 块就像是写一个标准函数,所以入口参数也应当使用“const &”的形式,避免对 象拷贝的代价:

function-try形式 就是把整个函数体视为一个大 try 块,而 catch 块放在后面,与函数体 同级并列。
其好处:不仅能够捕获函数执行过程中所有可能产生的异常,而且少了一级缩 进层次,处理逻辑更清晰。

几个应当使用异常的判断准则:

noexcept 的真正意思是:“我对外承诺不抛出异常,我也不想处理异常,如果真的有异常发生,直接崩溃(crash、core dump)


[ ],术语叫“lambda 引出符”(lambda introducer)。auto f1 = [](){}; // 相当于空函数,什么也不做

upvalue”,也就是在 lambda 表达式定义之前所有出现的变量,不管它是局部的还是全局的。



用auto后最好用注释说明它是个什么,后续该怎么用,否 则会导致后面的代码比较难懂。
用auto后最好用注释说明它是个什么,后续该怎么用,否 则会导致后面的代码比较难懂。