面试真题 | Momenta c++

一面(1月13日)

1. 自我介绍

回答

您好,我叫[您的名字],毕业于[毕业院校],专业是[专业名称]。在校期间,我深入学习了C++编程语言、数据结构、算法以及操作系统等计算机基础课程,并积极参与了多个项目开发,积累了丰富的实践经验。

毕业后,我加入了[公司名称],担任嵌入式软件开发工程师的职位。在这里,我主要负责嵌入式系统的设计与开发,包括硬件驱动编写、操作系统移植、应用层软件开发等工作。通过参与多个大型项目,我不仅掌握了嵌入式C++编程的精髓,还熟悉了ARM、DSP等嵌入式处理器的架构和编程模型。同时,我也对RTOS(实时操作系统)有了深入的理解和应用,如FreeRTOS、VxWorks等。

在专业技能方面,我精通C++编程语言,熟悉面向对象编程思想,能够熟练运用STL库进行高效编程。此外,我还掌握了Git版本控制工具,能够高效地管理代码版本和协作开发。在操作系统方面,我熟悉Linux和Windows系统的开发环境,能够灵活运用Makefile、CMake等构建工具进行项目管理。

个人兴趣方面,我对新技术充满好奇,喜欢研究最新的嵌入式技术和开发工具。同时,我也热衷于参加开源社区和技术论坛,与同行交流心得,不断提升自己的技术水平。

追问及回答

追问1

  • 问题:在您的嵌入式开发经验中,遇到过哪些技术难题?您是如何解决的?
  • 回答:在嵌入式开发过程中,我遇到过不少技术难题。例如,在一次项目中,我们需要将一个大型的应用软件移植到一个资源受限的嵌入式平台上。由于平台资源有限,导致软件运行效率低下,甚至频繁崩溃。为了解决这个问题,我首先对软件进行了性能分析,找出了瓶颈所在。然后,我对代码进行了优化,包括算法优化、内存管理优化等方面。同时,我也与硬件团队紧密合作,对硬件资源进行了合理的分配和调度。最终,我们成功地将软件移植到了该平台上,并实现了高效稳定的运行。

追问2

  • 问题:在嵌入式开发中,您认为最重要的技能是什么?为什么?
  • 回答:在嵌入式开发中,我认为最重要的技能是问题解决能力和持续学习的能力。嵌入式系统通常涉及复杂的硬件和软件环境,开发过程中难免会遇到各种问题和挑战。具备问题解决能力意味着能够迅速定位问题、分析原因并找到有效的解决方案。而持续学习的能力则意味着能够跟上技术发展的步伐,不断掌握新的技术和工具,以适应不断变化的市场需求。这两种能力相辅相成,共同构成了嵌入式开发工程师的核心竞争力。

追问3

  • 问题:您如何看待嵌入式开发中的团队协作?在您的经验中,如何确保团队协作的高效性?
  • 回答:嵌入式开发通常是一个复杂的系统工程,需要多个团队和成员的紧密协作。我认为团队协作在嵌入式开发中至关重要,因为它能够汇聚众人的智慧和力量,共同攻克技术难题,提高开发效率和质量。在我的经验中,确保团队协作的高效性需要做到以下几点:一是建立明确的分工和职责划分,确保每个成员都清楚自己的任务和目标;二是加强沟通和交流,定期召开项目会议和技术讨论会,及时分享进展和遇到的问题;三是采用敏捷开发等先进的项目管理方法,提高开发过程的灵活性和响应速度;四是注重团队文化和氛围的建设,增强团队成员之间的信任和凝聚力。

项目相关问题回答及面试官追问

项目介绍

问题:请介绍你参与过的C++项目,包括项目背景、你的角色、所用技术栈以及你贡献的部分。

回答

在我职业生涯中,我有幸参与了一个名为“智能仓储管理系统”的C++项目。这个项目旨在通过自动化和智能化技术提升仓储管理的效率和准确性。

项目背景: 随着电子商务和物流行业的迅速发展,仓储管理面临着前所未有的挑战。传统的人工管理方式已经无法满足日益增长的仓储需求,因此我们的团队被委托开发一个智能仓储管理系统。该系统需要能够实时监控库存状态、自动化处理订单、优化仓储布局以及提供数据分析等功能。

