IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++、auto decltype constexpr lambda表达式 -> 正文阅读

[C++知识库]C++、auto decltype constexpr lambda表达式

1. auto

auto 关键字可以根据初始化值自动推导所定义变量的数据类型,其作用于编译阶段。
auto 通常用于迭代器的定义,因为其具体数据类型由于模板的原因可能十分冗长,使用 auto 可以使得代码更加简洁。
auto 可能造成一定的代码阅读理解困难,所以如果该变量的类型不是显而易见的最好不要使用。
auto 不能作为函数的参数类型,但在 C++14 中可作为返回类型。
auto 也不能作为类普通数据成员的数据类型,除了 (const static) 变量,并且必须在类内初始化。

2. decltype

decltype 关键字可通过括号内的表达式自动推导结果的数据类型,但并不会执行该表达式,同样作用于编译阶段。与 auto 相比其不需要对变量进行初始化,可用于变量的声明。

int a = 1;
decltype(a) b = 2;

3. nullptr

C++11 中使用 nullptr 来代替 NULL,避免不同系统对 NULL 具体数值的不同定义可能带来的问题,以及 NULL 与整型数据类型可能存在的二义性问题。

4. constexpr

constexpr 即 const expression,主要是告诉编译器其所修饰的变量、函数的结果在编译期就可确定值,可以进行相应的优化以节省内存和时间。而 const 只是表示变量被初始化后不能再修改,因此可能在运行期才能确定值,这时编译器为了安全可能不会对 const 进行优化。

实际上,对于变量和函数,constexpr 有着不同的行为。constexpr 变量必须初始化为编译期可确定的值,任何可能导致编译期无法确定值的表达式都是非法的。constexpr 函数则稍微宽松一些,如果函数返回值不要求在编译期中被使用,那么可以使用非 const 的参数输入,相当于普通的函数,但是函数内部的表达式除了输入的参数以外不允许包含其他编译期不能确定值的变量以及运算。在 C++11 中 constexpr 所修饰的函数只能包含返回语句,在 C++14 中 constexpr 所修饰的函数则可以包含更多的语句。

const int fn1(const int i) {
   return i + 1;
}

constexpr int fn2(int i) {
   // i并不需要用cosnt修饰
   return i + 1;
}

constexpr int fn3(const int &i) {
   // 如果使用引用最好加上const,因为非常量引用的初始值必须是左值,
   // 如果直接使用数字等常量右值作为参数输入,const引用可以自动地进行类型转换。
   // 还可以通过右值引用的方法来处理以上情况
   return i + 1;
}

constexpr int fac(int n) {
   // constexpr函数也可以是递归的
   return n == 1 ? 1 : n * fac(n - 1);
}

int main() {
   int x[fn1(5)];    // MSVC编译失败,g++可以编译通过
   int y[fn2(5)];    // 编译通过,通过constexpr修饰编译器可在编译期确定返回值

   constexpr int a;  // 编译失败,constexpr必须初始化
   int i = 2;
   constexpr int j = i + 1; // 编译失败,表达式中不能包含任何编译期不能确定值的变量
   int k = fn2(i);   // 编译通过,因为编译期不需要使用返回值,相当于普通函数
}

5. range-based for

range-based for 主要用于遍历一个数组或具有 begin() 和 end() 方法的容器,可以避免显式的迭代器使用。

vector<int> x(10);
int cnt = 0;
for(auto it = x.begin(); it < x.end(); it++) 
{
	// 普通的for循环需要明确指明开头和结尾
	*it = cnt++;
}
cnt = 0;
for (auto &xi : x)
{
	// 这种写法与上面等价,但是更加简洁
	xi = cnt++;
}
for( auto xi : x ) 
{ 
	// 注意与迭代器的区别,这时xi是x元素的值复制
	cout << xi << " ";
}
cout << endl;

6. lambda 表达式

lambda 表达式通常用来定义一些比较简单的匿名函数,并且能够像普通变量一样在局部作用域中随用随定义,而无需在文件全局作用域单独进行定义。相比普通函数,由于其定义可放在某个作用域的代码中间,所以其可以直接访问到该作用域的局部变量,而无需通过参数列表传递。另外,像普通局部变量一样,lambda 表达式也有局部的作用域与生命期,可以像普通变量一样赋值,也可以作为返回值返回。但与普通变量不同的是,lambda 表达式的赋值有点像 共享指针,即赋值操作不会在内存中产生新的 lambda 表达式,而是增加其引用计数;当 lambda 表达式离开某个局部作用域时,会导致其引用计数减少;当引用计数为 0,即离开了所有的局部作用域时,lambda 表达式就会被自动销毁,释放其表达式内部定义的局部变量。其通常形式为:

