点进来,你看到的就是全网最全c++11新特性(持续整理更新中)

C++11 是第二个真正意义上的 C++ 标准,也是 C++ 的一次重大升级。C++11增加了很多现代编程语言的特性,比如自动类型推导、智能指针、lambda 表达式等,这使得 C++ 看起来又酷又潮,一点也不输 Java 和C#。

GitHub地址:https://github.com/0voice/cpp_new_features#cpp_11
在这里插入图片描述

Linuxc/c++后台开发方向技术文章资料电子书整理

2021-2000道大厂面经/面试题整理(腾讯、字节、阿里、百度、京东等)

c++11新特性

关键字

新增关键字

thread_local

thread_local是C++11增加的存储类指定符

C++中有4种存储周期:

  • automatic
  • static
  • dynamic
  • thread

有且只有thread_local关键字修饰的变量具有线程周期(thread duration),这些变量(或者说对象)在线程开始的时候被生成(allocated),在线程结束的时候被销毁(deallocated)。并且每 一个线程都拥有一个独立的变量实例(Each thread has its own instance of the object)。thread_local 可以和static 与 extern关键字联合使用,这将影响变量的链接属性(to adjust linkage)。

那么,哪些变量可以被声明为thread_local?可以是以下3类:

  • 命名空间下的全局变量
  • 类的static成员变量
  • 本地变量

thread_local案例

#include <iostream>
#include <mutex>
#include <string>
#include <thread>
 
thread_local unsigned int rage = 1;
std::mutex cout_mutex;
 
void increase_rage(const std::string& thread_name) {
  ++rage; // 在锁外修改 OK ;这是线程局域变量
  std::lock_guard<std::mutex> lock(cout_mutex);
  std::cout << "Rage counter for " << thread_name << ": " << rage << '\n';
}
 
void test() {
  thread_local int i = 0;
  printf("id=%d, n=%d\n", std::this_thread::get_id(), i);
  i++;
}
 
void test2() {
  test();
  test();
}
 
int main() {
  std::thread a(increase_rage, "a"), b(increase_rage, "b");
 
  {
    std::lock_guard<std::mutex> lock(cout_mutex);
    std::cout << "Rage counter for main: " << rage << '\n';
  }
 
  a.join();
  b.join();
 
  std::thread t1(test);
  std::thread t2(test);
  t1.join();
  t2.join();
 
  std::thread t3(test2);
  t3.join();
 
  system("pause");
  return 0;
}

static_assert

static_assert

struct MyClass
{
    char m_value;
};

struct MyEmptyClass
{
    void func();
};

// 确保MyEmptyClass是一个空类(没有任何非静态成员变量,也没有虚函数)
static_assert(std::is_empty<MyEmptyClass>::value, "empty class needed");

//确保MyClass是一个非空类
static_assert(!std::is_empty<MyClass>::value, "non-empty class needed");

template <typename T, typename U, typename V>
class MyTemplate
{
    // 确保模板参数T是一个非空类
    static_assert(
        !std::is_empty<T>::value,
        "T should be n non-empty class"
    );

    // 确保模板参数U是一个空类
    static_assert(
        std::is_empty<U>::value,
        "U should be an empty class"
    );

    // 确保模板参数V是从std::allocator<T>直接或间接派生而来,
    // 或者V就是std::allocator<T>
    static_assert(
        std::is_base_of<std::allocator<T>, V>::value,
        "V should inherit from std::allocator<T>"
    );

};

// 仅当模板实例化时,MyTemplate里面的那三个static_assert才会真正被演算,
// 藉此检查模板参数是否符合期望
template class MyTemplate<MyClass, MyEmptyClass, std::allocator<MyClass>>;

nullptr

nullptr
nullptr关键字用于标识空指针,是std::nullptr_t类型的(constexpr)变量。它可以转换成任何指针类型和bool布尔类型(主要是为了兼容普通指针可以作为条件判断语句的写法),但是不能被转换为整数。

char *p1 = nullptr;     // 正确
int  *p2 = nullptr;     // 正确
bool b = nullptr;       // 正确. if(b)判断为false
int a = nullptr;        // error

noexcept

noexcept
noexcept有两类作用:noexcept指定符和noexcept运算符

  • noexcept 指定符
void f() noexcept;  // 函数 f() 不抛出
void (*fp)() noexcept(false); // fp 指向可能抛出的函数
void g(void pfa() noexcept);  // g 接收指向不抛出的函数的指针
// typedef int (*pf)() noexcept; // 错误

  • noexcept运算符
#include <iostream>
#include <utility>
#include <vector>
 
// noexcept 运算符 
void may_throw() {};
void no_throw() noexcept {};
auto lmay_throw = [] {};
auto lno_throw = []() noexcept {};
 
class T {
};
class T1 {
public:
  ~T1() {}
};
class T2 {
public:
  ~T2() {}
  int v;
};
class T3 {
public:
  ~T3() {}
  std::vector<int> v;
};
class T4 {
public:
  std::vector<int> v;
};
 
