当前位置: 首页 > article >正文

1.5 新特性 C++面试常见问题

1.5.1 说说C++11的新特性有哪些?

C++11 引入了许多重要的新特性和增强,目的是提升语言的性能、可读性和简洁性。以下是 C++11 的一些主要新特性:

1. 自动类型推导

  • 使用 auto 关键字,可以让编译器自动推导变量的类型。
    auto x = 42;        // int
    auto y = 3.14;     // double
    

2. 范围基的 for 循环

  • 提供了简化的语法来遍历容器。
    std::vector<int> vec = {1, 2, 3, 4, 5};
    for (auto& value : vec) {
        std::cout << value << " ";
    }
    

3. lambda 表达式

  • 允许定义匿名函数,支持函数式编程。
    auto add = [](int a, int b) { return a + b; };
    std::cout << add(2, 3); // 输出 5
    

4. 智能指针

  • 引入 std::unique_ptr, std::shared_ptr, 和 std::weak_ptr,帮助管理动态内存,避免内存泄漏。
    std::unique_ptr<MyClass> ptr(new MyClass());
    

5. 移动语义

  • 引入右值引用,支持移动构造和移动赋值,提高性能。
    class MyClass {
    public:
        MyClass(MyClass&& other) noexcept { /* 转移资源 */ }
    };
    

6. 初始化列表

  • 提供了更简洁的对象初始化语法。
    std::vector<int> vec = {1, 2, 3, 4, 5};
    

7. 静态断言

  • 使用 static_assert 在编译时检查条件。
    static_assert(sizeof(int) == 4, "int size must be 4 bytes");
    

8. 线程支持

  • 引入 <thread> 库,提供了线程的创建、管理和同步。
    std::thread t([] { std::cout << "Hello from thread!" << std::endl; });
    t.join();
    

9. 新的标准库组件

  • 增加了 std::chrono 用于时间处理。
  • 增加了 std::random 用于随机数生成。
  • 增加了 std::regex 用于正则表达式匹配。

10. 委托构造函数

  • 允许一个构造函数调用另一个构造函数,以减少重复代码。
    class MyClass {
    public:
        MyClass() : MyClass(0) {} // 委托构造
        MyClass(int x) { /* ... */ }
    };
    

11. 显式类型转换

  • 引入 explicit 关键字,以防止不必要的隐式转换。

12. 用户定义的字面量

  • 允许用户定义新的字面量,例如:
    constexpr long double operator"" _km(long double x) { return x * 1000.0; }
    

13. 范围和 nullptr

  • 引入了 nullptr 作为指针的空值,替代 NULL,增加类型安全。

14. 对 const 的增强

  • 引入 constexpr,允许在编译时计算常量。

总结

C++11 是一个重大的版本,添加了许多新的特性,极大地提升了 C++ 的表达能力和性能。通过这些新特性,开发者可以编写更高效、更易读和更安全的代码。

1.5.2 说说C++中智能指针和指针的区别是什么?

智能指针和普通指针在 C++ 中有着显著的区别。智能指针是为了更好地管理动态内存而引入的,它们在某些方面提供了比普通指针更高的安全性和便利性。以下是它们之间的一些主要区别:

1. 内存管理

  • 普通指针

    • 需要手动管理内存,使用 new 分配内存和 delete 释放内存。
    • 容易导致内存泄漏、悬空指针和双重释放等问题。
    int* ptr = new int(10);
    delete ptr; // 手动释放内存
    
  • 智能指针

    • 自动管理内存,确保在不再需要时自动释放内存。
    • 使用 RAII(资源获取即初始化)原则,能够有效避免内存泄漏和悬空指针。
    • 主要有三种类型:std::unique_ptrstd::shared_ptrstd::weak_ptr

2. 类型

  • 普通指针

    • 只是简单的内存地址,表示某个对象的地址。
    • 不提供所有权管理和引用计数功能。
  • 智能指针

    • 是类的实例,封装了指针并提供额外的功能。
    • 具有所有权管理能力,支持引用计数(std::shared_ptr)和独占所有权(std::unique_ptr)。