我的角色: 在这个项目中,我担任了核心开发者的角色。我负责了系统的架构设计、关键模块的开发以及与其他团队成员的协作。我需要与产品经理、UI设计师和后端开发者紧密合作,确保系统的功能和性能满足项目需求。

所用技术栈: 我们采用了C++作为主要开发语言,因为它提供了高效的内存管理和强大的面向对象特性,非常适合开发性能要求较高的系统。此外,我们还使用了Qt框架来构建用户界面,因为它提供了丰富的UI组件和跨平台支持。在数据库方面,我们选择了MySQL作为存储后端,并利用SQL语句进行数据处理。为了提升系统的实时性和响应速度,我们还采用了多线程编程技术。

我贡献的部分: 在项目中,我主要负责了订单处理模块和仓储布局优化模块的开发。订单处理模块需要能够接收并解析订单信息,然后调用相应的库存管理和拣货逻辑进行处理。我设计了高效的数据结构和算法来优化订单处理流程,确保系统能够处理大量的并发订单。仓储布局优化模块则通过分析库存数据和订单分布来优化仓储布局,提高拣货效率。我利用图论和启发式搜索算法来求解布局优化问题,并实现了可视化界面来展示优化结果。

面试官追问及相关答案

追问1

  • 问题:在开发过程中,你遇到了哪些技术难题?你是如何解决的?
  • 答案:在开发过程中,我们遇到了多个技术难题。其中一个是多线程编程中的线程安全问题。由于系统需要处理大量的并发订单,我们采用了多线程技术来提高处理速度。然而,多线程编程带来了线程安全问题,如数据竞争和死锁等。为了解决这个问题,我采用了互斥锁和条件变量等同步机制来保护共享资源,并进行了详细的测试和调试工作。另一个难题是仓储布局优化算法的设计和实现。由于仓储布局优化是一个复杂的组合优化问题,我们采用了启发式搜索算法来求解。然而,算法的性能和准确性受到多种因素的影响,如初始解的选择、搜索策略的调整等。为了优化算法性能,我进行了大量的实验和参数调优工作,并与团队成员进行了深入的讨论和协作。

追问2

  • 问题:在项目中,你是如何与其他团队成员进行协作的?你认为有效的团队协作有哪些关键因素?
  • 答案:在项目中,我与其他团队成员进行了紧密的协作。我们定期召开项目会议来讨论项目进展、问题和解决方案。在开发过程中,我积极与产品经理沟通需求变更和功能调整,确保系统的功能和性能满足项目需求。同时,我也与UI设计师和后端开发者进行了深入的交流和协作,共同解决了界面设计和数据交互等方面的问题。我认为有效的团队协作的关键因素包括明确的项目目标和分工、良好的沟通和协作能力、以及共同解决问题的意识和行动。这些因素有助于团队成员之间建立信任、提高协作效率并最终实现项目的成功。

追问3

  • 问题:在项目中,你是如何进行代码测试和调试的?你认为代码质量和测试的重要性体现在哪些方面?
  • 答案:在项目中,我采用了单元测试、集成测试和性能测试等多种测试方法来确保代码的质量和稳定性。我编写了详细的测试用例来验证系统的功能和性能是否符合预期。在调试过程中,我利用了调试器和日志输出等工具来定位和解决代码中的错误和异常。我认为代码质量和测试的重要性体现在多个方面。首先,高质量的代码能够减少系统中的错误和缺陷,提高系统的稳定性和可靠性。其次,有效的测试能够发现潜在的问题和风险,并为修复和优化提供有力的支持。最后,代码质量和测试也是项目成功交付和客户满意度的重要保障之一。因此,在开发过程中我们应该始终关注代码质量和测试工作,确保系统的质量和性能达到预期目标。

C++锁相关问题

3. C++锁

请解释C++中锁的概念,并列举几种常见的锁类型(如互斥锁、自旋锁等)。

在C++多线程编程中,锁是一种同步机制,用于保护共享资源,防止多个线程同时访问这些资源,从而避免数据竞争和条件竞争等并发问题。锁通过提供一种机制,使得在任何给定时间内,只有一个线程能够持有锁并访问受保护的资源。