int main()
{
  T t;
  T1 t1;
  T2 t2;
  T3 t3;
  T4 t4;
 
  std::vector<int> vc;
 
  std::cout << std::boolalpha
    << "Is may_throw() noexcept? " << noexcept(may_throw()) << '\n'
    << "Is no_throw() noexcept? " << noexcept(no_throw()) << '\n'
    << "Is lmay_throw() noexcept? " << noexcept(lmay_throw()) << '\n'
    << "Is lno_throw() noexcept? " << noexcept(lno_throw()) << '\n'
    << "Is ~T1() noexcept? " << noexcept(std::declval<T1>().~T1()) << '\n'
    << '\n'
    << '\n'
 
    << "Is T(rvalue T) noexcept? " << noexcept(T(std::declval<T>())) << '\n'
    << "Is T(lvalue T) noexcept? " << noexcept(T(t)) << '\n'
    << '\n'
 
    << "Is T1(rvalue T1) noexcept? " << noexcept(T1(std::declval<T1>())) << '\n'
    << "Is T1(lvalue T1) noexcept? " << noexcept(T1(t1)) << '\n'
    << '\n'
 
    << "Is T2(rvalue T2) noexcept? " << noexcept(T2(std::declval<T2>())) << '\n'
    << "Is T2(lvalue T2) noexcept? " << noexcept(T2(t2)) << '\n'
    << '\n'
 
    << "Is T3(rvalue T3) noexcept? " << noexcept(T3(std::declval<T3>())) << '\n'
    << "Is T3(lvalue T3) noexcept? " << noexcept(T3(t3)) << '\n'
    << '\n'
 
    << "Is T4(rvalue T4) noexcept? " << noexcept(T4(std::declval<T4>())) << '\n'
    << "Is T4(lvalue T4) noexcept? " << noexcept(T4(t4)) << '\n'
    << '\n'
 
    << "Is std::vector<int>(rvalue std::vector<int>) noexcept? " << noexcept(std::vector<int>(std::declval<std::vector<int>>())) << '\n'
    << "Is std::vector<int>(lvalue std::vector<int>) noexcept? " << noexcept(std::vector<int>(vc)) << '\n';
 
  system("pause");
  return 0;
}

decltype

decltype

decltype类型说明符,它的作用是选择并返回操作数的数据类型,在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。 decltype用法

  • 基本用法
int getSize();

int main(void)
{
    int tempA = 2;
    
    /*1.dclTempA为int*/
    decltype(tempA) dclTempA;
    /*2.dclTempB为int,对于getSize根本没有定义,但是程序依旧正常,因为decltype只做分析,并不调用getSize,*/
    decltype(getSize()) dclTempB;

    return 0;
}

  • 与const结合
double tempA = 3.0;
    const double ctempA = 5.0;
    const double ctempB = 6.0;
    const double *const cptrTempA = &ctempA;
    
    /*1.dclTempA推断为const double(保留顶层const,此处与auto不同)*/
    decltype(ctempA) dclTempA = 4.1;
    /*2.dclTempA为const double,不能对其赋值,编译不过*/
    dclTempA = 5;
    /*3.dclTempB推断为const double * const*/
    decltype(cptrTempA) dclTempB = &ctempA;
    /*4.输出为4(32位计算机)和5*/
    cout<<sizeof(dclTempB)<<"    "<<*dclTempB<<endl;
    /*5.保留顶层const,不能修改指针指向的对象,编译不过*/
    dclTempB = &ctempB;
    /*6.保留底层const,不能修改指针指向的对象的值,编译不过*/
    *dclTempB = 7.0;
```C

* 与引用结合
```C
int tempA = 0, &refTempA = tempA;

    /*1.dclTempA为引用,绑定到tempA*/
    decltype(refTempA) dclTempA = tempA;
    /*2.dclTempB为引用,必须绑定到变量,编译不过*/
    decltype(refTempA) dclTempB = 0;
    /*3.dclTempC为引用,必须初始化,编译不过*/
    decltype(refTempA) dclTempC;
    /*4.双层括号表示引用,dclTempD为引用,绑定到tempA*/
    decltype((tempA)) dclTempD = tempA;
    
    const int ctempA = 1, &crefTempA = ctempA;
    
    /*5.dclTempE为常量引用,可以绑定到普通变量tempA*/
    decltype(crefTempA) dclTempE = tempA;
    /*6.dclTempF为常量引用,可以绑定到常量ctempA*/
    decltype(crefTempA) dclTempF = ctempA;
    /*7.dclTempG为常量引用,绑定到一个临时变量*/
    decltype(crefTempA) dclTempG = 0;
    /*8.dclTempH为常量引用,必须初始化,编译不过*/
    decltype(crefTempA) dclTempH;
    /*9.双层括号表示引用,dclTempI为常量引用,可以绑定到普通变量tempA*/
    decltype((ctempA))  dclTempI = ctempA;

  • 与指针结合