3. 所有权

  • 普通指针

    • 不支持所有权转移,所有权是模糊的,容易产生不确定性。
  • 智能指针

    • std::unique_ptr 拥有独占所有权,只能有一个智能指针管理同一对象。
    • std::shared_ptr 允许多个指针共享同一个对象的所有权,并通过引用计数管理内存。
    • std::weak_ptr 作为 shared_ptr 的辅助,避免循环引用。

4. 使用简便性

  • 普通指针

    • 在使用上较为直接,但需要小心手动管理内存。
  • 智能指针

    • 提供更安全的 API,允许以更简单的方式处理对象的生命周期。
    • 智能指针的构造和析构都是自动的,用户不需要显式调用 delete

5. 性能

  • 普通指针

    • 通常性能较高,因为没有额外的管理开销。
  • 智能指针

    • std::unique_ptr 的性能开销非常小,接近普通指针。
    • std::shared_ptr 由于需要管理引用计数,可能会引入额外的性能开销。

6. 功能

  • 普通指针

    • 仅能指向对象,没有其他功能。
  • 智能指针

    • 提供了额外的功能,如 std::shared_ptruse_count(),可以查询当前引用计数。

示例

普通指针

int* ptr = new int(10);
// 使用 ptr
delete ptr; // 手动释放

智能指针

#include <memory>

std::unique_ptr<int> uptr(new int(10)); // 使用 unique_ptr
// 不需要手动 delete,uptr 超出作用域时会自动释放内存

std::shared_ptr<int> sptr = std::make_shared<int>(20); // 使用 shared_ptr
// 内存会在没有引用时自动释放

总结

智能指针在内存管理上提供了更高的安全性和便利性,能够有效避免许多常见的内存管理错误,而普通指针则需要开发者自行管理内存。使用智能指针可以减少内存泄漏和其他潜在问题,使代码更加健壮。

1.5.3 说说C++中的智能指针有哪些?分别解决的问题以及区别?

C++ 中主要有三种智能指针:std::unique_ptrstd::shared_ptrstd::weak_ptr。它们各自解决特定的问题,并在内存管理中提供不同的功能和特性。以下是它们的详细介绍、解决的问题以及区别:

1. std::unique_ptr

特点
  • 独占所有权std::unique_ptr 拥有指向对象的唯一所有权,不能被复制。
  • 自动释放:当 std::unique_ptr 超出作用域时,自动调用 delete 释放内存。
  • 支持移动语义:可以通过移动构造和移动赋值将所有权转移给另一个 unique_ptr
解决的问题
  • 避免内存泄漏:自动管理内存,确保内存被释放。
  • 简化代码:无需手动调用 delete,避免了常见的内存管理错误。
示例
#include <memory>

std::unique_ptr<int> uptr(new int(10)); // 创建 unique_ptr
// 不需要手动 delete,uptr 超出作用域时会自动释放内存

2. std::shared_ptr

特点
  • 共享所有权:多个 std::shared_ptr 可以指向同一个对象,使用引用计数管理所有权。
  • 引用计数:当最后一个指向对象的 shared_ptr 被销毁时,自动释放对象内存。
  • 线程安全:对引用计数的操作是线程安全的,但对对象本身的访问需要额外的同步。
解决的问题
  • 避免内存泄漏:通过引用计数确保对象在没有指针指向时被释放。
  • 多个所有者:支持多个指针共享同一对象,方便实现共享资源。
示例
#include <memory>

std::shared_ptr<int> sptr1 = std::make_shared<int>(20); // 创建 shared_ptr
std::shared_ptr<int> sptr2 = sptr1; // sptr2 共享 sptr1 的所有权
// 当 sptr1 和 sptr2 超出作用域时,内存将自动释放

3. std::weak_ptr

特点
  • 不拥有所有权std::weak_ptr 不拥有对象的所有权,只是观察 shared_ptr 指向的对象。
  • 防止循环引用:通过不增加引用计数来避免循环引用问题。
  • 临时访问:可以通过 std::shared_ptr 来访问对象,但不会影响引用计数。
解决的问题
  • 避免循环引用:在使用 shared_ptr 的情况下,防止造成内存泄漏。
  • 检查对象有效性:可以检查指向的对象是否仍然存在。
