文章目录
前言
C++11开始添加了很多好用的新特性,个人认为想要真正掌握这些特性还是需要多读代码,多应用这些特性,本文只记录了一些个人用过的,并结合自己的使用体验讲了一下使用场景
nullptr和NULL
C++11中NULL表示的是一个int类型的0,用nullptr来表示空指针以避免野指针,悬空指针等问题,nullptr允许隐式转换为其它类型的指针
const和constexpr
1.const
const在C++11之前就有,用处在于变量只读
以及修饰常量
,但要注意两者是不一样的
- 变量只读:比如函数中我们不希望传入的参数被修改
void func (const int num)
- 修饰常量:有时提前定义一个常量,并用于描述数组大小
const int a = 20; int arr[a];
2.constptr
首先讲明constexpr和const的区别:在修饰常量方面,二者都可用,但是constexpr达不到修饰变量只读的作用,所以在参数传递的时候只能用const来修饰其只读的效果
1.使用意义:constexpr所声明的表达式或函数,必须在编译期间完成运算,这就意味着其作用主要是用于优化程序,个人认为,如果不是厉害的cpper,用好const就够了。
2.使用场景:
声明变量:
constexpr int max_size = 100; // 编译时常量
const int max_size = 100; // 编译时const
const int max_size = N;//运行时const
声明函数(前提是所有参数也都是编译时常量):
constexpr int add(int x, int y) {
return x + y;
}
constexpr int val = add(5, 3); // 编译时求值
auto和decltype
1.auto用于编译期间类型推导,主要用于stl的遍历,不能用于形参的推导
2.decltype用于表达式类型的推导,其也是在编译阶段实现的,使用 decltype推导出的是表达式类型的引用,decltype通常与尾返回类型推导结合使用,来获取函数的返回类型,而decltype前置时变量会未定义,如:decltype(x+y) add(T x, U y)是不对的,正确的应该是这样
template<typename T, typename U>
auto add2(T x, U y) -> decltype(x+y){
return x + y;
}
在C++11之前,函数的返回值类型必须显示的定义在函数前面,在C++11及以后的标准中可以使用尾返回类型推导来让编译器推导函数的返回类型
lambda表达式
lambda表达式定义了一个匿名函数,并且可以捕获一定范围内的变量。其最大的好处就是使用时再定义
[capture](params) opt -> ret {body;};
[] - 不捕捉任何变量
[&] - 捕获外部作用域中所有变量, 并作为引用在函数体内使用 (按引用捕获)
[=] - 捕获外部作用域中所有变量, 并作为副本在函数体内使用 (按值捕获) 拷贝的副本在匿名函数体内部是只读的
- opt:函数选项,没有可以不写
一般来说,我们使用lambda表达式只需要关注捕获列表,函数参数,函数体即可,举个例子,sort函数默认进行从小到大的排序,如果我们希望修改排序规则,那么就要在sort的第三个参数位置传入一个函数来修改规则:
vector<int> nums;
sort(nums.begin(), nums.end(), [&] (int a,int b){
return a>b;
};)
function和bind
1.function的目地在于封装各种各样的可调用实体,形成一个新的可调用的function对象,另外function对象是对可调用对象的一种类型安全的包装,相比于类型不安全的函数指针更适合作为回调函数。
个人在使用中,主要是用function包装一个仅在本函数中使用的函数,通过function+lambda表达式来实现,举个例子:在进行图论的算法题中经常会用到dfs算法,我们可以直接在main函数中定义dfs函数,并实现调用
int main()
{
//这里包装的dfs函数可以在
function<void(int,int)> dfs = [&](int x, int y){
//函数逻辑
};
...
dfs(3,5);
}
可调用对象主要有以下几种:
- 重载了函数调用运算符
()
的类的对象,即为函数对象(仿函数) - 函数
- 函数指针
- lambda表达式
2.bind
它主要用于将可调用对象(如函数、成员函数、函数对象)与其参数绑定,创建一个新的可调用对象。例如:
void print(int a, int b) {
std::cout << a << ", " << b << std::endl;
}
class Foo {
public:
void display(int x) {
std::cout << x << std::endl;
}
};
int main() {
// 绑定全局函数的参数
auto boundFunc = std::bind(print, 42, std::placeholders::_1);
boundFunc(24); // 输出: 42, 24
Foo foo;
// 绑定成员函数及其对象
auto boundMemberFunc = std::bind(&Foo::display, &foo, std::placeholders::_1);
boundMemberFunc(15); // 输出: 15
}
std::placeholders::_1、std::placeholders::_2…这些表示的是参数占位符,通常表示未进行绑定的那些参数
右值引用
1.右值和左值:左值可以取地址,右值不能取地址。
2.右值引用,用&&表示,它主要用于支持移动语义(Move Semantics)和完美转发(Perfect Forwarding),右值引用允许我们安全地引用临时对象
3.右值引用通常用于延长临时对象的生命周期,使得可以直接使用它,而不是把它拷贝给我们自己定义对象,它提高了程序的效率,比如说有时我们会通过一个临时对象来构造我们的对象,如果这个临时对象本身就很大,它创建出来已经耗费了一部分系统资源,然后还要再拷贝给我们自己的对象,拷贝完再销毁这个临时对象,整个过程的开销是很大的。
4.通常用于移动构造函数和移动赋值操作符重载函数
Example(Example&& other){}
Example& operator=(Example&& other){
return *this;
}
移动语义move
在C++11添加了右值引用,并且不能使用左值初始化右值引用,如果想要使用左值初始化一个右值引用需要借助std::move()函数,使用std::move方法可以将左值转换为右值。使用这个函数并不能移动任何东西,而和移动构造函数一样都具有移动语义,将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存拷贝
智能指针
智能指针对于C++的内存安全来说是一个重大的更新,值得我们单独讲一下,看我的这篇文章智能指针详解
标签:11,const,函数,右值,必知,C++,int,对象 From: https://blog.csdn.net/m0_56049283/article/details/136963312