C++11常用关键字

1.auto

1.1 定义

        auto早在C++98标准引入,在98标准含义为:声明变量拥有自动周期,但是这本身就是多余

在C++11中auto全新定义: 变量的自动类型推断

1.2 使用

std::vector str1 = {"nihao", "hello"};
for (std::vector::const_iterator it = str1.begin(); it != str1.end(); ++it)
{
    std::cout << *it << std::endl;
}

for (auto it = str1.begin(); it != str1.end(); ++it)
{
    std::cout << *it << std::endl;
}

1.3 注意事项

  • auto关键字使用必须初始化
  • 函数或者模板参数不能被声明为auto(C++11),C++14及其以后可以。
  • 初始化表达式为数组时,auoto关键字推导类型为指针.
  • auto & =  一定是一个左值引用,不允许绑定常量或将亡对象。
  • auto && 遇到左值推到左值引用,遇到右值推导右值引用
  • 用auto关键字推断变量类型时,会忽略引用修饰符.
     
    去引用
    {
    
    int a = 1;
    int &b = a;
    auto c = b;  //c展开为int 而非int&
    }
    
    去const
    {
    
    const int a = 10;
    auto b = a;
    
    b = 20; // 合法,修改b的值
    // a的值仍然是10,const修饰符保持不变
    
    const auto c = a;
    
    c = 30; // 非法,无法修改c的值
    // c仍然是10,const修饰符保持不变
    
    }

2.const\constexpr\const_case

2.1 定义

        const是c++本来就有的关键字,用来表示只读状态,但是通过一些手段还是可以进行修改,比如使用const_cast或者使用指针强项篡改。

        而constexpr才是真正意义上的常量,constexpr修饰的变量必须是在编译期间就可以确定的值,如果无法再编译期间确定,那么就会报错。所以constexpr既有const不变特性,也会像#define在编译的时候进行求值和类型检查。

2.2 使用

         使用const_case去除const修饰:

//方法一: 低版本编译器可以实现
int const a = 5;
int *p = (int *)&a;
*p = 55;

//方法二:
 const int a = 10;
    // 使用const_cast去除const修饰符
 int& b = const_cast(a);
    // 修改b的值
 b = 20;

         使用constexpr

constexpr int a = 10;

2.3 注意事项

  • C++11中,constexpr变量必须是整型、浮点型或者指针类型,而在C++14中,constexpr变量可以是任意的字面类型,还可以用于构造函数。
  • C++11中,constexpr函数可以进行递归,C++14可以constexpr函数可以使用局部变量和范围循环。
  • C++11中,constexpr函数只能将返回值写入一个return 表达式,不能存在多个return,C++14可以
  • constexpr 不能使用动态内存分配、不能使用虚函数、不能使用非常量表达式。
  • constexpr在编译的时候可以进行求值或者类型检查。

3.thread_local

3.1 定义

C++ 有几种存储周期

序号 类型 备注
1

自动存储周期    

right-aligned 该关键字用于两种情况:1. 声明变量时, 根据初始化表达式自动推断变量类型。2. 声明函数作为函数返回值的占位符。

自动存储周期适用于在函数内部声明的局部变量,默认情况下,它们的存储周期是在每次函数调用时创建和销毁。这些变量存储在栈上。

2 静态存储周期 全局\static变量只初始化一次,除此之外它还有可见性的属性:1. static修饰函数内的“局部”变量时,表明它不需要在进入或离开函数时创建或销毁。且仅在函数内可见。2. static修饰全局变量时,表明该变量仅在当前(声明它的)文件内可见。3. static修饰类的成员变量时,则该变量被该类的所有实例共享。
3 register无存储周期 寄存器变量。该变量存储在CPU寄存器中,而不是RAM(栈或堆)中。该变量的最大尺寸等于寄存器的大小。由于是存储于寄存器中,因此不能对该变量进行取地址操作。
4 动态存储周期 动态存储周期适用于使用new运算符分配的动态内存,以及使用malloc函数分配的内存。这些变量在显式释放之前一直存在,存储在堆上。
5 线程存储周期 thread_local关键字修饰的变量具有线程周期(thread duration),这些变量(或者说对象)在线程开始的时候被生成(allocated),在线程结束的时候被销毁(deallocated)。并且每 一个线程都拥有一个独立的变量实例。

thread_local 关键词只对声明于命名空间作用域的对象、声明于块作用域的对象及静态数据成员允许。它指示对象拥有线程存储期。它能与 static 或 extern 结合,以分别指定内部或外部链接(除了静态数据成员始终拥有外部链接),但附加的 static 不影响存储期。 线程存储期: 对象的存储在线程开始时分配,而在线程结束时解分配。每个线程拥有其自身的对象实例。唯有声明为 thread_local 的对象拥有此存储期。 thread_local 能与 static 或 extern 一同出现,以调整链接。

        thread_local 可以和static 与 extern关键字联合使用,这将影响变量的链接属性。

1)当 thread_local 关键字与 static 关键字一起使用时,它指示变量具有静态存储持续时间和线程本地存储(TLS)链接属性。每个线程都会有自己的变量副本,每个线程对该变量的访问都是独立的,不会相互干扰。注:附加的 static 不影响存储期,thread_local 局部变量自带static,可加可不加,但作为类成员变量必须加static        。

2)当 thread_local 关键字与 extern 关键字一起使用时,它指示变量具有外部链接属性和线程本地存储(TLS)链接属性。每个线程都会有自己的变量副本,每个线程对该变量的访问都是独立的,不会相互干扰。
      