示例
#include <memory>

std::shared_ptr<int> sptr = std::make_shared<int>(30);
std::weak_ptr<int> wptr = sptr; // 创建 weak_ptr

if (auto temp = wptr.lock()) { // 尝试获取 shared_ptr
    std::cout << *temp; // 使用 temp
} else {
    std::cout << "对象已被释放";
}

区别总结

特性std::unique_ptrstd::shared_ptrstd::weak_ptr
所有权独占所有权共享所有权不拥有所有权
拷贝与移动不可拷贝,只能移动可拷贝,支持引用计数不可拷贝,通常与 shared_ptr 一起使用
引用计数
内存管理自动释放自动释放,当最后一个 shared_ptr 被销毁时不管理内存,提供对 shared_ptr 的观察
用途适合唯一拥有者场景适合多个所有者场景适合避免循环引用和临时访问对象

总结

C++ 的智能指针提供了高效、安全的内存管理机制,帮助开发者避免常见的内存管理错误。选择合适的智能指针类型,可以根据具体的需求和使用场景来有效管理资源,确保代码的健壮性和安全性。

1.5.4 简述 C++ 右值引用与转移语义

C++ 中的 右值引用rvalue references)和 转移语义move semantics)是 C++11 引入的两个关键特性,旨在提高程序性能,特别是处理临时对象和大型数据结构时。

右值引用

  • 右值引用 使用 && 表示,仅能绑定到右值(例如临时对象或字面值)上。
  • 它允许对临时对象进行“窃取”操作,避免不必要的深拷贝。

示例

int&& r = 10;  // r 是一个右值引用,绑定到右值 10 上

转移语义

  • 转移语义 是利用右值引用来实现资源的“转移”而非复制,从而避免性能开销。
  • 转移语义主要通过移动构造函数移动赋值运算符实现,使得资源可以从一个对象转移到另一个对象。

示例

std::vector<int> a = {1, 2, 3};
std::vector<int> b = std::move(a);  // 将 a 的资源转移到 b 中

作用和意义

  • 避免不必要的拷贝:特别是处理临时对象或返回大对象时,转移语义避免了深拷贝。
  • 提升性能:在容器操作和资源管理中,通过转移语义减少了开销。

总结

右值引用和转移语义在 C++ 中提供了高效的资源管理方式,尤其在处理大数据时,能显著提升程序的执行效率。

1.5.5 简述C++中智能指针的特点

C++ 中的智能指针(smart pointers)是 C++11 引入的一种内存管理工具,通过 RAII(资源获取即初始化)原则管理动态内存,自动释放资源,避免内存泄漏和管理错误。常见的智能指针有 std::unique_ptrstd::shared_ptrstd::weak_ptr。以下是它们的特点:

1. std::unique_ptr

  • 特点:独占所有权,一个对象只能由一个 unique_ptr 管理,不能复制,只能转移。
  • 适用场景:唯一所有权,确保对象只被一个指针管理,如函数内部的临时对象。
  • 实现:提供了移动构造和移动赋值函数。

2. std::shared_ptr

  • 特点:共享所有权,可以有多个 shared_ptr 指向同一对象,引用计数管理对象生命周期。
  • 适用场景:多个指针共享同一资源,适用于对象需要多个所有者的情况。
  • 实现:引用计数机制确保最后一个 shared_ptr 销毁时自动释放资源。

3. std::weak_ptr

  • 特点:不拥有对象的所有权,与 shared_ptr 协作,避免循环引用。
  • 适用场景:用于观察对象生命周期,防止循环引用导致的内存泄漏。
  • 实现:没有增加引用计数的轻量级指针,通过 lock() 函数临时访问资源。

智能指针的通用特点

  • 自动内存管理:在超出作用域时自动释放资源,无需手动调用 delete
  • 安全性:通过封装原生指针,减少悬空指针、重复释放等内存管理错误。
  • 便捷性:提供了简单的接口,适应 C++ RAII 原则,降低手动内存管理的复杂度。

总结

智能指针通过自动化的资源管理提高了代码的安全性和健壮性,适合用于现代 C++ 的资源管理和内存控制。

