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

C++面试模拟01

第一部分:基础知识

  1. :解释 const 关键字的作用,以及在什么场景下你会使用 const

  2. :在 C++ 中,newmalloc 的区别是什么?

  3. :请解释什么是“深拷贝”和“浅拷贝”?在什么情况下我们需要进行深拷贝?

  4. :C++ 中的引用和指针有什么区别?各自的使用场景是什么?


第二部分:面向对象编程

  1. :请解释 C++ 中的“继承”和“多态”。可以举例说明如何实现多态吗?

  2. :假设你有一个基类 Shape 和派生类 CircleRectangle。如何设计一个类层次结构来支持多态调用?请写出代码示例。

  3. :C++ 支持多重继承(multiple inheritance),但是它可能会带来问题。请解释这些问题并说明如何使用虚继承来解决其中一个问题。


第三部分:STL(标准模板库)

  1. :C++ 标准库中的 std::vectorstd::list 有什么区别?分别适用于什么场景?

  2. std::mapstd::unordered_map 的区别是什么?它们的时间复杂度分别是多少?

  3. :在什么情况下你会使用 std::deque 而不是 std::vectorstd::list


第四部分:并发与多线程

  1. :请解释 C++ 中的多线程机制。如何使用 std::thread 创建线程?请写出代码示例。

  2. :什么是互斥锁(Mutex)?如何在 C++ 中使用它来保护共享资源?

  3. :什么是条件变量(Condition Variable)?请简要描述它的使用场景。


第五部分:C++ 高级特性

  1. :C++11 引入了移动语义(Move Semantics)。请解释移动语义的作用以及它与复制语义的区别。

  2. :什么是 RAII(资源获取即初始化)?为什么在 C++ 中非常重要?

  3. :C++20 引入了协程(Coroutines)。你能描述一下协程的基本概念和使用场景吗?


第六部分:现场编程

  1. :写一个函数,接收一个整数数组,移除所有重复的元素,并返回不重复的元素个数。请尽可能优化时间复杂度和空间复杂度。
int removeDuplicates(std::vector<int>& nums);
  1. :请写一个单例模式(Singleton Pattern)的实现。保证线程安全。

结束问题

  1. :在你的开发经历中,是否遇到过多线程的竞争条件?你是如何解决的?

  2. :你平时如何优化代码性能?有哪些常用的工具和方法?

 

newmalloc 的区别

  • 类型检查

    • new:会调用构造函数,分配内存时会执行类型检查。
    • malloc:不执行类型检查,返回 void* 类型的指针,需要手动转换类型。
  • 返回类型

    • new:返回指定类型的指针,不需要类型转换。
    • malloc:返回 void*,需要显式进行类型转换。
  • 异常处理

    • new:如果分配失败,会抛出 std::bad_alloc 异常。
    • malloc:如果分配失败,返回 NULL,需要手动检查。
  • 构造/析构函数

    • new:调用构造函数,负责初始化对象。
    • malloc:只分配内存,不会调用构造函数。
  • 释放内存

    • new:使用 delete 释放内存,并调用析构函数。
    • malloc:使用 free 释放内存,不会调用析构函数。

引用和指针的区别

  • 引用(Reference)

    • 引用是某个变量的别名,必须在声明时进行初始化,之后不能更改引用的对象。
    • 语法简单、直观,不需要解引用操作符(*)。
    • 一旦绑定到某个对象,就不能重新绑定。
  • 指针(Pointer)

    • 指针是存储内存地址的变量,可以指向任意对象或 NULL,可以在运行时改变所指向的对象。
    • 需要使用解引用操作符(*)来访问指向的对象。
    • 指针可以被重新分配、动态分配内存,使用更加灵活。
  • 区别

    • 引用不能为空,指针可以为空。
    • 引用一旦绑定,不能更改;指针可以在运行时改变指向的对象。
    • 引用更适合用于参数传递,指针更适合需要动态管理内存的场景。
  • 使用场景

    • 引用适用于函数参数传递和返回值,当你想避免拷贝数据时,通常使用引用。