常见的锁类型包括

  1. 互斥锁(Mutex)

    • 互斥锁是最常见的锁类型,用于保护临界区,确保同一时间只有一个线程能够进入临界区。
    • 互斥锁在获取失败时会阻塞线程,直到锁被释放。
  2. 自旋锁(Spinlock)

    • 自旋锁与互斥锁类似,但它在获取锁失败时不会阻塞线程,而是会不断循环检查锁是否可用。
    • 自旋锁适用于短时间的等待,因为长时间的自旋会浪费CPU资源。
  3. 读写锁(Read-Write Lock)

    • 读写锁允许多个线程同时读取共享资源,但在写入时必须独占访问。
    • 这提高了读取操作的并发性,同时保证了写入操作的数据一致性。
  4. 递归锁(Recursive Mutex)

    • 递归锁允许同一个线程多次获取锁而不会导致死锁。
    • 这在复杂的函数调用链中非常有用,其中同一个线程可能需要多次进入受保护的临界区。
  5. 条件变量锁(Condition Variable)

    • 条件变量锁不是直接用于保护资源的锁,而是用于线程间的同步。
    • 它允许线程等待某个条件成立,并在条件满足时被唤醒。

描述锁在多线程编程中的作用以及使用锁时需要注意的事项。

作用

  • 保护共享资源:锁可以防止多个线程同时访问共享资源,从而避免数据竞争和条件竞争。
  • 保证数据一致性:通过确保对共享资源的访问是串行的,锁可以维护数据的一致性和完整性。
  • 实现线程同步:锁可以用于协调线程之间的执行顺序,确保它们按照预期的方式运行。

使用锁时需要注意的事项

  1. 避免死锁:确保锁的获取和释放顺序一致,避免循环等待条件,以防止死锁的发生。
  2. 减少锁粒度:尽量缩小临界区的范围,以减少锁持有时间,提高系统的并发性能。
  3. 避免优先级反转:在高优先级线程等待低优先级线程释放锁时,可能会发生优先级反转问题。可以使用优先级继承协议来解决这个问题。
  4. 考虑性能开销:锁的操作(如获取和释放)是有开销的,特别是在高并发环境下。因此,需要权衡锁的使用和系统的性能需求。
  5. 使用高级同步机制:在可能的情况下,考虑使用更高级的同步机制(如信号量、读写锁、条件变量等)来优化系统的并发性能。

面试官追问及相关答案

追问1

  • 问题:在C++中,如何实现一个互斥锁?可以使用标准库中的哪些类?
  • 答案:在C++中,可以使用标准库中的std::mutex类来实现一个互斥锁。std::mutex提供了lock()unlock()try_lock()等成员函数来管理锁的状态。此外,还可以使用std::lock_guardstd::unique_lock等RAII(Resource Acquisition Is Initialization)包装器来自动管理锁的生命周期,从而避免忘记释放锁导致的资源泄露问题。

追问2

  • 问题:什么是死锁?在C++多线程编程中,如何避免死锁?
  • 答案:死锁是指两个或多个线程相互等待对方释放资源,从而导致所有线程都无法继续执行的状态。在C++多线程编程中,可以通过以下方法来避免死锁:
    • 确保所有线程以相同的顺序获取锁。
    • 使用尝试锁(如std::mutex::try_lock())来避免阻塞等待。
    • 使用锁超时机制来限制等待时间。
    • 使用锁所有权传递机制来确保锁的正确释放。
    • 避免嵌套锁(即在一个锁内获取另一个锁)的情况,或者确保嵌套锁的顺序一致。

追问3

  • 问题:在高性能计算场景中,自旋锁和互斥锁哪个更适合?为什么?
  • 答案:在高性能计算场景中,自旋锁和互斥锁的选择取决于多个因素,包括锁的持有时间、系统的CPU资源等。如果锁的持有时间很短(即临界区很小),那么自旋锁可能更适合,因为它避免了线程切换的开销。然而,如果锁的持有时间较长,自旋锁会浪费大量的CPU资源,此时互斥锁更为合适,因为它允许线程在等待锁时进入睡眠状态,从而节省CPU资源。因此,在选择锁类型时,需要根据具体的应用场景和需求进行权衡。

C++线程相关问题

4. C++线程

请解释C++中线程的概念,并描述如何创建和管理线程。

回答

在C++中,线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。每个线程都拥有独立的指令和计数器,但共享进程所拥有的资源,如内存和打开的文件。线程的执行是并发的,这意味着它们可以同时运行,尽管实际上在任何给定的瞬间只有一个线程可以真正在CPU上运行(在多核处理器上,多个线程可以同时运行)。