1.5.6 weak_ptr 能不能知道对象计数为0,为什么?

总结

  • weak_ptr 无法直接访问对象的引用计数。
  • 可以通过 lock() 判断对象是否存在,但无法获得确切的引用计数。

std::weak_ptr 不能直接知道对象的引用计数是否为 0。这是因为 weak_ptr 本身不影响 shared_ptr 的引用计数,它只是一个对所指对象的非拥有性弱引用。虽然 weak_ptr 会跟踪对象的生命周期,但它无法直接查看或影响 shared_ptr 的引用计数。

原因

  1. 引用计数独立性weak_ptrshared_ptr 引用计数独立存在。shared_ptr 持有两个计数器:一个是共享引用计数(用于跟踪 shared_ptr 的数量),另一个是弱引用计数(用于跟踪 weak_ptr 的数量)。weak_ptr 增加的是弱引用计数,而非共享引用计数。

  2. 访问方法限制weak_ptr 可以通过 lock() 方法临时转换为 shared_ptr,然后检查对象是否有效(即 shared_ptr 是否为 nullptr)。但 weak_ptr 不能直接查看 shared_ptr 的引用计数。

如何检查对象是否已销毁

可以通过 weak_ptrlock() 方法来判断:

  • 如果 lock() 返回一个空的 shared_ptr(即 nullptr),说明对象已经被销毁,引用计数为 0。
  • 如果 lock() 返回一个非空 shared_ptr,则对象仍然存在。

示例

#include <iostream>
#include <memory>

std::weak_ptr<int> wptr;

{
    auto sptr = std::make_shared<int>(10);
    wptr = sptr;
    std::cout << "Shared count: " << sptr.use_count() << std::endl;  // 输出引用计数
}

if (auto temp = wptr.lock()) {
    std::cout << "对象仍然存在。" << std::endl;
} else {
    std::cout << "对象已被销毁,引用计数为 0。" << std::endl;
}

1.5.7 weak_ptr 如何解决 shared_ptr 的循环引用问题?

std::weak_ptr 通过不增加引用计数来解决 shared_ptr 的循环引用问题。当两个对象相互持有 shared_ptr 时,它们的引用计数永远不会减少到零,导致资源无法释放,从而引发内存泄漏。使用 weak_ptr 可以打破这个循环。

问题背景

假设两个类 AB 各自拥有一个 shared_ptr 指向对方的实例,形成了循环引用:

class B; // 前向声明

class A {
public:
    std::shared_ptr<B> b_ptr;
};

class B {
public:
    std::shared_ptr<A> a_ptr;
};

在这种情况下,即使超出作用域后 ABshared_ptr 都销毁了,但由于两者互相引用,shared_ptr 的引用计数不会归零,导致两者的资源都无法释放。

使用 weak_ptr 打破循环引用

为了防止循环引用,可以将其中一个类的 shared_ptr 改为 weak_ptr,使其不参与引用计数,从而允许资源在不再使用时正确释放。例如,将 B 中的指针改为 weak_ptr

#include <memory>

class B; // 前向声明

class A {
public:
    std::shared_ptr<B> b_ptr; // 正常的 shared_ptr,拥有 B 的所有权
    ~A() { std::cout << "A destroyed\n"; }
};

class B {
public:
    std::weak_ptr<A> a_ptr; // 使用 weak_ptr 打破循环引用
    ~B() { std::cout << "B destroyed\n"; }
};

int main() {
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();

    a->b_ptr = b;
    b->a_ptr = a; // B 对 A 的引用不会增加 A 的引用计数

    // 当 main 函数结束时,a 和 b 会自动销毁,调用析构函数
}

工作原理

  • A 拥有 Bshared_ptr,因此 B 的引用计数增加。
  • B 使用 weak_ptr 指向 A,不增加 A 的引用计数。
  • main 函数结束时,shared_ptr 计数归零,资源顺利释放。

总结

通过使用 weak_ptr,可以避免 shared_ptr 之间相互引用时的内存泄漏问题,确保资源按预期被释放。

1.5.8 shared_ptr怎么知道跟他共享对象的指针释放了?

