C++11
C++11/14/17 引入了大量非常有用的特性,使代码更直观、安全、简洁、方便。C++11 特性是 C++20 以前更新最多的版本, C++14、C++17 特性更新少很多。
在此总结一下C++11 语言特性和标准库。
此处列举的仅是一部分较常用的特性,完整的列表还需参考官方文档或者微软的文档:
https://docs.microsoft.com/en-us/previous-versions/hh567368(v=vs.140)?redirectedfrom=MSDN
C++11 关键特性
C++11特性:auto 自动推导说明符
auto 自动推导说明符,主要就是用来根据上下文自动推导声明的变量类型,从而不需要写冗余的类型,提高开发效率和减少代码量。
auto.cpp
#include <algorithm>
#include <vector>
#include <iostream>
#include <map>
#include <string>
#include <string.h>
#include <stdint.h>
#include <typeinfo>
/*
1、用于声明变量或常量
2、用于声明的变量从表达式初始化
3、用在 range-for的C++新for循环语法里
4、用于模板函数的返回后缀语法的占位符
*/
/////////////////////////////////////////////////////////////////////////////////////////////////////
int64_t Add(int64_t first, int64_t second) {
return first + second;
}
template<class T, class N>
auto AddTemplate(T t, N n)->decltype(t + n) {
return t + n;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char const *argv[])
{
std::cout << "----------1、用于声明变量或常量---------------" << std::endl;
auto i = 12; // int
auto str = "hello vic.MINg"; // const char*
auto ilist = { 1, 2, 3 }; // std::list
auto d = 3.14; // double
auto& d1 = d; // double&
auto d2 = &d; // double*
auto ivector = new std::vector<int>(); // std::vector<int>
system("pause");
std::cout << "----------2、用于声明的变量从表达式初始化---------------" << std::endl;
auto istrlen = strlen(str) + 1; // 算术表达式
auto func = [](int j) {std::cout << j << std::endl; }; // lambda 表达式
// 应用在lambda
auto print = [](const char* name) {
std::cout << "name: " << name << std::endl;
};
print("vic.MINg");
system("pause");
std::cout << "----------3、用在 range-for的C++新for循环语法里---------------" << std::endl;
for (auto one : { 1, 2, 3 }) {
std::cout << "one: " << typeid(one).name() << ":" << one << std::endl;
}
std::map<std::string, int> p1 = { { "info",1 },{ "world",2 } };
// std::map<>::value_type 类型,即std::pair<std::string,int>, 必须是auto&才不会创建新的 std::pair.
// 也只有auto& 才可以改里面的值.
for (auto& vt : p1) {
auto& key = vt.first;
auto& value = vt.second;
value = 3;
}
for (auto ite : p1)
std::cout << "key: " << ite.first << " second: " << ite.second << std::endl;
// 注意这个是std::map<>::iterator,不是value_type, ite相当于指向value_type的共享指针.
for (auto ite = p1.begin(); ite != p1.end(); ++ite) {
auto& key = ite->first;
auto& value = ite->second;
int start = 0xa;
value = start++;
}
for (auto ite : p1)
std::cout << "key: " << ite.first << " second: " << ite.second << std::endl;
system("pause");
std::cout << "----------4、用于模板函数的返回后缀语法的占位符---------------" << std::endl;
auto v1 = Add(1, 2); // v1: long long
std::cout << "v1: " << typeid(v1).name() << std::endl;
auto v2 = AddTemplate(8.9, -1); // v2: double
std::cout << "v2: " << typeid(v2).name() << std::endl;
system("pause");
return 0;
}
C++11特性:constexpr 常量表达式说明符
constexpr.cpp
#include <algorithm>
#include <vector>
#include <stddef.h>
#include <iostream>
#include <string>
#include <stdlib.h>
#include <memory>
#include <complex>
/*
constexpr 说明:
1、提供更加通用的常量表达式。
2、允许常量表达式包括用户定义的类型。
3、提供一种方法保证在编译时完成初始化。
constexpr 语法:
1、constexpr 变量声明
2、constexpr 函数声明
3、constexpr 构造函数声明
4、用户自定义的字面常量
*/
////////////////////////////////////////////////////////////////////////////////////
enum Flags { undo = 0, doing = 1, fail = 2, finish = 4 };
// 给定的常量参数和简单运算可以在编译时得到常量结果.
// 简单运算: 编译器能在编译器计算出结果的.(也就是编译不出错).
constexpr int funFlags(Flags f1, Flags f2) {
return f1 + f2;
}
constexpr std::complex<long double> operator""_b(long double d)
{
return { 3.14, d }; // complex is a literal type
}
////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char const *argv[])
{
std::cout << "----------1、constexpr 变量声明--------------" << std::endl;
// constexpr 类型 变量名 = 表达式; ( 表达式: 需要简单逻辑的常量表达式 )
constexpr int x1 = fail | finish; // 编译器在编译时得出x1 结果 = 6
const int x2 = fail | finish; // 编译器不能保证在编译时得结果.
static_assert(x1 == 6, "x1 not 6"); // 静态断言,编译时断言.
system("pause");
std::cout << "----------2、constexpr 函数声明--------------" << std::endl;
// 1、给定的常量参数和简单运算可以在编译时得到常量结果.
// 2、注意要函数和变量声明都需要加 constexpr.
// -- 变量不加 constexpr 不会在编译时计算结果.
// -- 函数不加 constexpr 编译报错: function call must have a constant value in a constant expression
constexpr int result = funFlags(undo, doing); // 结果 = 1
static_assert(result == 1, "result not 1"); // 静态断言,编译时断言.
system("pause");
std::cout << "----------3、constexpr 构造函数声明--------------" << std::endl;
// 需要简单逻辑的构造函数
struct Point {
int m_nX, m_nY;
constexpr Point(int x, int y) : m_nX(x), m_nY(y) { }
};
// 调用构造函数获取实例时也需要声明constexpr
constexpr Point pt[] = { Point(0,0), Point(1,1), Point(2,2) };
constexpr int Y = pt[1].m_nY; // 结果 = 1
//static_assert(Y == 1, "x not 1"); // 静态断言,在编译时断言.
system("pause");
std::cout << "----------4、用户自定义的字面常量--------------" << std::endl;
// C++11新特性声明用户自定义字面常量 (user - defined literals)
constexpr auto z = 1.0_b; // {(3.14L + 1.0L)}
//std::cout << "1.0_b = " << z << std::endl;
system("pause");
// const vs constexpr
// const
// 1、声明变量时,可以不初始化值.
// 2、如果声明的变量没初始化,编译器通常能初始化(不保证)。
// 3、把const对象存放在编译器表格里,但是不会放入产生的代码(编译后的代码)里。
// constexpr
// 1. 声明变量时,必须有初始化值.
// 2. 在编译时间计算初始化值。
// 3. 把对象放入编译器表格里,只在需要的时候放入产生的代码里。
return 0;
}
C++11特性:copy-rethrow-exception 复制和重抛异常
复制和重抛异常在 C++11 里是用函数实现的所以并没有什么新的关键字说明符
copy-rethrow-exception.cpp
#include <iostream>
#include <string>
#include <vector>
#include <string.h>
#include <stdint.h>
/*
1、异常复制
2、异常重抛
*/
/////////////////////////////////////////////////////////////////////////////////////////////////
void HandlerException(std::exception_ptr eptr) noexcept {
std::cout << "handler exception" << std::endl;
// 处理失败,重新抛出。
try {
std::rethrow_exception(eptr);
}
catch (const std::exception& e) {
std::cerr << e.what() << '\n';
}
}
void RethrowException() noexcept(false)
{
try {
std::string().at(1);
}
catch (const std::exception& e) {
std::cerr << e.what() << '\n';
auto eptr1 = std::current_exception();
auto eptr2 = std::current_exception();
HandlerException(eptr1);
}
// 当前异常指针对象已经失效,因为之前已经创建过eptr1并且创建后的指针对象已经销毁.
auto eptr = std::current_exception();
if ( eptr == NULL )
std::cout << "eptr is nullptr exception" << std::endl;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char const *argv[])
{
std::cout << "----------1、异常复制---------------" << std::endl;
// 1、std::current_exception():返回一个 std::exception_ptr 类型对象,它引用了当前被处理的异常或者说是当前被处理的异常的复制。
// 如果没有异常抛出时,返回的是 null 的 exception_ptr 对象。
// 当异常 exception_ptr 对象失效时,std::current_exception() 就不再返回这个异常
// 2、实际上可以把 std::current exception() 当做返回一个 std::shared ptr 类型的异常对象。只要共享对象计数为0时,异常对象会销毁。
// 获取当前异常, 如果没有异常, 那么返回一个异常对象,但它的内部成员变量为NULL.
auto eptr = std::current_exception();
if ( eptr == NULL )
std::cout << "no exception throw" << std::endl;
system("pause");
std::cout << "----------2、异常重抛---------------" << std::endl;
// 1、捕抓到异常后,如果这个异常处理不了,比如某个条件不能满足不能继续处理下去,那么需要重新抛出异常。
// 让更外层的异常捕抓器处理,那么可以通过以下函数:
// std::rethrow_ exception( exception_ptr p ); // 重新抛出异
RethrowException();
system("pause");
return 0;
}
C++11特性:decltype 类型声明说明符
decltype.cpp
#include <algorithm>
#include <vector>
#include <iostream>
#include <string>
#include <memory>
#include <map>
/*
decltype 语法:
1、decltype() 说明符在编译时获取()里的声明类型,注意,不是运行时获取类型,特别是多态类型的对象,只能获取该对象当前表示的类型,不能获取实际类型。
2、decltype 和 auto 不同的是它支持表达式。
decltype 场景:
1、用在后缀返回语法里,比如模板。
2、用在获取 lambda表达式的类型。
3、简化类型声明,经常用在获取auto类型的声明类型,之后声明一个该类型的变量。
*/
/////////////////////////////////////////////////////////////////////////////////////////////////////
// 函数相关
template <typename T>
std::string TypeName()
{
std::string r = typeid(T).name();
if (std::is_lvalue_reference<T>::value)
r += "&";
else if (std::is_rvalue_reference<T>::value)
r += "&&";
return r;
}
template <typename T>
void Print()
{
std::cout << "value type is :" << TypeName<T>() << std::endl;
}
// 后缀返回语法
template<class T, class N>
auto AddTemplate(T t, N n)->decltype(t + n) {
return t + n;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char const *argv[])
{
std::cout << "decltype 语法:" << std::endl;
std::cout << "----------1、decltype() 说明符在编译时获取()里的声明类型---------------" << std::endl;
// 注意,不是运行时获取类型,特别是多态类型的对象,只能获取该对象当前表示的类型,不能获取实际类型。
int i = 0;
decltype(i) j; // int
Print<decltype(j)>();
decltype((i)) j1 = j; // 带括号推导出引用类型. int&
Print<decltype(j1)>();
// decltype(int); // 编译错误,不允许使用类型.
system("pause");
std::cout << "----------1、decltype() 说明符在编译时获取()里的声明类型---------------" << std::endl;
// 如果表达式的值类型是 xvalue(过期值,可以理解为编译器生成的临时变量),那么 decltype推导( yields)出T&&
decltype(std::move(i)) a1 = std::move(2); // int&&
Print<decltype(a1)>();
// 如果表达式的值类型是 lvalue(左值),那么 decltype推导( yields)出T&
int m = 2; // 值类型是左值
decltype(m = 3) m2 = m; // 表达式是左值表达式, 那么结果就是左值引用
Print<decltype(m2)>();
struct A
{
double x;
};
const A *a;
decltype(a->x) y; // 值类型是纯右值
Print<decltype(y)>();
decltype(m == 3) m3; // 值类型是纯右值
Print<decltype(m3)>();
system("pause");
std::cout << "decltype 场景:" << std::endl;
std::cout << "---------1、用在后缀返回语法里,比如模板。---------------" << std::endl;
std::cout << "AddT: " << AddTemplate(3.14, 6) << std::endl;
system("pause");
std::cout << "---------2、用在获取 lambda表达式的类型。---------------" << std::endl;
// 在对 map 的 key 进行指定排序以下 map 因为需要指定 map 的比较函数类型,需要在模板特例化知道类型,那么获取 Lambda 表达式类型的唯一方式就是通过 decltype();
// 声明 lambda 表达式的类型,之后应用于模板特例.
auto func = [](int i, int j)->bool { return i > j; };
// 通过 lambda 实现排序功能。
std::map<int, std::string, decltype(func)> prs1(func);
prs1[1] = "1";
prs1[2] = "2";
for (auto &one : prs1)
std::cout << one.first << std::endl;
system("pause");
std::cout << "---------3、简化类型声明,经常用在获取auto类型的声明类型,之后声明一个该类型的变量。---------------" << std::endl;
std::map<int, std::string> prs0;
prs0[1] = "1";
prs0[2] = "2";
for (auto &one : prs0)
std::cout << one.first << std::endl;
auto ite = prs0.begin();
decltype(ite) ite2;
system("pause");
return 0;
}
C++11特性:initializer-list 初始化列表
initializer-list.cpp
#include <iostream>
#include <typeinfo>
#include <vector>
#include <list>
#include <map>
#include <string>
#include <queue>
#include <set>
#include <memory>
/*
1、C++98 和 C++11 的初始化列表。
2、{}禁止用高精度赋值给低精度类型的初始化列表。
3、统一初始化列表的语法和语义。
*/
/////////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char const *argv[])
{
std::cout << "----------1、C++98 和 C++11 的初始化列表。---------------" << std::endl;
std::cout << "C++98 的初始化列表'" << std::endl;
// 初始化静态数组可以用初始化列表。
int number1[] = { 1, 2, 3, 4 };
int number2[4] = { 0, 1, 4, 7 };
// 动态数组可以使用() 初始化为0。
int* number3 = new int[5](); // 初始化为0
// STL容器不能使用{}初始化。
// std::vector<int> vector1 = { 1, 2, 3 }; // C++98 编译错误
std::cout << "C++11 的初始化列表'" << std::endl;
// C++11的{}方式初始化列表可以直接对STL容器类型初始化。
std::cout << "std::vector InitializerList" << std::endl;
std::vector<int> vector1 = { 1, 2, 3 }; // C++11 编译通过
for (auto one : vector1)
std::cout << "vector1 one: " << one << std::endl;
std::cout << "std::list InitializerList" << std::endl;
std::list<std::string> list1 = { "hello","vic.MINg" };
for (auto one : list1)
std::cout << "list1 one: " << one << std::endl;
std::cout << "std::set InitializerList" << std::endl;
std::set<float> set1 = { 0.1f, -1.1f, 49.2223f, 90.0f};
for (auto one : set1)
std::cout << "set1 one: " << one << std::endl;
std::cout << "std::queue InitializerList not support" << std::endl; // std::queue 不支持 初始化
std::cout << "std::string InitializerList" << std::endl;
std::string str1 = { 'v','i','c','.','M','I','N','g' };
for (auto one : str1)
std::cout << "str1 one: " << one << std::endl;
std::cout << "std::map InitializerList" << std::endl;
std::map<std::string, std::vector<int>> map1{ { "onetwothree",{ 1,2,3 } },{ "fourfivesix",{ 4,5,6 } } }; // map 是无序的
for (auto one : map1) {
auto& ite = one;
auto& str = ite.first;
auto& array = ite.second;
std::cout << "map1 key: " << str << " size: " << array.size() << std::endl;
}
// 直接访问, std::initializer_list是不可变序列
for (auto one : { 6,7,8 })
std::cout << "one: " << one << std::endl;
auto sil = { 10,11,12 };
for (auto ite = sil.begin(); ite != sil.end(); ++ite)
std::cout << "one: " << *ite << std::endl;
system("pause");
std::cout << "----------2、{}禁止用高精度赋值给低精度类型的初始化列表。---------------" << std::endl;
// int a1[2] = {1, 2.1}; // C++11 double 转 int 会编译错误 C++98 支持的数组初始化
// std::vector<int> v1 = { 1, 2, 3, 4.0 };
system("pause");
std::cout << "----------3、统一初始化列表的语法和语义。---------------" << std::endl;
// C+-11开始可以用进行初始化,包括原始对象和类对象。
class OneClass
{
public:
OneClass(int i) { i_ = i; }
int i_;
};
// 统一初始化语法和语义, 可以用{}来进行到底.
int b1{ 1 }; // 初始化整数,相当于 int b1 = 1;
int b2 = { 2 };
int* pb3 = new int{ 3 };
int b4 = int{ 4 };
OneClass a1{ 1 }; // A a1_1(1);
OneClass a2 = { 2 }; // A a2_1 = 2;
OneClass* pa3 = new OneClass{ 3 }; // A* pa3_1 = new A(3);
OneClass a4 = OneClass{ 4 }; // A a4_1 = A(4);
system("pause");
return 0;
}
C++11特性:inline-namespace 内联命名空间
ming.h
#pragma once
// namespace MINg
namespace MINg
{
#include "version01.hpp"
#include "version02.hpp"
}
version01.hpp
#pragma once
// namespace Version01
namespace Version01
{
void fun(int);
}
#include <iostream>
#include "ming.h"
void MINg::Version01::fun(int i) {
std::cout << "Version01 fun int: " << i << std::endl;
}
version02.hpp
#pragma once
// namespace Version02
inline namespace Version02
{
void fun(int);
void fun(double);
}
#include <iostream>
#include "ming.h"
void MINg::Version02::fun(int i) {
std::cout << "Version02 fun int: " << i << std::endl;
}
void MINg::Version02::fun(double d)
{
std::cout << "Version02 fun double: " << d << std::endl;
}
inline-namespace.cpp
#include <iostream>
#include <string>
#include "../include/ming.h"
/*
1、语法和说明
2、常见用法
*/
using namespace MINg;
int main(int argc, char const *argv[])
{
std::cout << "----------1、语法和说明---------------" << std::endl;
// 说明:内联命名空间是想要在语言层面提供一种机制,它能让库在版本迭代中支持版本化的表示方法
// 语法:在 namespace 前加 inline 声明的命令空间,它所在的命名空间可以直接访问 inline 命名空间里的声明内容。
std::cout << "----------2、常见用法---------------" << std::endl;
// 声明库时,随着库的演进,给库加上一个版本化的命名空间。
// 研发了一个软件 MINg, 发行 Version 01 版本时,有一个功能 fun(int)
// 发行 Version 02 版本时,fun(int) 进行了功能改进,并且添加了一个功能 fun(double)
Version01::fun(1);
Version02::fun(1);
// 如果不加命名空间,则调用内联命名空间的函数。
fun(2);
fun(3.14);
system("pause");
return 0;
}
C++11特性:lambda 表达式
lambda.cpp
#include <algorithm>
#include <vector>
#include <iostream>
#include <string>
#include <array>
#include <functional>
/*
1、Lambda表达式的说明语法。
2、Lambda表达式在STL里的应用。
3、Lambda表达式在自定义函数里的应用。
*/
///////////////////////////////////////////////////////////////////////////////////////
class SortClass
{
public:
std::vector<int> m_vVector;
std::string m_strName;
SortClass() :m_strName{ "vic.MINg" } {}
template<typename T>
std::vector<int> Filter(T func) {
std::cout << "SortClass::Filter()" << std::endl;
return func(m_vVector);
}
typedef std::vector<int>(*pFilter)(std::vector<int>& input);
std::vector<int> Filter2(pFilter func) {
std::cout << "SortClass::Filter2()" << std::endl;
return func(m_vVector);
}
void Sort() {
std::cout << "SortClass::Sort()" << std::endl;
m_vVector.resize(5);
int index = 0;
int maxIndex = 0xffff;
std::generate(m_vVector.begin(), m_vVector.end(), [&index]() {return ++index; });
// 没有&符号是传值模式, 无法修改.
// 在类里的方法和函数, 如果需要引用 this实例, 那么需要在捕抓列表里添加&或this.
std::sort(m_vVector.begin(), m_vVector.end(), [&, maxIndex](int a, int b) {
// std::cout << "name : " << this->m_strName << std::endl;
// maxIndex = 0; 编译错误 expression must be a modifiable lvalue
return a > b;
});
for (auto one : m_vVector)
std::cout << "SortClass : " << "name = " << this->m_strName << ", one = " << one << std::endl;
// 按照升序排列
// 可以加后缀返回语法,即返回类型->bool
auto funcSortAscend = [](int a, int b)->bool {
return a < b;
};
std::cout << typeid(funcSortAscend).name() << std::endl;
std::sort(m_vVector.begin(), m_vVector.end(), funcSortAscend);
for (auto one : m_vVector)
std::cout << "SortClass : " << "name = " << this->m_strName << ", one = " << one << std::endl;
}
};
/////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char const *argv[])
{
std::cout << "----------1、Lambda表达式的说明语法。---------------" << std::endl;
// 语法:[捕捉列表:可选] (函数参数:可选) [->类型, 返回类型语法:可选] { 函数体 }
int a = 1, b = 2, c = 3;
auto retVal = [=, &a, &b]() mutable->int
{
printf("inner a[%d] b[%d] c[%d]\n", a, b, c);
a = 10;
b = 20;
c = 30;
printf("inner c2[%d]\n", c);
return a + b;
};
printf("sum[%d]\n", retVal());
printf("a[%d] b[%d] c[%d]\n", a, b, c);
system("pause");
std::cout << "----------2、Lambda表达式在STL里的应用。---------------" << std::endl;
class OneClass
{
public:
std::string m_strName;
int m_nID;
OneClass(const std::string name, const int id) : m_strName(name), m_nID(id) {}
static bool cmp(const OneClass a, const OneClass b)
{
return a.m_nID < b.m_nID;
}
};
OneClass OneA("A", 42);
OneClass OneB("B", 23);
OneClass OneC("C", 13);
OneClass OneD("D", 34);
OneClass OneE("E", 7);
std::array<OneClass, 5> classList = { OneA, OneB, OneC, OneD, OneE };
// C++98 排序
std::sort(classList.begin(), classList.end(), OneClass::cmp);
// C++11 Lambda 排序
std::sort( classList.begin(), classList.end(),
[](const OneClass a, const OneClass b) { return a.m_nID < b.m_nID; } );
// C++11 Lambda 遍历
for_each( classList.begin(), classList.end(),
[](const OneClass one) { std::cout << "OneClass : " << "name = " << one.m_strName << ", id = " << one.m_nID << std::endl; });
system("pause");
std::cout << "----------3、Lambda表达式在自定义函数里的应用。---------------" << std::endl;
// 局部类不能使用模板
SortClass sort;
sort.Sort();
// 可以传递lambda函数作为模板特例.
auto numbers = sort.Filter([](std::vector<int>& v1) {
std::vector<int> v2;
for (auto one : v1) {
if (one % 2)
v2.push_back(one);
}
return v2;
});
for (auto one : numbers)
std::cout << "SortClass.Filter() : " << "name = " << sort.m_strName << ", one = " << one << std::endl;
// 可以传递lambda函数作为参数是函数指针的方法。
auto numbers2 = sort.Filter2([](std::vector<int>& v1) {
std::vector<int> v2;
for (auto one : v1) {
if (one % 2)
v2.push_back(one);
}
return v2;
});
for (auto one : numbers2)
std::cout << "SortClass.Filter2() : " << "name = " << sort.m_strName << ", one = " << one << std::endl;
system("pause");
return 0;
}
C++11特性:noexcept 操作法阻止异常传播
noexceρt 是 C++11 是新增的函数修饰符,用来声明该函数不会抛出异常,如果抛出异常,那么异常不会传播,只会直接终止程序。相对的 throw() 这个在函数声明里的修饰符已经失效,它刚好和 noexcept 相反,它声明函数可能会抛出某种异常;还有另一种作用就是 noexcept 被设计比 throw() 更为简单高效的机制。
noexcept.cpp
#include <iostream>
#include <string>
#include <vector>
#include <string.h>
#include <stdint.h>
/*
1、语法和说明
2、常用用法
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Noexcept1()
{
auto str = std::string("abc").substr(10);
}
void Noexcept2() noexcept(false)
{
auto str = std::string("abc").substr(10);
}
void Noexcept3(std::vector<int>& args) noexcept(true)
{
std::cout << "args[1]: " << args[1] << std::endl;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char const *argv[])
{
std::cout << "----------1、语法和说明---------------" << std::endl;
// 语法
// func() noexcept 等价于 func() noexcept(true)
// func() noexcept(expression) expression = true (不会抛出异常) / expression = false (可以抛出异常)
// 说明
// 1、一个析构函数不应该抛出异常;一个自动生成的析构函数会在以下情况满足时隐式声明为 noexcept,就是所有它的类的成员又都 noexcept 的析构函数。
// 一个简单类默认就有 noexcept 析构
// 2、一个生成的复制和移动操作符会在以下情况满足时隐式声明为 noexcept,就是所有在这些操作符里用到的类实例有 noexcept 析构。
system("pause");
std::cout << "----------2、常见用法---------------" << std::endl;
try {
// 不加noexcept时,可以捕抓异常.
Noexcept1();
}
catch (const std::exception& e) {
std::cerr << e.what() << '\n';
}
try {
// noexcept的表达式为false时,可以捕抓异常.
Noexcept2();
}
catch (const std::exception& e) {
std::cerr << e.what() << '\n';
}
std::vector<int> args = { 1,2,3 };
args.clear();
std::cout << "args.capacity(): " << args.capacity() << std::endl;
args.reserve(0); // 内置规则不允许清空小的容量.
std::cout << "args.capacity(): " << args.capacity() << std::endl;
args = std::vector<int>();
auto func = [&args]() {
Noexcept3(args);
};
try {
// noexcept的表达式为里抛出异常时为true, 捕抓异常失败.
func();
}
catch (const std::exception& e) {
std::cerr << e.what() << '\n';
}
system("pause");
return 0;
}
C++11特性:nullptr 空指针常量
nullptr 空指针常量 :是C++11提供的内置空指针类型的常量,它的类型就是 std::nullptr_t 这个类型还是通过以下定义的,所以说它的类型并不是内置的。
typedefdecltype(nullptr)nullptr_t;
nullptr.cpp
#include <algorithm>
#include <vector>
#include <iostream>
#include <string>
#include <memory>
/*
1、nullptr 的作用。
2、nullprt & NULL。
*/
/////////////////////////////////////////////////////////////////////////////////////////////////
void Print(int i) {
std::cout << "i: " << i << std::endl;
}
void Print(const char* str) {
std::cout << "str: " << ((str) ? str : "") << std::endl;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char const *argv[])
{
std::cout << "----------1、nullptr 的作用---------------" << std::endl;
// nullptr空指针常量就是在重载函数里,传递 nullptr会调用指针参数的函数,而不会调用参数为int类型的函数。
Print(10);
Print("10");
// Print(NULL); // 编译错误, 因为 NULL可以表示整数0,或者指针. 两个重载函数不知道选哪个。
Print(nullptr); // 调用 Print(str); 只有这个作用, 在重载函数里能区分指针调用和整型调用。
system("pause");
std::cout << "----------2、nullprt & NULL。---------------" << std::endl;
// 不允许 nullptr 赋值给整型,NULL可以
//int i = nullptr; // 编译错误, 空指针类型不是int.
// 可以把NULL、0赋值给 std::nullptr_t 类型,其他值不可以
std::nullptr_t nl = NULL;
system("pause");
return 0;
}
C++11特性:rang-for 循环语句
循环语句 for(x:y) 是 C++11 新増的快速枚举的语句,基于范围的for语句( range-for)。使我们能减少代码量,提高开发效率。
STL库支持的有
容器类型:vector、set、list、map
序列类型:string
判断的依据就是:这个类必须有 begin() 和 end() 方法,返回枚举类型。或者支持标准的 std::begin(x) 和,std::end(x) 返回枚举类型。
rang-for.cpp
#include <algorithm>
#include <vector>
#include <iostream>
#include <map>
#include <time.h>
#include <string>
#include <string.h>
#include <stdint.h>
#include <typeinfo>
/*
1、C++98 循环语句 和 C++11的 rang-for 对比。
2、自定义类型支持 rang-for。
*/
/////////////////////////////////////////////////////////////////////////////////////////////////////
// 自定义数据类型实现枚举可以使用range-for
// 该类必须有 begin() 和 end() 方法,如果没有该方法,那么需要支持 std::begin(x) 和 std::end(x) 返回枚举类型。
class OneData
{
public:
OneData(std::initializer_list<int> list) {
v2 = list;
}
inline void Add(int a) {
v2.push_back(a);
}
std::vector<int>::iterator begin() {
return v2.begin();
}
std::vector<int>::iterator end() {
return v2.end();
}
private:
std::vector<int> v2;
};
/////////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char const *argv[])
{
std::cout << "----------1、C++98 循环语句 和 C++11的 rang-for 对比。---------------" << std::endl;
std::cout << "C++98 循环语句'" << std::endl;
std::vector<char> v1 = { 'a','b','c','d' };
// for 循环
for (size_t i = 0; i < v1.size(); i++) {
std::cout << "i-> " << i << ": value-> " << v1[i] << std::endl;
}
// while 循环
size_t index = 0;
while (index < v1.size()) {
std::cout << "index-> " << index << ": value-> " << v1[index] << std::endl;
++index;
}
// std::for_each 循环
std::for_each(v1.begin(), v1.end(), [](char one) {
std::cout << "value-> " << one << std::endl;
});
std::cout << "C++11的 rang-for'" << std::endl;
// 通过 range-for 修改值后打印
for (auto& one : v1) {
one = rand() % 26 + 'a';
}
for (auto one : v1) {
std::cout << "value-> " << one << std::endl;
}
system("pause");
std::cout << "----------2、自定义类型支持 rang-for。---------------" << std::endl;
OneData data{ 5, 6, 7 };
for (auto one : data) {
std::cout << "value-> " << one << std::endl;
}
system("pause");
return 0;
}
C++11特性:rvalue-reference 右值引用和移动语义
rvalue-reference.cpp
#include <algorithm>
#include <vector>
#include <iostream>
#include <string>
#include <stdlib.h>
#include <memory>
/*
1、右值引用的定义
2、移动语义,和右值的关系
3、常见用法
*/
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Print(const char* str, const char* title = "str") {
std::cout << title << ": " << ((str) ? str : "") << std::endl;
}
template<class T>
void swap(T& a, T& b) // "perfect swap" (almost)
{
T tmp = move(a); // could invalidate a
a = move(b); // could invalidate b
b = move(tmp); // could invalidate tmp
}
template <typename T>
void func(T t) {
std::cout << "in func" << std::endl;
}
template <typename T>
void relay(T&& t) {
std::cout << "in relay" << std::endl;
// std::forward() 完美转发:实现了参数在传递过程中保持其值属性的功能,即若是左值,则传递之后仍然是左值,若是右值,则传递之后仍然是右值。
func(std::forward<T>(t));
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char const *argv[])
{
std::cout << "----------1、右值引用的定义---------------" << std::endl;
// 右值引用:T&& 左值引用:T&
// 非常量的右值,通常说的是编译器生成的临时对象。
// 右值引用声明可以区别一个左值和右值
// 右值引用通过移动语义(std::move)产生,它可以把临时对象(一般不能引用)的资源移动到其他地方去。
// 右值引用也可以用来扩展临时对象的生命周期。
int&& i = std::move(1); // rvalue
std::string str1 = "vic.MINg";
std::string str2(std::move(str1));
Print(str2.c_str(), "str2"); // 输出 vic.MINg
Print(str1.c_str(), "str1"); // 输出空,已经被移动到str2里.
std::vector<std::string> vecs;
vecs.push_back(std::move(str2)); // 把 str2 里的资源再次移动到 vector 里, str2 为空
system("pause");
std::cout << "----------2、移动语义,和右值的关系---------------" << std::endl;
// 移动语义在swap里的应用;没有复制数据,只是数据在对象里传递. 减少了数据的构造过程
std::string first("first");
std::string second("second");
std::cout << "first data address: " << (int*)first.data() << std::endl;
std::cout << "second data address: " << (int*)second.data() << std::endl;
swap(first, second);
Print(first.c_str(), "first");
Print(second.c_str(), "second");
std::cout << "first data address: " << (int*)first.data() << std::endl;
std::cout << "second data address: " << (int*)second.data() << std::endl;
system("pause");
std::cout << "----------3、常见用法---------------" << std::endl;
// std::forward() 的使用
// 一个左值和右值的测试类。
class OneClass {
public:
OneClass(){
std::cout << "default constructor" << std::endl;
}
OneClass(const OneClass & t) {
std::cout << "lvalue constructor" << std::endl;
}
OneClass(OneClass && t) {
std::cout << "rvalue constructor" << std::endl;
}
~OneClass() {
std::cout << "destructor" << std::endl;
}
};
// 右值
relay(OneClass());
// 左值
OneClass oneClass;
relay(oneClass);
system("pause");
return 0;
}
C++11特性:user-defined-literals 用户定义的字面常量
user-defined-literals.cpp
#include <iostream>
#include <string>
#include <string.h>
#include <stdint.h>
/*
1、语法和说明
2、常见用法
3、注意事项
*/
/////////////////////////////////////////////////////////////////////////////////////////////////
long double operator"" _mm(long double x) { return x / 1000; }
long double operator"" _m(long double x) { return x; }
long double operator"" _km(long double x) { return x * 1000; }
size_t operator"" _len(char const * str, size_t size)
{
return size;
}
namespace CPP11
{
struct RGBA
{
uint8_t r, g, b, a;
RGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a) :r(r), g(g), b(b), a(a) {}
};
RGBA operator"" _RGBA(const char* str, size_t size)
{
const char* r = nullptr, *g = nullptr, *b = nullptr, *a = nullptr;
for (const char* p = str; p != str + size; ++p)
{
if (*p == 'r') r = p + 1;
if (*p == 'g') g = p + 1;
if (*p == 'b') b = p + 1;
if (*p == 'a') a = p + 1;
}
if (r == nullptr || g == nullptr || b == nullptr) throw;
if (a == nullptr)
{
return RGBA(atoi(r), atoi(g), atoi(b), 0);
}
else
{
return RGBA(atoi(r), atoi(g), atoi(b), atoi(a));
}
}
namespace MINg
{
//输出运算符重载
std::ostream& operator<<(std::ostream& os, const RGBA& color)
{
return os << "r=" << (int)color.r << " g=" << (int)color.g << " b=" << (int)color.b << " a=" << (int)color.a << std::endl;
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////
int main(int argc, char const *argv[])
{
std::cout << "----------1、语法和说明---------------" << std::endl;
// C++提供了一些字面常量用于内置类型。
auto l_0 = 1ULL; // unsigned long long
auto l_1 = 1L; // long
auto l_2 = 2LL; // long long
auto i_1 = 123; // int
auto i_2 = 123u; // unsigned int
auto od = 0xD0; // 十六进制表示的int
auto b1 = 0b101010; // 二进制表示的int
constexpr auto e1 = 010; // 八进制表示的int
auto d_1 = 1.2; // double
auto f_1 = 1.2f; // float
auto c_1 = 'a'; // char
auto s_1 = "abc"; // const char*
int32_t i32 = 2323; // 常用的整型声明.
// C++11新标准中引入了用户自定义字面量,也叫自定义后缀操作符,即通过实现一个后缀操作符,将申明了该后缀标识的字面量转化为需要的类型。
std::cout << "1.0_mm = " << 1.0_mm << std::endl; // 0.001
std::cout << "1.0_m = " << 1.0_m << std::endl; // 1
std::cout << "1.0_km = " << 1.0_km << std::endl; // 1000
system("pause");
std::cout << "----------2、常见用法---------------" << std::endl;
std::cout << "\"vic.MINg\"_len = " << "vic.MINg"_len << std::endl; //结果为4
system("pause");
std::cout << "----------3、注意事项---------------" << std::endl;
// 因为用户定义的字面常量定义多了有可能混淆和冲突,所以我们最好把它声明在某个命名空间里。
//自定义字面量来表示RGBA对象
using namespace CPP11;
using namespace CPP11::MINg;
std::cout << "r255 g255 b255 a40"_RGBA << std::endl;
system("pause");
return 0;
}