void foo(int& ref);  // 引用传递

指针适用于需要动态分配内存的场景,或者需要改变指向的对象。

int* ptr = new int(5);  // 动态分配内存

 

C++ 中的“继承”和“多态”

  • 继承

    • 继承是一种面向对象编程的机制,允许从一个现有的类(基类/父类)创建一个新类(派生类/子类),以复用基类的成员变量和成员函数。

    • 继承可以让派生类拥有基类的属性和方法,并且可以扩展新的方法或重写基类的方法。

多态

  • 多态是指相同的函数或运算符在不同的对象上表现出不同的行为。C++ 支持编译时多态(通过函数重载和运算符重载)和运行时多态(通过虚函数实现)。

  • 多态使得程序能够通过基类的指针或引用来调用派生类的实现,从而实现灵活性。

  • 示例(虚函数实现多态):

class Base {
public:virtual void show() { std::cout << "Base class" << std::endl; }
};class Derived : public Base {
public:void show() override { std::cout << "Derived class" << std::endl; }
};void func(Base& obj) {obj.show();  // 会根据传入的对象类型调用相应的 show() 方法
}

设计 ShapeCircleRectangle 类层次结构

#include <iostream>
#include <cmath>class Shape {
public:virtual double area() const = 0;   // 纯虚函数,表示该类是抽象类virtual ~Shape() {}                // 虚析构函数
};class Circle : public Shape {
private:double radius;
public:Circle(double r) : radius(r) {}double area() const override {     // 实现多态return M_PI * radius * radius;}
};class Rectangle : public Shape {
private:double width, height;
public:Rectangle(double w, double h) : width(w), height(h) {}double area() const override {return width * height;}
};int main() {Shape* shapes[2];shapes[0] = new Circle(5.0);shapes[1] = new Rectangle(4.0, 6.0);for (int i = 0; i < 2; i++) {std::cout << "Area: " << shapes[i]->area() << std::endl;delete shapes[i];  // 注意内存管理}return 0;
}

 

C++ 中的多重继承问题及虚继承

  • 多重继承的问题
    • 二义性问题:当一个类同时继承自两个具有同名成员的基类时,派生类会出现二义性。

    • 菱形继承问题:在多重继承中,如果一个类从两个基类派生,而这两个基类又继承自同一个基类,派生类会继承两份相同的基类成员,这就造成了冗余数据和二义性问题。

    • 菱形继承示例

class A {
public:void show() { std::cout << "A's show" << std::endl; }
};class B : public A {};
class C : public A {};class D : public B, public C {};  // D 继承了两份 A 类的成员
  • 虚继承的解决方案:使用虚继承解决菱形继承问题,确保派生类只继承一份基类的成员。

  • 虚继承示例

class A {
public:void show() { std::cout << "A's show" << std::endl; }
};class B : virtual public A {};    // 虚继承
class C : virtual public A {};    // 虚继承class D : public B, public C {};  // D 只继承一份 A 的成员

 解释:通过 virtual 关键字,B 和 C 虚继承自 A,D 类中只有一份 A 类的成员,避免了二义性和数据冗余问题。

std::vectorstd::list 的区别

  • 底层实现

    • std::vector:基于动态数组实现,元素在内存中是连续存储的。
    • std::list:基于双向链表实现,元素在内存中是非连续存储的。
  • 时间复杂度

    • 访问vector 支持常数时间的随机访问(O(1)),因为其元素是连续存储的;而 list 访问任意位置的元素都需要线性时间(O(n))。
    • 插入/删除list 在任何位置插入或删除元素的时间复杂度为常数时间(O(1)),而 vector 在尾部插入/删除元素的时间复杂度为 O(1),但在中间插入或删除元素时需要移动数据,时间复杂度为 O(n)
  • 适用场景

    • std::vector:适用于需要频繁随机访问元素,或在末尾插入、删除元素的场景。
    • std::list:适用于需要频繁在中间插入或删除元素的场景,但不需要随机访问。

std::mapstd::unordered_map 的区别

  • 底层实现

    • std::map:基于红黑树(自平衡二叉搜索树)实现,键值对按键的顺序存储。
    • std::unordered_map:基于哈希表实现,键值对无序存储。
  • 时间复杂度

    • std::map:插入、删除、查找的平均时间复杂度为 O(log n)
    • std::unordered_map:插入、删除、查找的平均时间复杂度为 O(1),最坏情况下为 O(n)
  • 适用场景

    • std::map:适用于需要按键排序的场景。
    • std::unordered_map:适用于对性能要求较高、且不需要排序的场景。

何时使用 std::deque 而不是 std::vectorstd::list

  • std::deque:双端队列(double-ended queue),在两端插入和删除元素的时间复杂度为 O(1),并支持常数时间的随机访问。

  • 使用场景

    • 当需要在容器的两端频繁进行插入和删除操作,同时还需要随机访问时,使用 std::deque 是最好的选择。

    • std::vector 不同,deque 支持在头部进行高效的插入和删除操作。

    • std::list 不同,deque 支持随机访问。

 

RAII(Resource Acquisition Is Initialization,资源获取即初始化)是一种重要的 C++ 编程习惯,确保资源的正确管理。RAII 的核心思想是:将资源的生命周期绑定到对象的生命周期。当对象被创建时获取资源,当对象被销毁时释放资源。

  • 优点

    1. 自动管理资源:资源的分配和释放与对象的生命周期绑定,避免资源泄漏(如内存泄漏、文件句柄泄漏)。
    2. 异常安全:如果在资源使用过程中发生异常,RAII 确保在栈展开时资源会被正确释放。
    3. 简化代码:RAII 自动处理资源的释放,减少手动管理资源的复杂性。
  • 常见场景

    1. 内存管理:智能指针(如 std::unique_ptrstd::shared_ptr)通过 RAII 管理动态分配的内存。
    2. 文件管理std::fstream 等标准库类通过 RAII 确保文件在对象销毁时自动关闭。
  • 示例

class FileHandler {
private:FILE* file;
public:FileHandler(const char* filename) {file = fopen(filename, "r");if (!file) {throw std::runtime_error("File open failed");}}~FileHandler() {if (file) {fclose(file);  // 确保文件关闭}}
};

 


http://www.mrgr.cn/news/29504.html

相关文章:

  • Visual Studio 2022 安装
  • 单元测试、集成测试、系统测试、验收测试、压力测试、性能测试、安全性测试、兼容性测试、回归测试(超详细的分类介绍及教学)
  • 将大型语言模型(如GPT-4)微调用于文本续写任务
  • 去地面算法——depth_clustering算法调试(1)
  • 工作和学习遇到的技术问题
  • 标准C++ 字符串
  • C++:日期类的实现
  • YOLOv8改进:SA注意力机制【注意力系列篇】(附详细的修改步骤,以及代码,与其他一些注意力机制相比,不仅准确度更高,而且模型更加轻量化。)
  • 隐藏excel单元格数据的两个方法
  • 【d2l安装超详细老妈子教程】
  • 史上最强异步编程~CompletableFuture精读
  • hym8563/pcf8563 定时关机后,中断没有清除,导致INT脚一直为低,系统不开机。
  • GEE教程:1950-2023年ECMWF数据中积雪的长时序统计分析
  • 什么是泛在智能?应用在哪些场景?
  • PMP出成绩非常慢?PDU如何获取?
  • 为什么收录是谷歌seo的底子?
  • 在 Docker 中部署无头 Chrome:在 Browserless 中运行
  • ISO 26262功能安全标准与汽车软件代码质量的关系探讨
  • 点工干了三年,快要废了。。。
  • Mastering openFrameworks_第十一章_网络
  • 健身房预约小程序定制搭建,数字化运营管理
  • 【C++登堂入室】类和对象(下)
  • LIMS实现自动化工作流程的关键功能
  • Linux标准IO-系统调用详解
  • 026.爬虫专用浏览器-绕过常见无头检测(二)
  • 如何查看电脑端的微信聊天记录?今天告诉你!五步掌握微信聊天记录查看与管理技巧!【企业必看】