std::shared_ptr 通过引用计数来跟踪对象的生命周期,确保共享的对象在没有任何 shared_ptr 指向它时才会释放。当所有指向该对象的 shared_ptr 实例被销毁后,对象才会自动释放。

工作机制

  1. 引用计数:每个 shared_ptr 在指向某对象时,内部会维护一个引用计数use_count),用来记录指向该对象的 shared_ptr 数量。shared_ptr 的构造、复制或赋值会增加引用计数,而析构、重置或释放会减少引用计数。

  2. 自动释放:当 use_count 归零,即没有 shared_ptr 实例再指向该对象时,shared_ptr 会自动销毁托管的对象,并释放相关资源。

例子

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> ptr1 = std::make_shared<int>(10);
    std::cout << "Use count after ptr1: " << ptr1.use_count() << std::endl; // 输出 1

    {
        std::shared_ptr<int> ptr2 = ptr1; // 引用计数 +1
        std::coutshared_ << "Use count after ptr2: " << ptr1.use_count() << std::endl; // 输出 2
    } // 离开作用域,ptr2 被销毁,引用计数 -1

    std::cout << "Use count after ptr2 out of scope: " << ptr1.use_count() << std::endl; // 输出 1
    return 0;
} // 离开作用域,ptr1 被销毁,引用计数变为 0,对象自动释放

总结

shared_ptr 通过引用计数来监控共享对象的状态,只有在引用计数为 0 时才会释放对象,因此任何一个 shared_ptr 不需要主动跟踪其他 shared_ptr 的销毁状态。

1.5.9 说说智能指针及其实现,shared_ptr 线程安全性,原理

智能指针是 C++11 引入的自动化内存管理工具,用于管理动态分配的资源,防止内存泄漏。常见的智能指针包括 std::unique_ptrstd::shared_ptrstd::weak_ptr。它们依赖 RAII(资源获取即初始化)原则来控制对象的生命周期,确保资源在不再需要时被自动释放。智能指针主要通过引用计数、所有权转移、弱引用等机制来实现。

智能指针的实现

  1. std::unique_ptr

    • 实现:仅有单一所有权,一个对象只能被一个 unique_ptr 实例持有。转移所有权时通过移动语义(move)转移资源。
    • 使用场景:适用于只需单一拥有者的对象,不允许共享,不可复制,只能移动。
    • 线程安全性:不支持多线程访问,不能多线程共享同一个 unique_ptr,因为它独占资源。
  2. std::shared_ptr

    • 实现:通过共享所有权管理资源。每个 shared_ptr 都持有一个指向共享控制块的指针,该控制块中包含引用计数,跟踪资源的共享情况。当引用计数归零时释放资源。
    • 使用场景:适合需要多个指针共同持有同一对象的情况,比如在图、树等数据结构中。
  3. std::weak_ptr

    • 实现:用于解决循环引用问题。weak_ptr 仅作为对 shared_ptr 所指对象的弱引用,不增加引用计数,但可以检查对象是否存在。
    • 使用场景:用于临时观察对象生命周期,避免循环引用,比如双向关联的数据结构。

shared_ptr 的线程安全性

C++ 标准对 shared_ptr 提供了基本的线程安全保证,主要体现在引用计数更新的原子性上。

  • 线程安全的引用计数shared_ptr 使用了原子操作(std::atomic)来管理引用计数的增加和减少,因此多个线程可以安全地复制和销毁 shared_ptr。这意味着,即便多个线程同时持有同一 shared_ptr 实例的副本,也可以保证引用计数的正确性,确保资源在引用计数归零后才会释放。

  • 指针的线程安全性:虽然 shared_ptr 对引用计数的操作是线程安全的,但对于托管的资源本身或 shared_ptr 的数据成员并不提供线程安全保护。如果多个线程需要同时访问或修改托管对象的数据,仍需手动添加同步措施(如使用 mutex),以确保数据安全。