在C++11标准中,引入了原生的线程支持,通过std::thread类来创建和管理线程。创建线程的基本步骤如下:

  1. 包含头文件:首先需要包含<thread>头文件。

  2. 定义线程函数:线程函数是一个普通的C++函数,它将被新线程执行。这个函数可以接收参数,并返回一个值(如果有的话)。

  3. 创建线程对象:通过std::thread类的构造函数创建一个线程对象,并将线程函数作为参数传递给构造函数。如果线程函数需要参数,这些参数也需要传递给构造函数。

  4. 启动线程:当std::thread对象被创建时,线程并不会立即启动。要启动线程,需要调用std::thread对象的join()detach()成员函数。join()会阻塞调用它的线程,直到被join()的线程完成执行。detach()则会使线程在后台运行,独立于创建它的线程。

  5. 管理线程:线程的生命周期可以通过join()detach()来管理。一个线程在被join()detach()之前,被认为是可加入的(joinable)。一旦线程被join()detach(),它就不再是可加入的。尝试对一个不可加入的线程调用join()detach()会导致运行时错误。

线程同步和线程间通信的机制

线程同步是确保多个线程按照预期顺序执行的一种机制。线程间通信则涉及在线程之间传递数据或信号。C++提供了多种线程同步和通信机制:

  1. 互斥锁(Mutex):互斥锁用于保护共享资源,确保同一时间只有一个线程可以访问该资源。C++标准库提供了std::mutex类来实现互斥锁。

  2. 条件变量(Condition Variable):条件变量用于在线程之间等待某个条件成立。一个线程可以在条件变量上等待,而另一个线程可以通知等待的线程条件已经成立。C++标准库提供了std::condition_variablestd::condition_variable_any类来实现条件变量。

  3. 未来和承诺(Future and Promise):未来(std::future)和承诺(std::promise)提供了一种机制,允许一个线程将结果或异常传递给另一个线程。这可以用于线程间的数据传递或同步。

  4. 原子操作(Atomic Operations):C++11引入了原子类型和操作,它们提供了一种无锁的方式来执行线程安全的操作。原子操作通常用于实现低级别的同步机制,如自旋锁。

面试官追问及相关答案

追问1

  • 问题:在多线程编程中,死锁是如何发生的?如何避免死锁?
  • 答案:死锁是指两个或多个线程在执行过程中,因争夺资源而造成的一种僵局,若无外力作用,它们都将无法继续执行。死锁发生的四个必要条件是:互斥条件、请求与保持条件、不剥夺条件和环路等待条件。要避免死锁,可以破坏这四个条件中的至少一个。常见的策略包括:使用资源有序分配法、银行家算法(虽然在实际多线程编程中很少使用)、避免嵌套锁定(即避免一个线程持有多个锁)以及使用超时锁定尝试(如果锁定操作在一定时间内没有成功,则放弃并释放已持有的锁)。

追问2

  • 问题:解释一下std::unique_lockstd::lock_guard的区别,以及在什么情况下你会选择使用它们?
  • 答案std::lock_guard是一个简单的RAII(Resource Acquisition Is Initialization)封装,用于自动管理互斥锁的生命周期。它在构造时锁定互斥锁,在析构时解锁。它不允许在锁定后解锁再重锁,也不允许转移锁的所有权。而std::unique_lock提供了更灵活的锁定机制,它可以在构造时不锁定互斥锁,允许在锁定后解锁再重锁,并且支持锁的所有权转移。在选择使用它们时,如果只需要简单的锁定和解锁操作,std::lock_guard是一个更好的选择,因为它更简单且不容易出错。如果需要更灵活的锁定机制,比如需要在锁定期间暂时释放锁或者需要在多个作用域之间转移锁的所有权,那么std::unique_lock是更合适的选择。

追问3

  • 问题:在C++中,如何实现线程安全的单例模式?
  • 答案:在C++中实现线程安全的单例模式通常需要使用互斥锁来确保只有一个线程可以创建单例实例。一种常见的方法是使用std::call_oncestd::once_flag来保证单例实例的创建是线程安全的。另一种方法是使用局部静态变量(C++11及以后的标准保证了局部静态变量的线程安全性),这种方法既简单又高效。还可以使用std::mutexstd::unique_lock来保护单例实例的创建过程,但这种方法相对复杂且性能可能不如前两种方法。在选择实现方式时,需要根据具体的应用场景和需求来权衡。

C++内存管理

