点我–>C++语言基础
点我–>C++内存
点我–>面向对象
点我–>STL
【操作系统】常见问题汇总及解答
【数据库】常见问题汇总及解答
【计算机网络】常见问题汇总及解答
【设计模式】总结篇
本文目录
- 1. C++11 的新特性有哪些
- 2. C++中智能指针和指针的区别是什么
- 3. C++ 的智能指针有哪些,分别解决的问题以及它们的区别
- 4. C++ 的右值引用与转移语义
- 5. weak_ptr 能不能知道对象计数为 0,为什么
- 6. weak_ptr 如何解决 shared_ptr 的循环引用问题
- 7. share_ptr 怎么知道跟它共享对象的指针释放了
- 8. 说说 shared_ptr 线程安全性,原理
- 9. 智能指针有没有内存泄露的情况
- 10. C++11 中四种类型转换
- 11. C++11 中 auto 的具体用法
- 12. 说说 C++11 中的可变参数模板新特性
- 13. 说说 C++11 中 Lambda 新特性
1. C++11 的新特性有哪些
C++11 新特性主要包括【语法改进】和【标准库扩充】两个方面:
语法的改进:
- 统一的初始化方法;
- 成员变量默认初始化;
auto
关键字:使用auto
关键字可以让编译器自动推断变量的类型,从而简化变量声明(前提:定义一个变量时对其进行初始化);decltype
求表达式的类型;- 管理动态内存的智能指针(smart pointers):STL中引入了
unique_ptr
、shared_ptr
、weak_ptr
等类型,可以更容易地管理动态内存,避免内存泄漏等问题; - 空指针
nullptr
(原来为NULL
); - 基于范围的
for
循环; - 右值引用和移动语义:介绍了右值引用和移动构造函数/移动赋值函数的概念,这些功能可以大大提高资源管理的效率和性能。
标准库扩充(往STL里新加一些模板类):
- 无序容器(哈希表):引入了
unordered_set
、unordered_map
等新的容器类型,提供了更多的选择; - 正则表达式:可以认为正则表达式实质上是一个字符串,该字符串描述了一种特定模式的字符串;
Lambda
表达式。
2. C++中智能指针和指针的区别是什么
在回答此问题之前,先来说说指针和智能指针的概念:
- 指针:C语言规定所有变量在使用前必须先定义,指定其类型,并按此分配内存单元。指针变量不同于整型变量和其它类型的变量,它是专门用来存放地址的,所以必须将它定义为“指针类型”。
- 智能指针:如果在程序中使用
new
从堆(自由存储区)分配内存,等到不需要时,应使用delete
将其释放。C++ 引用了智能指针auto_ptr
,以帮助自动完成这个过程。随后的编程体验(尤其是使用STL)表明,需要有更精致的机制。基于程序员的编程体验和 BOOST 库提供的解决方案,C++11摒弃了auto_ptr
,并新增了三种智能指针:unique_ptr
、shared_ptr
和weak_ptr
。所有新增的智能指针都能与 STL 容器和移动语义协同工作。
C++ 中智能指针是一种封装了指针的对象,用于自动管理动态分配对象的内存。智能指针会负责自动地对其所指向的对象进行内存的分配和释放,从而防止内存泄漏和野指针等常见的程序错误。与普通指针相比,智能指针具有以下区别:
- 自动管理内存:智能指针可以自动管理所指对象的内存,避免了手动释放内存的麻烦和出错的风险。
- 引用计数:智能指针通常使用引用计数来跟踪所指对象的引用次数。当引用计数达到零时,智能指针会自动调用析构函数来释放所指对象的内存。
- 无需使用
delete
:与普通指针不同,智能指针不需要显式地调用delete
操作符来释放内存。智能指针在离开其作用域时会自动执行析构函数,从而释放动态分配的内存。 - 无法进行指针算术和数组操作:智能指针只能指向动态分配的单个对象,不能进行指针算术操作和数组操作,因为这些操作可能导致指针内存地址越界,从而破坏了智能指针的自动管理功能。
3. C++ 的智能指针有哪些,分别解决的问题以及它们的区别
- C++ 中的智能指针有 4 种:
auto_ptr
、unique_ptr
、shared_ptr
、weak_ptr
,其中auto_ptr
被 C++11 弃用。 - 使用智能指针的原因:申请的空间(即
new
出来的空间),在使用结束时,需要delete
掉,否则会形成内存碎片。在程序运行期间,new
出来的对象,在析构函数中delete
掉,但是这种方法不能解决所有问题,因为有时候new
发生在某个全局函数里面,该方法会给程序员造成精神负担。此时,智能指针就派上了用场。使用智能指针可以很大程度上避免这个问题,因为智能指针就是一个类,当超出了类的作用域时,类会自动调用析构函数,析构函数会自动释放资源。所以,智能指针的作用原理就是在函数结束时自动释放内存空间,避免了手动释放内存空间。 - 四种指针分别解决的问题以及各自特性如下:
智能指针 | 特点 |
---|---|
auto_ptr | 实现独占式拥有的概念,同一时间只能有一个智能指针可以指向该对象;但 auto_ptr 在C++11中被摒弃,其主要问题在于:(1)对象所有权的转移,比如在函数传参过程中,对象所有权不会返还,从而存在潜在的内存崩溃问题;(2)不能指向数组,也不能作为STL容器的成员 |
unique_ptr | 实现独占式拥有的概念,同一时间只能有一个智能指针可以指向该对象,因为无法进行拷贝构造和拷贝赋值,但是可以进行移动构造和移动赋值 |
shared_ptr | 实现共享式拥有的概念,即多个智能指针可以指向相同的对象,该对象及相关资源会在其所指对象不再使用之后,自动释放与对象相关的资源 |
weak_ptr | 解决 shared_ptr 相互引用时,两个指针的引用计数永远不会下降为0,从而导致死锁问题。而 weak_ptr 是对对象的一种弱引用, 可以绑定到 shared_ptr,但不会增加对象的引用计数 |
「对 shared_ptr 和 weak_ptr 的补充说明」:
- shared_ptr
shared_ptr
实现共享式拥有概念。多个智能指针可以指向相同对象,该对象和其相关资源会在“最后一个引用被销毁”时释放。从名字 share 就可以看出了资源可以被多个指针共享,它使用计数机制来表明资源被几个指针共享。可以通过成员函数 use_count()
来查看资源的所有者个数。除了可以通过 new
来构造,还可以通过传入 auto_ptr
、unique_ptr
、weak_ptr
来构造。当我们调用 release()
时,当前指针会释放资源所有权,计数减一。当计数等于 0 时,资源会被释放。
shared_ptr
是为了解决 auto_ptr
在对象所有权上的局限性(auto_ptr
是独占的), 在使用引用计数的机制上提供了可以共享所有权的智能指针。
成员函数:
use_count
:返回引用计数的个数。unique
:返回是否是独占所有权(use_count
为 1)。swap
:交换两个shared_ptr
对象(即交换所拥有的对象)。reset
:放弃内部对象的所有权或拥有对象的变更,会引起原有对象的引用计数的减少。get
:返回内部对象(指针),由于已经重载了()
方法,因此和直接使用对象是一样的。如 shared_ptr sp(new int(1)); sp 与 sp.get() 是等价的。
- weak_ptr
weak_ptr
是一种不控制对象生命周期的智能指针,它指向一个 shared_ptr
管理的对象。进行该对象的内存管理的是那个强引用的 shared_ptr
。weak_ptr
只是提供了对管理对象的一个访问手段。weak_ptr
设计的目的是为配合 shared_ptr
而引入的一种智能指针来协助 shared_ptr
工作,它只可以从一个 shared_ptr
或另一个 weak_ptr
对象构造, 它的构造和析构不会引起引用记数的增加或减少。weak_ptr
是用来解决 shared_ptr
相互引用时的死锁问题,如果说两个 shared_ptr
相互引用,那么这两个指针的引用计数永远不可能下降为 0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数,和 shared_ptr
之间可以相互转化,shared_ptr
可以直接赋值给它,它可以通过调用 lock
函数来获得 shared_ptr
。
4. C++ 的右值引用与转移语义
在 C++ 中,一个表达式具有左值或右值属性。一个左值表达式表示一个对象或函数的标识符,它可以出现在赋值语句的左侧或右侧,比如:
int a = 10; // a 是左值,10 是右值
int b = a; // a 和 b 都是左值
一个右值表达式表示一个临时对象或字面量,它只能出现在赋值语句的右侧,比如:
int a = 10; // a 是左值,10 是右值
int b = a; // a 和 b 都是左值
int c = a + b; // a + b 是右值
C++11 中的右值引用是一种新类型的引用,它允许我们将一个左值绑定到一个右值引用上,例如:
int a = 10;
int&& r = std::move(a); // 将左值 a 移动到右值引用 r 中
右值引用与常规引用(左值引用)的区别在于,右值引用只能绑定到右值,但常规引用可以绑定到左值或右值。
通过移动语义,我们可以利用右值引用实现高效的资源管理。例如,如果我们要实现一个动态数组容器类,如果要实现对原有数组进行扩展,我们需要先创建一个新数组,再将原数组的元素复制到新数组中,最后释放原数组的内存。这个过程需要进行大量的内存分配和数据复制操作,非常耗时和浪费。而通过移动语义,我们可以将原数组的内存资源“移动”到新数组中,避免了内存分配和数据复制操作,从而提高了程序的性能和效率。
具体来说,通过将一个对象的右值引用作为函数参数来实现移动语义,例如:
class myvector {
public:
// 将原有数组的内存资源“移动”到新数组中
myvector(myvector&& other) : data_(other.data_), size_(other.size_), capacity_(other.capacity_) {
other.data_ = nullptr;
other.size_ = 0;
other.capacity_ = 0;
}
// ...
private:
int* data_;
size_t size_;
size_t capacity_;
};
在上面的代码中,myvector
类的移动构造函数接受一个 myvector
对象的右值引用作为参数,将原有数组的内存资源“移动”到新数组中。我们将原有数组的指针、大小和容量移动到新数组中,并将原有数组的指针、大小和容量设置为 0。这样,原有数组的内存资源就被释放了,并且新数组可以继续使用原有数组的内存空间,避免了内存分配和数据复制操作。
「右值引用的补充说明」:
C++98/03 标准中就有引用,使用 “&” 表示。但此种引用方式有一个缺陷,即正常情况下只能操作C++中的左值,无法对右值添加引用。比如:
int num = 10;
int& b = num; // 正确
int& c = 10; // 错误
如上所示,编译器允许我们为 num 左值建立一个引用,但不可以为 10 这个右值建立引用。因此,C++98/03 标准中的引用又称为左值引用。
「注意」:虽然 C++98/03 标准不支持为右值建立非常量左值引用,但允许使用常量左值引用操作右值。也就是说,常量左值引用既可以操作左值,也可以操作右值,例如:
int num = 10;
const int& b = num; // 正确
const int& c = 10; // 正确
我们知道,右值往往是没有名称的,因此要使用它只能借助引用的方式。这就产生一个问题,实际开发中我们可能需要对右值进行修改(实现移动语义时就需要),显然左值引用的方式是行不通的。
为此,C++11 标准新引入了另一种引用方式,称为右值引用,用 “&&” 表示。
「注意」:和声明左值引用一样,右值引用也必须立即进行初始化操作,且只能使用右值进行初始化,比如:
int num = 10;
int && a = num; // 错误,右值引用不能初始化为左值
int&& a = 10;
和常量左值引用不同的是,右值引用还可以对右值进行修改。例如:
int&& a = 10;
a = 100;
cout << a << endl; // 输出100
另外值得一提的是,C++ 语法上是支持定义常量右值引用的,例如:
const int&& a = 10; // 编译器不会报错
但这种定义出来的右值引用并无实际用处。一方面,右值引用主要用于移动语义和完美转发,其中前者需要有修改右值的权限;其次,常量右值引用的作用就是引用一个不可修改的右值,这项工作完全可以交给常量左值引用完成。
5. weak_ptr 能不能知道对象计数为 0,为什么
不能。因为 weak_ptr
是一种不控制对象生命周期的智能指针,它指向一个 shared_ptr
管理的对象。进行该对象管理的是那个引用的 shared_ptr
。weak_ptr
只是提供了对管理对象的一个访问手段。weak_ptr
设计的目的只是为了配合 shared_ptr
而引入的一种智能指针,配合 shared_ptr
工作,它只可以从一个 shared_ptr
或者另一个 weak_ptr
对象构造,它的构造和析构不会引起计数的增加或减少。
6. weak_ptr 如何解决 shared_ptr 的循环引用问题
为了解决循环引用导致的内存泄漏,引入了弱指针 weak_ptr
,weak_ptr
的构造函数不会修改引用计数的值,从而不会对对象的内存进行管理,其类似一个普通指针,但是不会指向引用计数的共享内存,但是可以检测到所管理的对象是否已经被释放,从而避免非法访问。
7. share_ptr 怎么知道跟它共享对象的指针释放了
多个 shared_ptr
对象可以同时托管一个指针,系统会维护一个托管计数。当无 shared_ptr
托管该指针时,delete
该指针。
ChatGPT给出的回答:
std::shared_ptr
是 C++ 标准库中的一个智能指针,用于共享所有权所指向的对象。它可以用于避免由于持有原始指针而导致的内存泄漏或空悬指针的问题。在使用 share_ptr
进行内存管理的过程中,需要注意确保所共享对象的生命周期。
一般来说,当使用 std::shared_ptr
来管理一个对象的内存时,std::shared_ptr
会在没有任何指针引用该对象的情况下,即没有任何 std::shared_ptr
指向该对象时自动释放该对象。这可通过使用 std::weak_ptr
检查该对象是否仍然存在从而确定。
为了监测共享对象的释放,std::shared_ptr
提供了一个自定义删除器函数,该函数在 std::shared_ptr
释放一个对象时自动调用。可以使用自定义删除器函数来执行任何必要的清理操作并通知您所关心的其他对象。
例如,使用 lambda 表达式作为 std::shared_ptr
的自定义删除器函数,您可以在共享对象被销毁时将信息输出至控制台。下面是一个示例:
#include <iostream>
#include <memory>
struct MyObj
{
MyObj() { std::cout << "MyObj constructed." << std::endl; }
~MyObj() { std::cout << "MyObj destroyed." << std::endl; }
};
int main()
{
auto customDeleter = [](MyObj* obj) { std::cout << "MyObj is being destroyed." << std::endl; delete obj; };
std::shared_ptr<MyObj> objPtr(new MyObj, customDeleter);
objPtr.reset();
return 0;
}
程序运行结果:
MyObj constructed.
MyObj is being destroyed.
在上面的示例代码中,我们创建了一个名为 MyObj
的类,它在创建时向控制台输出一条消息,同时在销毁时输出一条消息。我们还通过使用 lambda 表达式作为 std::shared_ptr
的自定义删除器函数,将输出一条消息告知该对象即将被销毁。
在 main
函数中,我们首先创建了一个 std::shared_ptr
对象,该对象指向一个 MyObj
类型的对象,并使用自定义删除器函数来确保能够输出一条消息以检测共享对象的释放。接着,我们使用 objPtr.reset()
语句释放该对象,该语句会使 std::shared_ptr
停止持有该对象,从而调用自定义删除器函数并输出一条消息告诉我们该对象即将被销毁。
因此,通过使用 std::shared_ptr
的自定义删除器函数,可以轻松地检查和监控共享对象的释放。
8. 说说 shared_ptr 线程安全性,原理
shared_ptr
是 C++ STL 提供的一种智能指针,用于管理动态内存。与传统的指针相比,它可以自动释放资源,避免了内存泄漏等问题。在多线程编程中使用 shared_ptr
时,需要注意其线程安全性。
在 C++11 中,shared_ptr
的【线程安全性】分为两个方面:引用计数和指针赋值。
- 引用计数:
shared_ptr
内部维护了一个引用计数,表示指向某个对象的智能指针的数量。当引用计数为零时,该对象会被自动释放。由于多线程环境下存在竞争条件,因此需要使用原子操作(atomic operations)来保证引用计数的线程安全性。C++11 中,shared_ptr
默认使用std::atomic
来实现原子操作,从而保证了引用计数的线程安全性。 - 指针赋值:在多线程中,当多个线程同时使用同一个
shared_ptr
对象时,可能会导致指向不同对象的shared_ptr
指针同时被赋值,这会破坏引用计数的正确性。为了解决这个问题,C++11 中引入了std::atomic_store
和std::atomic_load
函数。这两个函数可以在多线程环境下安全地进行shared_ptr
的指针赋值和读取操作,从而保证了shared_ptr
的线程安全性。
【原理】:在多线程环境下,shared_ptr
的线程安全性是由 std::atomic
来保证的。std::atomic
使用硬件级别的原子操作来保证数据的原子性。当使用 std::atomic
来实现引用计数时,多个线程同时对引用计数进行加减操作时,std::atomic
会使用 CPU 提供的原子指令来实现同步,从而保证引用计数的正确性。而 std::atomic_store
和 std::atomic_load
函数则通过类似的机制来保证 shared_ptr
的指针赋值和读取操作的线程安全性。
9. 智能指针有没有内存泄露的情况
智能指针有内存泄露的情况发生。
- 智能指针发生内存泄露的情况:当两个对象同时使用一个
shared_ptr
成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄露。 - 智能指针的内存泄漏如何解决:为了解决循环引用导致的内存泄漏,引入了弱指针
weak_ptr
,weak_ptr
的构造函数不会修改引用计数的值,从而不会对对象的内存进行管理,其类似一个普通指针,但是不会指向引用计数的共享内存,但是可以检测到所管理的对象是否已经被释放,从而避免非法访问。
#include<iostream>
#include <memory>
using namespace std;
class Parent {
private:
shared_ptr<Child> ChildPtr;
public:
void setChild(shared_ptr<Child> child) {
this->ChildPtr = child;
}
void doSomething() {
if (this->ChildPtr.use_count()) {}
}
~Parent() {}
};
class Child {
private:
shared_ptr<Parent> ParentPtr;
public:
void setPartent(shared_ptr<Parent> parent) {
this->ParentPtr = parent;
}
void doSomething() {
if (this->ParentPtr.use_count()) {}
}
~Child() {}
};
int main() {
weak_ptr<Parent> wpp;
weak_ptr<Child> wpc;
shared_ptr<Parent> p(new Parent);
shared_ptr<Child> c(new Child);
p->setChild(c);
c->setPartent(p);
wpp = p;
wpc = c;
cout << p.use_count() << endl; // 2
cout << c.use_count() << endl; // 2
cout << wpp.use_count() << endl; // 1
cout << wpc.use_count() << endl; // 1
return 0;
}
上述代码中,parent
有一个 shared_ptr
类型的成员指向孩子,而 child
也有一个 shared_ptr
类型的成员指向父亲。然后在创建孩子和父亲对象时也使用了智能指针 c
和 p
,随后将 c
和 p
分别又赋值给 child
的智能指针成员 parent
和 parent
的智能指针成员 child
。从而形成了一个循环引用。
10. C++11 中四种类型转换
C++11 中有四种类型转换:静态转换(static_cast
)、动态转换(dynamic_cast
)、常量转换(const_cast
)和重新解释转换(reinterpret_cast
)。
- static_cast
静态转换是一种常用的类型转换方法,可以在不使用转换操作符的情况下将一个类型的值转换为另一个类型的值。这种类型转换只能在编译时完成,因此也叫编译时转换。
静态转换可以将一个类型的值转换为另一个无关类型的值,或者将虚基类指针向下转换为派生类指针,或者将指针或引用转换为不同的CV限定符。
例如:
int x = 10;
double y = static_cast<double>(x); // 将整数类型转换为浮点数类型
- dynamic_cast
动态转换是通过使用转换操作符(dynamic_cast
)在运行时将一个指针或引用转换为另一个类型的指针或引用。动态转换通常用于将基类指针指向派生类的对象。
如果在运行时无法进行转换,则动态转换将返回空指针或引用。因此,在进行动态转换之前,需要使用类型检查来确保转换是安全的。
例如:
class Base { virtual void foo() {} };
class Derived : public Base {};
Base *b = new Derived;
Derived *d = dynamic_cast<Derived*>(b); // 将基类指针 b 转换为派生类指针 d
if (d) {
// 转换成功
} else {
// 转换失败
}
- const_cast
常量转换可以将常量对象转换为非常量对象。常量转换通常用于处理从函数传递的常量参数,以便在函数内部修改它们。
常量转换不能用于将非常量对象转换为常量对象。
例如:
const int x = 10;
int &y = const_cast<int&>(x); // 将常量引用 y 转换为非常量引用
- reinterpret_cast
重新解释转换可以将一个指针或引用从一种类型转换为另一种类型,即使这两种类型之间并没有任何关联。重新解释转换通常是不安全的,在大多数情况下应该避免使用。
例如:
int x = 10;
char *p = reinterpret_cast<char*>(&x); // 将 int 指针转换为 char 指针
「注意」:为什么不用C的强制转换:C的强制转换表面上看起来功能强大什么都能转,但是转换不够明确,不能进行错误检查,容易出错。
11. C++11 中 auto 的具体用法
- 使用 auto 推导变量类型
在 C++11 之前,声明变量时需要显式地指定变量类型,例如:
int x = 10;
而在 C++11 中,我们可以使用 auto
来自动推导变量类型:
auto x = 10;
编译器会根据表达式的类型自动推导出变量 x
的类型为 int
。
- 使用 auto 推导函数返回类型
在 C++11 之前,我们需要显式地指定函数返回类型,例如:
int add(int a, int b) {
return a + b;
}
而在 C++11 中,我们可以使用 auto
来自动推导函数返回类型:
auto add(int a, int b) -> int {
return a + b;
}
这里需要使用“尾置返回类型”语法,即在函数参数列表之后使用 ->
来指定函数返回类型,然后使用 auto
来自动推导函数返回类型。
- 使用 auto 推导迭代器类型
在 C++11 中,我们可以使用 auto
来自动推导迭代器类型,例如:
std::vector<int> vec{1, 2, 3};
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " ";
}
在这个例子中,我们使用 auto
来自动推导出迭代器的类型为 std::vector<int>::iterator
。
- 使用 auto 推导 lambda 表达式类型
在 C++11 中,我们可以使用 auto
来自动推导 lambda 表达式的类型:
auto f = [](int x, int y) { return x + y; };
在这个例子中,我们使用 auto
来自动推导出 lambda 表达式的类型为一个闭包类型,可以根据上下文来自动推导出具体的类型。
【总结】:C++11 中的 auto
关键字可以用于类型推导,可以自动推导变量类型、函数返回类型、迭代器类型、lambda 表达式类型等。使用 auto
可以简化代码,提高代码的易读性和编写效率。但需要注意,过多使用 auto
可能会降低代码可读性,所以需要适度使用。
12. 说说 C++11 中的可变参数模板新特性
在 C++11 中,引入了可变参数模板(Variadic Templates)的新特性,使得函数和类可以接受任意数量的参数,它是模板元编程中非常重要的一个特性。
使用可变参数模板,我们可以定义一个可接受任意数量参数的函数或类模板。在函数模板中,使用 ellipsis(省略号)表示可变参数的位置,而在类模板中可以使用模板参数包(template parameter pack)表示可变数量的模板参数。通过一个可变参数模板,我们可以从参数包中获取参数的数量,以及对每个参数进行操作。
以下是一个简单的示例,在这个示例中我们定义了一个可变参数模板函数 print()
,用于输出不定数量的参数:
#include <iostream>
// 可变参数模板函数
template<typename... Args>
void print(const Args&... args) {
(std::cout << ... << args) << "\n";
}
int main() {
print("Hello", 42, 'A'); // 输出:Hello42A
return 0;
}
在上述示例中,我们使用到了 C++11 中的折叠表达式(fold expression),使用省略号作为二元操作符的版本,将所有的可变参数输出到调用者的控制台。折叠表达式允许我们将多个表达式以相同或不同的二元操作符连接起来,从而更为简洁地表达一些复杂的逻辑。
除了可变参数模板函数以外,在 C++11 中还引入了可变参数模板类,它们可以接受任意数量的模板参数,并使用参数包进行操作。在一些需要动态地构建和使用数据结构的场景中,可变参数模板类可以发挥很大的作用。
13. 说说 C++11 中 Lambda 新特性
C++11 引入了 Lambda 表达式作为一种新的语言特性。Lambda 表达式是一种用于创建匿名函数对象的语法结构,可以在函数内部直接定义函数对象,而无需显式地声明一个类或函数。
Lambda 表达式的基本语法如下:
[capture list] (parameter list) mutable(optional) exception-specification(optional) -> return type(optional) { function body }
其中,capture list
表示变量捕获列表,用于指定在 Lambda 表达式中某些外部变量的访问方式;parameter list
表示形参列表,用于定义 Lambda 函数的参数;mutable
关键字用于指示 Lambda 函数体中的变量是否可以被修改;exception-specification
用于指定异常规范;return type
用于指定 Lambda 函数的返回值类型;function body
表示 Lambda 函数体。
程序示例1:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> nums = { 1, 2, 3, 4, 5, 6 };
int target = 3;
auto it = std::find_if(nums.begin(), nums.end(), [target](int num) { return num == target; });
if (it != nums.end())
{
std::cout << "Found: " << *it << std::endl;
}
else
{
std::cout << "Not found." << std::endl;
}
return 0;
}
在这个例子中,Lambda 表达式通过 []
指定了捕获列表,包含了一个外部变量 target
,并定义了一个参数 num
和一个函数体,表示查找 nums
中是否存在值为 target
的元素。Lambda 表达式通过 auto
推导出了返回值类型,并被传递给了 std::find_if
算法作为一个谓词函数,最终实现了查找的功能。
程序示例2:
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int num[4] = { 4, 2, 3, 1 };
// 对数组中的元素进行排序
sort(num, num + 4, [=](int x, int y) -> bool { return x < y; });
for (int n : num)
{
cout << n << " "; // 1 2 3 4
}
cout << endl;
return 0;
}
Lambda 表达式的引入使得 C++ 中的函数式编程风格更加方便和实用,可以实现更加灵活和高效的算法和数据结构。