int tempA = 2;
int *ptrTempA = &tempA;
/*1.常规使用dclTempA为一个int *的指针*/
decltype(ptrTempA) dclTempA;
/*2.需要特别注意,表达式内容为解引用操作,dclTempB为一个引用,引用必须初始化,故编译不过*/
decltype(*ptrTempA) dclTempB;

decltype总结 decltype和auto都可以用来推断类型,但是二者有几处明显的差异: 1.auto忽略顶层const,decltype保留顶层const; 2.对引用操作,auto推断出原有类型,decltype推断出引用; 3.对解引用操作,auto推断出原有类型,decltype推断出引用; 4.auto推断时会实际执行,decltype不会执行,只做分析。 总之在使用中过程中和const、引用和指针结合时需要特别小心。

constexpr

constexpr
constexpr意义 将变量声明为constexpr类型以便由编译器来验证变量是否是一个常量表达式(不会改变,在编译过程中就能得到计算结果的表达式)。是一种比const更强的约束,这样可以得到更好的效率和安全性。

constexpr用法

  • 修饰函数
/*1.如果size在编译时能确定,那么返回值就可以是constexpr,编译通过*/
constexpr int getSizeA(int size)
{
    return 4*size;
}
/*2.编译通过,有告警:在constexpr中定义变量*/
constexpr int getSizeB(int size)
{
    int index = 0;
    return 4;
}
/*3.编译通过,有告警:在constexpr中定义变量(这个有点迷糊)*/
constexpr int getSizeC(int size)
{
    constexpr int index = 0;
    return 4;
}
/*4.编译通过,有告警:使用了if语句(使用switch也会告警)*/
constexpr int getSizeD(int size)
{
    if(0)
    {}
    return 4;
}
/*5.定义变量并且没有初始化,编译不过*/
constexpr int getSizeE(int size)
{
    int index;
    return 4;
}
/*6.rand()为运行期函数,不能在编译期确定,编译不过*/
constexpr int getSizeF(int size)
{
    return 4*rand();
}
/*7.使用了for,编译不过*/
constexpr int getSizeG(int size)
{
    for(;0;)
    {}
    return 4*rand();
}

  • 修改类型
int tempA;
cin>>tempA;

const int ctempA = 4;
const int ctempB = tempA;
/*1.可以再编译器确定,编译通过*/
constexpr int conexprA = 4;
constexpr int conexprB = conexprA + 1;
constexpr int conexprC = getSizeA(conexprA);
constexpr int conexprD = ctempA;
/*2.不能在编译期决定,编译不过*/
constexpr int conexprE = tempA;
constexpr int conexprF = ctempB;

  • 修饰指针
int g_tempA = 4;
const int g_conTempA = 4;
constexpr int g_conexprTempA = 4;

int main(void)
{
    int tempA = 4;
    const int conTempA = 4;
    constexpr int conexprTempA = 4;
    
    /*1.正常运行,编译通过*/
    const int *conptrA = &tempA;
    const int *conptrB = &conTempA;
    const int *conptrC = &conexprTempA;
    /*2.局部变量的地址要运行时才能确认,故不能在编译期决定,编译不过*/
    constexpr int *conexprPtrA = &tempA;
    constexpr int *conexprPtrB = &conTempA
    constexpr int *conexprPtrC = &conexprTempA;
    /*3.第一个通过,后面两个不过,因为constexpr int *所限定的是指针是常量,故不能将常量的地址赋给顶层const*/
    constexpr int *conexprPtrD = &g_tempA;
    constexpr int *conexprPtrE = &g_conTempA
    constexpr int *conexprPtrF = &g_conexprTempA;
    /*4.局部变量的地址要运行时才能确认,故不能在编译期决定,编译不过*/
    constexpr const int *conexprConPtrA = &tempA;
    constexpr const int *conexprConPtrB = &conTempA;
    constexpr const int *conexprConPtrC = &conexprTempA;
    /*5.正常运行,编译通过*/
    constexpr const int *conexprConPtrD = &g_tempA;
    constexpr const int *conexprConPtrE = &g_conTempA;
    constexpr const int *conexprConPtrF = &g_conexprTempA;

    return 0;
}

  • 修饰引用
int g_tempA = 4;
const int g_conTempA = 4;
constexpr int g_conexprTempA = 4;

int main(void)
{
    int tempA = 4;
    const int conTempA = 4;
    constexpr int conexprTempA = 4;
    /*1.正常运行,编译通过*/
    const int &conptrA = tempA;
    const int &conptrB = conTempA;
    const int &conptrC = conexprTempA;
    /*2.有两个问题:一是引用到局部变量,不能再编译器确定;二是conexprPtrB和conexprPtrC应该为constexpr const类型,编译不过*/
    constexpr int &conexprPtrA = tempA;
    constexpr int &conexprPtrB = conTempA 
    constexpr int &conexprPtrC = conexp
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值