请描述C++中的内存分配和释放机制,包括栈内存和堆内存的使用

C++中的内存管理是一个核心概念,它涉及内存的分配和释放。C++提供了两种主要的内存分配方式:栈内存(stack memory)和堆内存(heap memory)。

栈内存

  • 栈内存是由编译器自动管理的。
  • 当我们定义一个局部变量时,它通常会在栈上分配内存。
  • 栈内存的生命周期与定义它的作用域紧密相关。一旦变量超出了其作用域,编译器会自动释放它所占用的内存。
  • 栈内存分配和释放的速度非常快,因为它是按照后进先出(LIFO)的原则进行管理的。

堆内存

  • 堆内存是由程序员手动管理的。
  • 我们使用new运算符来在堆上分配内存,并使用delete运算符来释放它。
  • 堆内存的生命周期由程序员控制,因此它可以在程序的任何地方被分配和释放。
  • 堆内存分配和释放的速度相对较慢,因为它需要查找可用的内存块并进行复杂的内存管理操作。

讨论智能指针的作用以及如何使用它们来管理内存

智能指针是C++11及以后版本中引入的一种用于自动管理内存的机制。它们通过封装原始指针并提供自动化的内存管理功能,来减少内存泄漏和悬挂指针等问题的发生。

智能指针的主要作用包括:

  • 自动释放内存:智能指针会在其析构函数中自动释放所管理的内存,从而避免了手动调用delete的需要。
  • 异常安全性:在发生异常时,智能指针能够确保所管理的内存被正确释放,从而防止内存泄漏。
  • 所有权管理:智能指针通过不同的实现方式(如std::unique_ptrstd::shared_ptr)来管理对象的所有权,从而避免了多个指针指向同一块内存并导致重复释放或悬挂指针的问题。

智能指针的使用方式如下:

  • std::unique_ptr:这是一个独占所有权的智能指针,它确保同一时间只有一个std::unique_ptr可以指向一个给定的资源。当std::unique_ptr被销毁时,它所管理的资源也会被自动释放。
  • std::shared_ptr:这是一个共享所有权的智能指针,它允许多个std::shared_ptr实例共享对同一个资源的所有权。当最后一个指向该资源的std::shared_ptr被销毁时,资源才会被释放。std::shared_ptr使用控制块和引用计数来管理所有权和资源的释放。

使用智能指针时,需要注意以下几点:

  • 避免将原始指针与智能指针混用,以防止内存泄漏或悬挂指针的问题。
  • 当需要将智能指针传递给函数时,尽量使用引用或值传递的方式,而不是裸指针传递,以保持智能指针的语义和安全性。
  • 注意智能指针的生命周期和作用域,确保它们在不再需要时能够及时释放所管理的内存。

面试官追问及相关答案

追问1

  • 问题:请解释一下std::weak_ptr的作用,以及它如何与std::shared_ptr配合使用来避免循环引用问题?
  • 答案std::weak_ptr是一个不控制对象生命周期的智能指针,它用于解决std::shared_ptr之间的循环引用问题。std::weak_ptr可以指向一个由std::shared_ptr管理的对象,但它不增加对象的引用计数。因此,当所有的std::shared_ptr都被销毁时,对象仍然会被正确地释放,即使还有std::weak_ptr指向它。

为了避免循环引用,我们可以将一个或多个std::shared_ptr成员变量替换为std::weak_ptr。这样,当两个对象相互引用时,它们之间就不会形成循环引用,因为至少有一个引用是通过std::weak_ptr实现的,不会增加引用计数。

追问2

  • 问题:在使用智能指针时,如果忘记了释放内存,会发生什么情况?智能指针是如何解决这个问题的?
  • 答案:在使用智能指针时,如果忘记了释放内存,实际上并不会发生内存泄漏问题,因为智能指针会在其析构函数中自动释放所管理的内存。这是智能指针的核心功能之一,它通过封装原始指针并提供自动化的内存管理功能来减少内存泄漏问题的发生。

具体来说,当智能指针对象被销毁时(例如,当它们超出作用域或被显式删除时),它们的析构函数会被调用。在析构函数中,智能指针会检查它所管理的内存是否已经被释放(对于std::shared_ptr来说,还会检查引用计数是否为零)。如果内存尚未被释放,智能指针会负责释放它。这样,即使程序员忘记了手动释放内存,智能指针也能确保内存被正确释放。