3.2 使用

  例如 thread_local与static联用

#include 
#include 

void threadFunction() {
    // static thread_local int count = 0;
    static int count = 0;

    count++;
    std::cout << "Thread ID: " << std::this_thread::get_id() << ", Count: " << count << std::endl;
}

int main() {
    std::thread t1(threadFunction);
    std::thread t2(threadFunction);
    std::thread t3(threadFunction);

    t1.join();
    t2.join();
    t3.join();
    return 0;
}

输出结果:

Thread ID: 139899327997504, Count: 2
Thread ID: 139899336390208, Count: 3
Thread ID: 139899319604800, Count: 3

 可以看到所有线程共享count变量,变量值累加。

若将static int count = 0; 换成static thread_local int count = 0,输出结果:

Thread ID: 140481652586048, Count: 1
Thread ID: 140481644193344, Count: 1
Thread ID: 140481635800640, Count: 1

每个线程拥有自己独立变量副本 。

3.3 注意事项

  • 确保线程安全
  • 避免内存泄露或额外开销
  • 注意初始化顺序,尽量避免使用多个thread_local变量之间存在依赖关系的情况

4.noexcept

4.1 定义

        用于指示函数是否可能引发异常。

        分为noexcept指定符和noexcept运算符

4.2 使用

做noexcept指定符

void f() noexcept;  // 函数 f() 不抛出
void (*fp)() noexcept(false); // fp 指向可能抛出的函数
void g(void pfa() noexcept);  // g 接收指向不抛出的函数的指针

做noexcept运算符 

noexcept(may_throw());
auto lno_throw = []() noexcept {};

4.3 注意事项

  • 如果一个被标记为noexcept的函数或表达式实际上引发了异常,程序将会终止

5.override

5.1 定义

        主要用来声明子类函数继承于父类的虚函数。

        使用override好处是编译器可以在编译期间检查是否正确地覆盖了基类中的虚函数,使代码更加清晰易懂,避免因为函数名称或参数列表不匹配而导致的错误。

5.2 使用

class Animal
{
    virtual void Eat()
    {
        std::cout << "animal eat" << std::endl;
    }
};

class Dog : public Animal
{
    virtual void eat() override
    {
        std::cout << "dog eat" << std::endl;
    }
};

在上述代码中 本意是Dog eat继承 Animal类,如果将Eat写成eat则会报错。

5.3 注意事项

  • 子类重写的方法的方法名形参列表与父类被重写的方法的方法名和形参列表相同

  • 子类重写的方法的权限修饰符大于等于父类被重写的方法的权限修饰符

6.dectype

6.1 定义

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

6.2 使用

int value = 10;
auto varname = value;
decltype(exp) varname = value;
decltype(10.8) x;  //x 被推导成了 double

6.3 注意事项

  • auto在初始化时进行类型推导,而decltype直接查询表达式的类型,可以用于任何表达式,包括没有初始化的变量。

  • decltype可以用于推导表达式的类型,而auto只能用于推导变量的类型。

  • decltype保留类型的修饰符(const、引用等),而auto会忽略类型的修饰符。

  • auto在编译时进行类型推导,decltype在运行时才确定表达式的类型。

7.final

7.1 定义

        Final关键字就是当我们不希望某个函数被子类重写(修饰类就不允许有派生类)时,我们只需要添加一个Final,这个时候编译的时候就会报错了。

7.2 使用

class Animal
{
    virtual void Eat() final
    {
        std::cout << "animal eat" << std::endl;
    }
};

class Dog : public Animal
{
    virtual void Eat() override
    {
        std::cout << "dog eat" << std::endl;
    }
};
keyword.cpp:85:18: 错误:virtual function ‘virtual void Dog::Eat()’ overriding final function
   85 |     virtual void Eat() override
      |                  ^~~
keyword.cpp:77:18: 附注:overridden function is ‘virtual void Animal::Eat()’
   77 |     virtual void Eat() final
      |                  ^~~

7.3 注意事项

        无

8.default、delete

        当声明一个类时,如果这个类没有构造函数,那么编译器会自动给这个类生成一个构造函数,但是如果我们加了其他构造函数,编译器就不会生成默认构造函数,如果还需要默认构造函数,那么就需要自己手动写一个,比较麻烦,为了解决这个问题,c++11引入了default关键字。

        如果我们声明了一个类,不希望调用拷贝构造函数,这个时候我们一般的做法是把拷贝构造函数声明为private函数,但是这个时候还是会有被调用的风险,比如友元类或者类的成员函数。在c++11中,引入了delete关键字来做这件事。

9.nullptr

#undef NULL
#ifdef __cplusplus
#  if !defined(__MINGW32__) && !defined(_MSC_VER)
#    define NULL __null
#  else
#    define NULL 0
#  endif
#else
#  define NULL ((void*)0)
#endif

在C++中, NULL直接被定义为0, 在c中NULL被定义为((void *)0)。原因是C语言可以进行隐式转换,C++需要显示写出类型转换来,设计为NULL 0  一来为了方便,而来满足零初始化概念。

  C++11中引入了一个关键字nullptr,用来表示空指针,并且推荐使用nullptr来替代NULL。

void fun(int)
{
    std::cout << "int" << std::endl;
}

void fun(char*)
{
    std::cout << "char*" << std::endl;
}

int main()
{
    fun(NULL); // 输出 int
    fun(nullptr); // 输出 char*
    return 0;
}

你可能感兴趣的:(C++,c++,java,开发语言)