auto fn = [capture] (paras) {statements;}; // 注意最后要有分号
  • [] 用于捕获表达式所处作用域的局部普通变量,空[]代表不使用任何局部普通变量。静态变量不需要捕获。
  • [&] 默认表示表达式内所用到的所有局部普通变量都是以引用方式传递的,
  • [=] 默认表示 lambda 表达式内所用到的所有局部普通变量都是以值传递的。
  • [&, =] 的用法错误,两者不能同时存在。
  • 可以显式写明某个变量的传递方式,但必须与默认方式相反,否则错误。默认方式符号要写在最前面。
  • [&, a] 变量 a 以值传递,其他的以引用方式传递,
  • [=, &a] 变量 a 以引用传递,其他的以值传递。
  • [&, &a] [=, a] [a, &a] 这些都是错误的写法。
  • 当表达式在类成员函数里面时,需要捕获 this 指针(即 [this])才能访问类成员函数或者变量。
  • (paras) 包含了可输入参数的声明,没有参数时也可省略括号,但调用时还是需要括号。
  • 虽然 auto 不能作为函数输入参数类型,但在 C++14 中 lambda 表达式可以使用 auto 作为参数类型。
/* argsort实现示例
*/
int main()
{
    int x[5] = { 10, 3, 1, 9, 5 };
    int idx[5] = { 0, 1, 2, 3, 4 };
	// 对于静态数组,MSVC必须使用引用方式传递,g++两种方式都可以
	// 如果是指针则两种方式都可以,因为指针本身也是一个变量
    auto cmp = [&x](int i1, int i2) { return x[i1] > x[i2]; }; // 这里实现的是从大到小排列
    sort(idx, idx + 5, cmp);
    for (auto i : idx)
    {
        cout << i << ' ';
    }
    cout << endl;
    return 0;
}

因为 lambda 表达式可以在局部作用域定义,并使用局部作用域的变量,所以经常被用来编写函数闭包。例如在 Python 中,我们经常需要用到生成器来持续地生成列表元素,通常通过 next() 方法来获取,这种方法可以减少提前生成整个列表所需的内存。利用 lambda 表达式,我们同样可以用 C++ 写出类似的生成器。下面以斐波那契数列生成器为例:

/* 自定义shared_ptr的释放内存方式
 * 这里主要用来指示程序是否调用了智能指针的析构函数,确认会不会发生内存泄漏。
*/
void Del(int *p)
{
    if (NULL != p)
    {
        cout << "del " << p << endl;
        delete p;
    }
}

/* 基于lambda表达式的斐波那契数列生成器实现
 * 注意C++14才能支持auto返回类型,在g++中编译时需要加上--std=c++14
*/
auto yield_fib_iter()
{
    shared_ptr<int> a(new int(0), Del); // 记录第 n-2 个数
    shared_ptr<int> b(new int(1), Del);	// 记录第 n-1 个数
    auto next = [a, b]()
    {
        int c = *b;
        *b = *a + *b;
        *a = c;
        return c;
    };
    return next; // 返回lambda表达式
}

void test()
{
    auto fib = yield_fib_iter(); // 定义一个局部的生成器
    for (int i = 0; i < 5; i++)
    {
        cout << fib() << endl;
    }
    // 利用自定义的shared_ptr删除器,我们可以看到当test()结束时,
    // 由于fib属于局部lambda表达式,其离开作用域时也会被销毁,导致shared_ptr的引用计数为0,
    // 从而两个shared_ptr所管理的动态内存也会被自动释放,不会造成内存泄漏
}

int main()
{
	test(); // 测试局部作用域的lambda表达式,你也可以选择返回该表达式
	
    auto fib = yield_fib_iter(); // 定义一个新的生成器,从第一个数开始生成
    for (int i = 0; i < 5; i++)
    {
        cout << fib() << endl;
    }
    return 0;
}

注意,函数闭包并没有改变变量的生命期,当函数结束时,所有函数内部的非静态变量都会被销毁(包括按值传递的参数)。如果 lambda 表达式通过引用来捕获变量,则必须保证该变量在 lambda 表达式被调用时还处在其生命期内。

在以上斐波那契数列生成器的实现中,我们使用了共享指针 shared_ptr 来存储第 n-2 和 n-1个数,当我们调用 yield_fib_iter() 函数并返回 lambda 表达式之后,yield_fib_iter() 函数内部的局部普通变量都会被销毁,包括两个共享指针本身,但是由于 lambda 表达式通过值传递的方式捕获复制了两个共享指针,共享指针所管理的动态内存的引用计数就会加 1,也就是说只要该 lambda 表达式没有被销毁,共享指针的内存引用计数就不会为 0,该内存就能一直被 lambda 表达式有效访问。当 lambda 表达式的生命期结束被销毁时,其内部的局部普通变量也会被销毁,两个共享指针的内存引用计数都变为 0,我们最初分配的动态内存就能被自动释放,而不会造成内存泄漏的问题。当我们再次调用 yield_fib_iter() 来获取另一个生成器时,注意此时返回的 lambda 表达式与之前返回的 lambda 表达式是独立的,相当于两个不同的局部变量,因此它们彼此之间不会相互影响,每个生成器都是从斐波那契数列的第一个数开始生成的。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-22 18:17:37  更:2022-04-22 18:20:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 22:04:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码