shared_ptr 的实现原理

  1. 控制块(Control Block)
    shared_ptr 内部会创建一个控制块(control block),其中包含:

    • 引用计数(use count):跟踪当前有多少 shared_ptr 指向同一对象。
    • 弱引用计数(weak count):跟踪当前有多少 weak_ptr 指向同一对象。
    • 托管对象的指针:指向实际管理的资源。

    shared_ptr 被复制时,use count 增加;当 shared_ptr 被销毁时,use count 减少。use count 归零时,托管对象销毁,而当 weak count 也为零时,控制块也会销毁。

  2. 引用计数的原子操作
    为了保证引用计数操作的线程安全性,shared_ptr 使用了原子操作来增加和减少引用计数。这使得多个 shared_ptr 实例可以安全地在多线程环境中操作同一对象的引用计数,而不会出现计数不一致的情况。

总结

智能指针通过引用计数、独占所有权和弱引用等机制来有效管理资源。shared_ptr 通过原子化引用计数实现了基本的线程安全,确保多线程中共享的对象在最后一个 shared_ptr 销毁后才释放资源。

1.5.10 请回答智能指针有没有内存泄漏的情况?

智能指针是C++11引入的一个功能,用于自动管理动态分配的内存,减少内存泄漏的风险。常见的智能指针包括 std::unique_ptrstd::shared_ptrstd::weak_ptr。然而,智能指针并不能完全消除内存泄漏,以下是一些可能导致内存泄漏的情况:

1. 循环引用

  • 情况:当两个或多个 std::shared_ptr 互相引用时,可能导致循环引用。由于引用计数无法减少到零,内存无法释放。
  • 解决方案:使用 std::weak_ptr 来打破循环引用。

2. 不当使用 newdelete

  • 情况:如果手动分配内存(使用 new)而不使用智能指针,或者在智能指针的管理范围外使用 delete,可能会导致内存泄漏。
  • 解决方案:确保始终通过智能指针管理动态分配的内存。

3. 被忽略的异常

  • 情况:如果在使用智能指针的过程中抛出异常,某些资源可能没有被正确释放,导致内存泄漏。
  • 解决方案:使用 RAII(资源获取即初始化)原则,确保资源在异常情况下也能正确释放。

4. 使用不当

  • 情况:如果将智能指针的所有权错误地转移或丢失,可能导致内存泄漏。例如,将 std::unique_ptr 传递到函数时,如果没有正确地转移所有权,原有的智能指针仍会保留对内存的引用。
  • 解决方案:使用 std::move 进行所有权转移,确保智能指针的生命周期和所有权管理是清晰的。

总结

尽管智能指针大大减少了内存泄漏的可能性,但在使用它们时仍需谨慎,确保遵循最佳实践来避免潜在的内存管理问题。

1.5.11 简述C++11的四种类型转换

C++11引入了四种主要的类型转换运算符,它们分别是:

1. static_cast

  • 用途:用于进行类型安全的转换,可以在相关类型之间进行转换,例如:
    • 基类指针到派生类指针(需要保证安全)。
    • 基本数据类型之间的转换(如 intfloat)。
  • 特征
    • 在编译时进行类型检查。
    • 不会进行运行时类型检查。

2. dynamic_cast

  • 用途:用于进行安全的向下转型(从基类到派生类),适用于多态类型(即具有虚函数的类)。
  • 特征
    • 在运行时进行类型检查。
    • 如果转换不成功,返回 nullptr(对于指针类型)或抛出 std::bad_cast 异常(对于引用类型)。

3. const_cast

  • 用途:用于添加或移除类型的 constvolatile 限定符。
  • 特征
    • 允许修改常量对象的值(需谨慎使用)。
    • 主要用于在需要修改 const 对象时的场景,但需要确保对象的原始类型允许修改。

4. reinterpret_cast

  • 用途:用于在任意类型之间进行低级别的位级别转换。
  • 特征
    • 不进行任何类型检查,可能导致不安全的转换。
    • 通常用于操作底层数据结构、指针类型之间的转换等。

总结表

转换类型用途特点
static_cast类型安全的转换编译时检查,不安全的转换可导致未定义行为
dynamic_cast多态类型间的安全向下转型运行时检查,失败返回 nullptr
const_cast添加或移除 const 限定符允许修改常量对象
reinterpret_cast任意类型之间的低级别位级别转换不安全,几乎不进行检查