追问3

  • 问题:请解释一下内存泄漏和悬挂指针的概念,并说明智能指针是如何帮助避免这些问题的?
  • 答案:内存泄漏是指程序在动态分配内存后,由于某种原因(如忘记释放内存、程序逻辑错误等)没有释放这块内存,导致系统内存的浪费和可用内存逐渐减少。悬挂指针则是指一个指针指向的内存已经被释放,但该指针仍然被程序使用,这可能导致程序崩溃或不可预测的行为。

智能指针通过自动管理内存来帮助避免这些问题。它们会在适当的时机(如智能指针对象被销毁时)自动释放所管理的内存,从而防止内存泄漏。此外,智能指针还通过避免使用裸指针和提供安全的所有权管理机制来减少悬挂指针问题的发生。例如,std::unique_ptr确保同一时间只有一个智能指针可以指向一个给定的资源,从而避免了多个指针指向同一块已释放内存的问题。

手写string

以下是一个简单的string类的实现,它包含了基本的字符串操作功能,如长度计算、拼接、比较等。为了简洁起见,这个实现没有包括所有的错误处理和内存管理细节,但在嵌入式C++环境中通常是可以接受的。

#include <iostream>
#include <cstring> // for std::strlen, std::strcpy, std::strcat
#include <stde

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

C/C++面试必考必会 文章被收录于专栏

【C/C++面试必考必会】专栏,直击面试核心,精选C/C++及相关技术栈中面试官最爱的必考点!从基础语法到高级特性,从内存管理到多线程编程,再到算法与数据结构深度剖析,一网打尽。助你快速构建知识体系,轻松应对技术挑战。希望专栏能让你在面试中脱颖而出,成为技术岗的抢手人才。

全部评论

相关推荐

new和malloc的区别?c++里new了数组怎么释放?32位系统的指针变量占多大字节?int*呢?空类呢?c++多态?为什么需要虚析构?虚析构实现原理?c++可以不写构造函数吗?编译器默认除了构造还会有什么?深拷贝和浅拷贝区别?智能指针哪个c++版本引入的,作用?用过哪些容器?项目里用了哪些容器?为什么用qlist?qmap什么是key?模板的好处?什么时候会用?设计模式了解哪些?自己写过吗?单例模式怎么写?单例要返回非静态指针类型怎么弄?队列和栈的区别?osi七层以及相应协议?网络层的协议?应用层?一些项目问题...网络加密算法知道哪些?tcp的三次握手四次挥手回收写过吗?写服务器基本模...
AI牛可乐:哇,你分享了好多面试经验呢,听起来你准备得很充分呢!👍 关于你提到的技术问题,我来帮你解答一下吧: 1. `new` 和 `malloc` 的区别?`new` 会自动调用构造函数,而 `malloc` 只负责分配内存。`new` 数组释放时用 `delete[]`,比如 `delete[] myArray;`。 2. 在32位系统中,指针变量通常占4个字节,`int*` 也是如此。 3. 空类的实例通常占1个字节,但具体实现可能不同。 4. C++多态允许我们通过基类指针或引用来调用派生类的函数。需要虚析构是因为基类指针可能指向派生类对象,不使用虚析构可能导致派生类析构函数不被调用。 5. 如果不写构造函数,编译器会提供一个默认构造函数。默认构造函数会进行成员变量的默认初始化。 6. 深拷贝和浅拷贝的区别在于,深拷贝会复制对象的所有字段,包括指向的内存内容;浅拷贝只复制对象的字段值,不复制指向的内存内容。 7. 智能指针是在C++11版本引入的,用于自动管理内存,防止内存泄漏。 8. 常用的容器有 `vector`、`list`、`map` 等。具体使用哪个容器取决于项目需求。 关于你问的其他问题,我简单回答一下: - `QList` 通常用于需要频繁插入和删除的场景。 - `QMap` 的 `key` 是映射表中的键值。 - 模板的好处是提高代码复用性,减少代码冗余。 - 设计模式有很多种,比如单例模式、工厂模式等。单例模式可以通过静态成员函数实现。 现在,悄悄告诉你一个小秘密,点击我的头像,我们可以私信聊天哦~🎉 那么,你平时在项目中都用过哪些容器呢?有没有什么特别的经验或者技巧可以分享的呢?😊
点赞 评论 收藏
分享
评论
7
46
分享

创作者周榜

更多
牛客网
牛客企业服务