MST

星途 面试题库

面试题:C++中Lambda表达式的基础性能差异

在C++中,Lambda表达式按值捕获和按引用捕获在性能上可能会有哪些不同?请举例说明,并阐述在何种场景下应优先选择哪种捕获方式以优化性能。
28.4万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

按值捕获和按引用捕获在性能上的不同

  1. 按值捕获
    • 性能特点:按值捕获会复制被捕获的变量到Lambda表达式内部。如果被捕获的变量是较大的对象,复制操作会带来额外的性能开销,包括时间开销(执行复制操作)和空间开销(为复制的对象分配内存)。
    • 举例
#include <iostream>
#include <vector>

class BigObject {
public:
    std::vector<int> data;
    BigObject() {
        for (int i = 0; i < 1000000; ++i) {
            data.push_back(i);
        }
    }
};

int main() {
    BigObject obj;
    auto lambdaByValue = [obj]() {
        // 这里使用按值捕获的obj
        std::cout << "Using obj by value capture. Size: " << obj.data.size() << std::endl;
    };
    lambdaByValue();
    return 0;
}
  • 在这个例子中,BigObject对象很大,按值捕获obj时会复制整个obj,包括其包含的std::vector<int>,这会消耗较多的时间和空间。
  1. 按引用捕获
    • 性能特点:按引用捕获不会复制被捕获的变量,而是在Lambda表达式内部使用原始变量的引用。因此,它没有复制带来的性能开销,无论是时间还是空间上都相对高效,尤其对于大对象。
    • 举例
#include <iostream>
#include <vector>

class BigObject {
public:
    std::vector<int> data;
    BigObject() {
        for (int i = 0; i < 1000000; ++i) {
            data.push_back(i);
        }
    }
};

int main() {
    BigObject obj;
    auto lambdaByReference = [&obj]() {
        // 这里使用按引用捕获的obj
        std::cout << "Using obj by reference capture. Size: " << obj.data.size() << std::endl;
    };
    lambdaByReference();
    return 0;
}
  • 在这个例子中,按引用捕获obj,Lambda表达式内部使用的是obj的引用,没有复制操作,性能更高。

选择捕获方式以优化性能的场景

  1. 优先选择按值捕获的场景
    • 场景一:需要独立副本:当Lambda表达式需要一个独立的变量副本,且不希望其修改影响到外部变量,同时被捕获的变量较小(如基本数据类型)时,按值捕获是较好的选择。例如:
#include <iostream>

int main() {
    int num = 5;
    auto lambda = [num]() {
        num = 10; // 这里修改的是按值捕获的副本
        std::cout << "Inside lambda: " << num << std::endl;
    };
    lambda();
    std::cout << "Outside lambda: " << num << std::endl;
    return 0;
}
  • 场景二:防止悬空引用:如果Lambda表达式的生命周期可能比被捕获变量的生命周期长,使用按值捕获可以防止悬空引用问题。比如在多线程场景中,如果在一个线程中创建Lambda表达式并捕获局部变量,使用按值捕获可以确保在该线程执行Lambda表达式时,被捕获变量不会因为超出作用域而被销毁。
  1. 优先选择按引用捕获的场景
    • 场景一:大对象且不需要独立副本:当被捕获的对象较大,且Lambda表达式不需要独立副本,而是希望直接操作原始对象时,按引用捕获可以显著提高性能。例如上述BigObject的例子。
    • 场景二:频繁调用且对象状态变化:如果Lambda表达式会被频繁调用,且每次调用都依赖外部变量的最新状态,按引用捕获可以避免每次都复制变量,从而提升性能。例如在一个循环中频繁调用Lambda表达式,且该表达式依赖外部的一个大对象的最新状态。