这些类型转换运算符使得C++的类型系统更加灵活和强大,开发者可以根据需求选择合适的转换方式。

1.5.12 简述C++11中的auto的具体用法

C++11引入了 auto 关键字,允许编译器根据初始化表达式自动推导变量的类型。这使得代码更加简洁和易读,尤其是在处理复杂类型时。以下是 auto 的具体用法和一些注意事项:

1. 基本用法

  • 声明变量:使用 auto 声明变量时,编译器会根据右侧的表达式推导出变量的类型。
    auto x = 10;           // x 是 int 类型
    auto y = 3.14;        // y 是 double 类型
    auto str = "Hello";   // str 是 const char* 类型
    

2. 与 STL 容器配合使用

  • 迭代器:在使用标准模板库(STL)时,auto 可以简化迭代器的声明。
    std::vector<int> vec = {1, 2, 3};
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";
    }
    

3. Lambda 表达式

  • 使用 auto 作为参数类型:在 lambda 表达式中,auto 可以用于参数类型推导。
    auto lambda = [](auto a, auto b) {
        return a + b; // a 和 b 的类型由调用时推导
    };
    

4. 多重类型推导

  • 结构化绑定:C++17引入的结构化绑定特性与 auto 结合,可以同时推导多个变量的类型。
    std::tuple<int, double> tup = {1, 2.5};
    auto [a, b] = tup; // a 是 int,b 是 double
    

5. 注意事项

  • 不能用于声明函数返回类型auto 不能单独用于函数的返回类型,但可以通过尾返回类型语法实现。

    auto func() -> int {
        return 42;
    }
    
  • 推导规则auto 不能用于没有初始化的变量声明,因为没有类型推导的上下文。

    auto z; // 错误:缺少初始化
    

总结

使用 auto 关键字可以提高代码的可读性和简洁性,尤其在处理复杂类型时(如容器、迭代器和lambda)。但使用时应注意类型推导的规则,确保代码的可维护性和可理解性。

1.5.13 简述一下C++11 中的可变参数模板新特性

C++11引入了可变参数模板(Variadic Templates),使得模板可以接收可变数量的参数。这一特性极大地增强了模板编程的灵活性,支持更为通用的代码结构。以下是可变参数模板的一些关键点和用法:

1. 基本语法

可变参数模板的定义使用 template<typename... Args>,其中 Args 是一个类型参数包,可以接收任意数量的类型。

template<typename... Args>
void func(Args... args) {
    // 函数体
}

2. 参数展开

在函数内部,可以使用参数展开(parameter pack expansion)来处理这些参数。

#include <iostream>

template<typename... Args>
void print(Args... args) {
    (std::cout << ... << args) << std::endl; // 使用折叠表达式(C++17)
}

int main() {
    print(1, 2.5, "Hello"); // 输出: 1 2.5 Hello
}

3. 递归模板

可以通过递归的方式处理参数包,实现如计算参数个数、拼接字符串等功能。

template<typename T>
void printOne(T t) {
    std::cout << t << " ";
}

template<typename T, typename... Args>
void printOne(T t, Args... args) {
    std::cout << t << " ";
    printOne(args...); // 递归处理剩余参数
}

4. 结合其他特性

可变参数模板可以与其他C++特性(如 decltype、类型萃取、特化等)结合使用,以实现更复杂的功能。

5. 示例:参数数量计算

使用可变参数模板可以轻松实现参数数量的计算。

template<typename... Args>
struct Count;

template<typename T, typename... Args>
struct Count<T, Args...> {
    static const int value = 1 + Count<Args...>::value; // 递归计算
};

template<>
struct Count<> {
    static const int value = 0; // 基础情况
};

// 用法
std::cout << Count<int, double, char>::value; // 输出: 3

6. 使用场景

可变参数模板适用于需要处理不确定数量参数的场景,如:

  • 实现类似于 printf 的函数。
  • 在库函数中处理任意数量的输入。
  • 生成特定类型的复合类型。

总结

可变参数模板是C++11的重要特性,它提供了更大的灵活性和扩展性,简化了处理多个参数的代码。通过参数展开和递归,开发者可以方便地构建复杂的模板功能。

