? ? ? ? auto早在C++98标准引入,在98标准含义为:声明变量拥有自动周期,但是这本身就是多余
在C++11中auto全新定义: 变量的自动类型推断
std::vector<std::string> str1 = {"nihao", "hello"};
for (std::vector<std::string>::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;
}
去引用
{
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修饰符保持不变
}
????????const是c++本来就有的关键字,用来表示只读状态,但是通过一些手段还是可以进行修改,比如使用const_cast或者使用指针强项篡改。
? ? ? ? 而constexpr才是真正意义上的常量,constexpr修饰的变量必须是在编译期间就可以确定的值,如果无法再编译期间确定,那么就会报错。所以constexpr既有const不变特性,也会像#define在编译的时候进行求值和类型检查。
? ? ? ? ?使用const_case去除const修饰:
//方法一: 低版本编译器可以实现
int const a = 5;
int *p = (int *)&a;
*p = 55;
//方法二:
const int a = 10;
// 使用const_cast去除const修饰符
int& b = const_cast<int&>(a);
// 修改b的值
b = 20;
? ? ? ? ?使用constexpr
constexpr int a = 10;
constexpr
变量必须是整型、浮点型或者指针类型,而在C++14中,constexpr
变量可以是任意的字面类型,还可以用于构造函数。C++ 有几种存储周期
序号 | 类型 | 备注 |
---|---|---|
1 | auto | right-aligned 该关键字用于两种情况:1. 声明变量时, 根据初始化表达式自动推断变量类型。2. 声明函数作为函数返回值的占位符。 |
2 | static | static变量只初始化一次,除此之外它还有可见性的属性:1. static修饰函数内的“局部”变量时,表明它不需要在进入或离开函数时创建或销毁。且仅在函数内可见。2. static修饰全局变量时,表明该变量仅在当前(声明它的)文件内可见。3. static修饰类的成员变量时,则该变量被该类的所有实例共享。 |
3 | register | 寄存器变量。该变量存储在CPU寄存器中,而不是RAM(栈或堆)中。该变量的最大尺寸等于寄存器的大小。由于是存储于寄存器中,因此不能对该变量进行取地址操作。 |
4 | extern | 引用一个全局变量。当在一个文件中定义了一个全局变量时,就可以在其它文件中使用extern来声明并引用该变量。 |
5 | mutable | 仅适用于类成员变量。以mutable修饰的成员变量可以在const成员函数中修改。 |
6 | thread_local | 线程周期。 |
????????thread_local关键字修饰的变量具有线程周期(thread duration),这些变量(或者说对象)在线程开始的时候被生成(allocated),在线程结束的时候被销毁(deallocated)。并且每 一个线程都拥有一个独立的变量实例。
????????thread_local 可以和static 与 extern关键字联合使用,这将影响变量的链接属性。
? ? ??
? 例如 thread_local与static联用
#include <iostream>
#include <thread>
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
每个线程拥有自己独立变量副本 。
thread_local
变量之间存在依赖关系的情况????????用于指示函数是否可能引发异常。
? ? ? ? 分为noexcept指定符和noexcept运算符
做noexcept指定符
void f() noexcept; // 函数 f() 不抛出
void (*fp)() noexcept(false); // fp 指向可能抛出的函数
void g(void pfa() noexcept); // g 接收指向不抛出的函数的指针
做noexcept运算符?
noexcept(may_throw());
auto lno_throw = []() noexcept {};
noexcept
的函数或表达式实际上引发了异常,程序将会终止? ? ? ? 主要用来声明子类函数继承于父类的虚函数。
? ? ? ? 使用override好处是编译器可以在编译期间检查是否正确地覆盖了基类中的虚函数,使代码更加清晰易懂,避免因为函数名称或参数列表不匹配而导致的错误。
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则会报错。
子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
子类重写的方法的权限修饰符大于等于父类被重写的方法的权限修饰符
? ? ? ? decltype类型说明符,它的作用是选择并返回操作数的数据类型,在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。
int value = 10;
auto varname = value;
decltype(exp) varname = value;
decltype(10.8) x; //x 被推导成了 double
auto在初始化时进行类型推导,而decltype直接查询表达式的类型,可以用于任何表达式,包括没有初始化的变量。
decltype
可以用于推导表达式的类型,而auto
只能用于推导变量的类型。
decltype
保留类型的修饰符(const、引用等),而auto
会忽略类型的修饰符。
auto
在编译时进行类型推导,decltype在运行时才确定表达式的类型。
????????Final关键字就是当我们不希望某个函数被子类重写(修饰类就不允许有派生类)时,我们只需要添加一个Final,这个时候编译的时候就会报错了。
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
| ^~~
? ? ? ? 无
????????当声明一个类时,如果这个类没有构造函数,那么编译器会自动给这个类生成一个构造函数,但是如果我们加了其他构造函数,编译器就不会生成默认构造函数,如果还需要默认构造函数,那么就需要自己手动写一个,比较麻烦,为了解决这个问题,c++11引入了default关键字。
????????如果我们声明了一个类,不希望调用拷贝构造函数,这个时候我们一般的做法是把拷贝构造函数声明为private函数,但是这个时候还是会有被调用的风险,比如友元类或者类的成员函数。在c++11中,引入了delete关键字来做这件事。
#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;
}