获课:weiranit.fun/6100/
获取ZY↑↑方打开链接↑↑
以下是一些C++ 大厂面试真题12:
基础语法
-
C++ 与 C 的区别
-
C 是面向过程的语言,C++ 是面向对象的语言,C++ 还支持泛型编程和函数式编程等特性。
-
C++ 支持动态内存管理
new/delete
,C 使用malloc/free
。 -
C++ 支持函数重载、引用等特性,C 不支持。
-
指针与引用的区别
-
指针是一个变量,存储的是另一个变量的地址,可以通过
*
操作符来访问指针所指向的变量,指针可以为空,也可以重新赋值指向其他变量。 -
引用是一个变量的别名,在定义时必须初始化,并且一旦初始化后就不能再引用其他变量,对引用的操作实际上就是对被引用变量的操作。
-
const
关键字的作用
-
定义常量,使变量的值不可修改。
-
修饰函数参数,表明函数不会修改传入的参数值。
-
修饰函数的返回值,限制返回值不能被修改。
-
修饰类的成员函数,表明该成员函数不会修改对象的成员变量。
面向对象编程
-
多态的实现方式
-
函数重载:在同一个类中,允许存在多个同名函数,但函数的参数列表不同(参数个数、参数类型或参数顺序不同)。编译器会根据调用函数时传入的参数来决定调用哪个函数,这是一种静态多态,在编译时就确定了调用的函数。
-
虚函数:在基类中使用
virtual
关键字声明的函数,子类可以重写该函数。当通过基类指针或引用调用虚函数时,会根据对象的实际类型来决定调用哪个类的虚函数,这是一种动态多态,在运行时才确定调用的函数。
-
抽象类与接口类的区别
-
抽象类可以包含普通成员变量和成员函数,也可以包含纯虚函数,抽象类不能被实例化,只能作为基类被继承。
-
接口类通常只包含纯虚函数和静态常量,没有普通成员变量和非纯虚函数,接口类的目的是为了定义一组相关的操作,让子类去实现这些操作。
-
构造函数和析构函数的作用及调用时机
-
构造函数用于初始化对象的成员变量,在对象创建时自动调用,可以有多个构造函数,实现构造函数的重载。
-
析构函数用于释放对象占用的资源,在对象销毁时自动调用,每个类只能有一个析构函数。
模板与 STL
-
模板的概念及使用场景
-
模板是 C++ 中实现泛型编程的一种机制,它允许程序员编写与类型无关的代码,提高了代码的复用性。
-
常用于容器类(如
vector
、list
、map
等)、算法库(如sort
、find
等)的实现,以及编写通用的函数和类,适用于需要处理多种数据类型,但逻辑相同的场景。
-
map
和unordered_map
的区别
-
map
内部是基于红黑树实现的,元素是有序的,按照键的大小进行排序,查找、插入和删除操作的时间复杂度为。 -
unordered_map
内部是基于哈希表实现的,元素是无序的,查找、插入和删除操作的平均时间复杂度为,但在最坏情况下可能退化。
数据结构与算法
-
如何在 C++ 中实现一个栈
-
可以使用数组或链表来实现栈。使用数组实现时,需要定义一个数组和一个栈顶指针,通过栈顶指针来操作栈的元素。使用链表实现时,需要定义一个链表节点结构体,通过链表的头指针来操作栈的元素,入栈操作在链表头部插入节点,出栈操作删除链表头部节点。
-
描述二叉树的遍历方式及其实现
-
前序遍历:先访问根节点,再递归地前序遍历左子树,最后递归地前序遍历右子树。
-
中序遍历:先递归地中序遍历左子树,再访问根节点,最后递归地中序遍历右子树。
-
后序遍历:先递归地后序遍历左子树,再递归地后序遍历右子树,最后访问根节点。
并发编程
-
C++11 引入了哪些多线程特性
-
std::thread
类:用于创建和管理线程,方便地启动一个新线程来执行指定的函数。 -
std::mutex
类:互斥锁,用于保护共享资源,确保在同一时刻只有一个线程能够访问共享资源,防止数据竞争和并发错误。 -
std::condition_variable
类:条件变量,用于线程间的同步,允许线程在满足特定条件时等待或唤醒其他线程。 -
原子操作:
std::atomic
模板类提供了对原子类型的支持,用于实现无锁编程,保证对共享变量的原子访问,避免使用锁带来的性能开销。
-
如何避免死锁
-
资源有序分配:对资源进行编号,规定线程只能按照从小到大的顺序获取资源,避免循环等待资源的情况。
-
避免资源独占:尽量减少对资源的独占时间,在使用完资源后及时释放,以便其他线程能够获取资源。
-
资源预分配:在程序开始时,一次性分配所有需要的资源,避免在运行过程中动态分配资源导致的死锁。
-
以下是继续为你补充的 C++ 大厂面试真题:
基础语法
-
new
和malloc
的区别
-
new
是 C++ 中的操作符,malloc
是 C 语言中的函数。 -
new
在申请内存时会调用构造函数来初始化对象,malloc
只是分配内存空间,不会进行初始化。 -
new
返回的是对象类型的指针,malloc
返回的是void*
类型的指针,需要进行类型转换。 -
new
可以进行重载,malloc
不能。
-
static
关键字的作用
-
修饰全局变量时,使全局变量的作用域仅限于当前文件,其他文件无法访问。
-
修饰局部变量时,局部变量在程序运行期间只初始化一次,下次进入函数时,会保留上一次的值。
-
修饰类的成员变量时,该成员变量为类的所有对象所共享,不属于任何一个具体的对象。
-
修饰类的成员函数时,该成员函数只能访问类的静态成员变量,不能访问非静态成员变量。
面向对象编程
-
多重继承和虚继承的区别
-
多重继承是一个类可以从多个基类继承,可能会导致菱形继承问题,即一个派生类从两个或多个基类继承,而这些基类又从同一个基类继承,会导致数据冗余和二义性。
-
虚继承是为了解决多重继承中的菱形继承问题,在继承时使用
virtual
关键字,使得在派生类中只保留一份共同基类的成员,避免数据冗余和二义性。
-
什么是纯虚函数和抽象类
-
纯虚函数是在基类中声明的虚函数,它在基类中没有实现,只是给出了函数的原型,在派生类中必须实现。
-
包含纯虚函数的类称为抽象类,抽象类不能被实例化,只能作为基类被继承,用于定义一组相关的操作,让子类去实现这些操作。
模板与 STL
-
实现一个简单的模板函数
收起
cpp
template <typename T>T add(T a, T b) { return a + b;}
-
vector
和list
的区别
-
vector
基于动态数组实现,支持随机访问,访问元素的时间复杂度为,在尾部插入和删除元素的时间复杂度为,在中间插入和删除元素的时间复杂度为。 -
list
基于双向链表实现,不支持随机访问,访问元素的时间复杂度为,在任何位置插入和删除元素的时间复杂度为。
数据结构与算法
-
实现一个二叉搜索树的插入操作
收起
cpp
// 二叉树节点结构体struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(NULL), right(NULL) {}};// 插入操作TreeNode* insertIntoBST(TreeNode* root, int val) { if (root == NULL) { return new TreeNode(val); } if (val < root->val) { root->left = insertIntoBST(root->left, val); } else { root->right = insertIntoBST(root->right, val); } return root;}
-
写一个函数实现字符串的反转
收起
cpp
#include <iostream>#include <cstring>void reverseString(char* str) { int len = strlen(str); for (int i = 0; i < len / 2; i++) { char temp = str[i]; str[i] = str[len - i - 1]; str[len - i - 1] = temp; }}int main() { char str[] = "hello world"; reverseString(str); std::cout << str << std::endl; return 0;}
并发编程
-
std::unique_lock
和std::lock_guard
的区别
-
std::lock_guard
是一个简单的 RAII(Resource Acquisition Is Initialization)锁包装器,在构造函数中获取锁,在析构函数中释放锁,不能手动释放锁,也不能在获取锁后进行其他操作。 -
std::unique_lock
比std::lock_guard
更灵活,它可以在构造函数中不获取锁,而是在需要的时候手动获取锁,也可以手动释放锁,还可以进行锁的转移等操作。
-
什么是条件变量,如何使用
-
条件变量是 C++ 中用于线程间同步的机制,它允许线程在满足特定条件时等待或唤醒其他线程。
-
使用时,通常需要与互斥锁配合使用,一个线程在获取互斥锁后,检查条件是否满足,如果不满足,则调用条件变量的
wait
函数等待,此时线程会释放互斥锁并进入等待状态,直到其他线程满足条件后调用条件变量的notify_one
或notify_all
函数唤醒等待的线程,等待的线程被唤醒后会重新获取互斥锁,然后继续执行。