1.5.14 简述C++中的Lambda新特性

C++11引入了Lambda表达式,使得可以在代码中定义匿名函数。Lambda表达式极大地提高了代码的简洁性和可读性,特别是在需要临时函数的场景中。以下是C++中的Lambda表达式的一些关键特性和用法:

1. 基本语法

Lambda表达式的基本语法如下:

[capture](parameters) -> return_type {
    // 函数体
}
  • capture:捕获外部变量的方式。
  • parameters:参数列表,可以为空。
  • return_type:可选,返回类型。
  • 函数体:实际的函数实现。

2. 捕获外部变量

Lambda可以捕获外部作用域的变量,有多种捕获方式:

  • 值捕获[x]):复制外部变量 x 的值。
  • 引用捕获[&x]):引用外部变量 x
  • 全局捕获[=]):以值捕获所有外部变量。
  • 全局引用捕获[&]):以引用捕获所有外部变量。
int x = 10;
auto lambda = [x]() { return x + 1; }; // 捕获 x 的值
std::cout << lambda(); // 输出: 11

auto lambda_ref = [&x]() { x++; }; // 捕获 x 的引用
lambda_ref();
std::cout << x; // 输出: 11

3. 使用场景

  • 作为参数传递:Lambda可以作为参数传递给标准库算法,如 std::sortstd::for_each 等。
std::vector<int> vec = {1, 2, 3, 4, 5};
std::for_each(vec.begin(), vec.end(), [](int n) { std::cout << n << " "; });
  • 简化代码:Lambda可以替代临时函数或函数对象,减少冗余代码。

4. 返回类型推导

C++14引入了自动返回类型推导,允许省略返回类型:

auto lambda = [](int a, int b) { return a + b; }; // 自动推导返回类型为 int

5. 递归Lambda

C++14支持递归Lambda,通过 std::function 或者 [] 捕获自身。

std::function<int(int)> factorial = [&](int n) {
    return (n <= 1) ? 1 : n * factorial(n - 1);
};
std::cout << factorial(5); // 输出: 120

6. 注意事项

  • 生命周期:Lambda捕获的外部变量在Lambda使用时必须是有效的。
  • 类型:Lambda表达式的类型是唯一的,因此不能直接传递给需要具体类型的接口。

总结

Lambda表达式是C++11的重要特性,提供了简洁的语法来定义匿名函数,支持捕获外部变量,极大地增强了代码的可读性和可维护性。通过灵活的捕获机制和简化的函数定义,Lambda使得现代C++编程更加高效和灵活。


http://www.kler.cn/a/372921.html

相关文章:

  • 多线程显示 CSV 2 PNG 倒计时循环播放
  • 沈阳乐晟睿浩科技有限公司抖音小店新篇章
  • 【论文精读】ID-like Prompt Learning for Few-Shot Out-of-Distribution Detection
  • 部署Leanote 蚂蚁笔记
  • 【vue】14.插槽:构建可复用组件的关键
  • 大数据-191 Elasticsearch - ES 集群模式 配置启动 规划调优
  • 【Linux】-常见指令(1)
  • MS01SF1 精准测距UWB模组助力露天采矿中的人车定位安全和作业效率提升
  • 62.不同路径 63.不同路径ii
  • 我的电脑问题
  • C++设计模式创建型模式———单例模式
  • 计算机网络(Ⅵ)应用层原理
  • HTML入门教程20:HTML头部
  • 代码随想录第十五天
  • oracle和mysql的区别常用的sql语句
  • 模块化CSS
  • 汽车零部件展|2025 第十二届广州国际汽车零部件加工技术及汽车模具展览会邀您共赏汽车行业盛会
  • 使用 Git 命令将本地项目上传到 GitLab
  • JVM 复习1
  • 修改IP分组头部内容的场景
  • 【部署与升级-会议签到的web安装】
  • c++应用网络编程之十三Linux下的epoll模式应用
  • 2D/3D医学图像配准算法
  • MongoDB-Plus
  • web前后端交互方式有哪些?
  • 在manjaro 2024里使用yay命令安装ROS2