C++面试题库

C++常规面试题库

  • 1. C++11新特性
    • 1.1 类型推导
    • 1.2 左值-右值
    • 1.3 列表初始化
    • 1.4 lambda表达式 && std::function && std::bind
    • 1.5 范围for
    • 1.6 智能指针
    • 1.7 final && override
    • 1.8 线程与锁
  • 2. 多线程读写锁与普通锁的区别
  • 3. C++多态机制
  • 4. vector避免内存重新分配
  • 5. 指针与引用的区别
  • 6. 虚函数
  • 7. 进程和线程
  • 8. STL容器
  • 9. C和C++的区别
  • 10. 程序内存分配方式
  • 11. 如何防止内存泄露
  • 12. vetcor的实现方式
  • 13. 虚函数概念
  • 14. inline与宏的区别
  • 15. 单例模式
  • 16. 静态成员变量和函数
  • 17. C++类型转换
  • 18. new/delete和malloc/free的区别
  • 19. vector和list区别
  • 20. 虚函数和纯虚函数的区别
  • 21. 虚函数和重载的区别

1. C++11新特性

1.1 类型推导

  • auto:用于推导变量类型;
  • decltype:用于推导表达式类型;
// 两者均在编译器执行类型推导
auto a = 1;
const int& b = 2;
decltype(b) c = 3;

1.2 左值-右值

  • 左值:可以取地址,放在等式左边的;
  • 右值:不能取地址,不能放在等号左边的;
  • 左值引用:对左值进行引用类型
  • 右值引用:对右值进行引用类型
  • 移动语义:主要用于转移变量的资源所有权,将原始的资源归为己用,原来的变量被销毁
  • 完美转发:std::forward,目标函数和转发函数类型一致
int a = 1; // a是左值
int b = a + 1; // b+1的返回值以及1字符型常量,均为右值
int& c = a; // 左值引用
int&& d = std::move(a); // 通过std:move将左值a转换为右值,从而使用右值引用
std::forward<int&>(a); // 完美转发,a是左值引用类型

1.3 列表初始化

直接对变量使用列表初始化值,包括基本数据类型、数组、STL

float a{10.2};
int b[2] = {1, 2};
std::vector<int> c{1, 3 ,6};

1.4 lambda表达式 && std::function && std::bind

  • lanbda表达式:匿名函数
    auto func = [capture] (params) -> ret { func_body; };
  • std::function:函数对象
  • std::bind:绑定可调用对象和参数
// 1.lamdba表达式,
// []不捕获任何变量;
// [&]引用捕获,捕获外部作用域所有变量,在函数体内当作引用使用;
// [=]值捕获,捕获外部作用域所有变量,在函数内内有个副本使用
// [a]只值捕获a变量,不捕获其它变量
// [this]捕获当前类中的this指针
vector<int> nums{2, 6, 3, 8};
auto sort_nums = [](int a, int b) -> bool {return a > b;}
std::sort(nums.begin(), nums.end(), sort_nums);

// 2.function函数
void print_nums(int a) {
	std::cout << "a: " << a << std::endl;
}

std::function<void(int)> fun = print_nums; // function调用函数对象
fun(12);

// 3.std::bind函数
double callableFunc (double x, double y) {return x/y;}
auto NewCallable = std::bind (callableFunc, std::placeholders::_1,2);  
std::cout << NewCallable (10) << '\n';  // 相当于调用NewCallable(10, 2),因为其中2被绑定

1.5 范围for

  • 可遍历数组、容器、字符串以及可迭代对象
for (const auto& ele : elems) {
	std::cout << "ele: " << ele << std::endl;
}

1.6 智能指针

  • shared_ptr:共享式指针,多个指针可同时共享对同一对象的拥有权;
  • unique_ptr:独占式指针,管理唯一对象,仅有一个指针可访问该对象;
  • weak_ptr:弱引用指针,可解决shared_ptr的循环引用问题;
std::shared_ptr<int> a_ptr = std::make_shared<int>(2);
std::weak_ptr<int> b_ptr = a_ptr;
std::unique_ptr<int> c_ptr = std::make_unqiue<int>(3);

1.7 final && override

  • final:修饰类的关键字,表示禁止该类派生和虚函数的重写;
  • override:修饰派生类成员函数,表名基类对应的函数被重写;
class base {
public:
	base() = default;
	virtual void function() {
	}
};

class derived : base {
public:
	dervied() = default;
	void function() override {
		std::cout << "override" << std::endl;
	}
};

1.8 线程与锁

  • std::thread:可直接定义创建子线程,可使用join和detch;
  • std::mutex:用于线程同步,配合std::lock_guard或std::unique_lock使用;
  • std::condition_variable:条件变量,配合std::unique_lock使用,可阻塞线程;
  • std::atomic:原子操作,可以不需要std::mutex保护;

互斥锁:用于多线程编程的同步机制,保证同一时刻只能有一个线程访问共享资源。

条件变量:用于多线程编程的同步机制,可以让一个线程在某些条件不满足时等待,并在条件满足时被唤醒;通常与互斥锁一起使用,以保证唤醒等待的线程能获得锁。

互斥锁用于保护共享资源;条件变量用于多线程协作运行。

void run(int nums) {
	nums++;
}
std::thread thread_process = std::thread(run, 10);
std::mutex lock_mutex;
std::lock_guard<std::mutex>(lock_mutex);
std::atomic<bool> process_single;

2. 多线程读写锁与普通锁的区别

普通锁仅保证多线程访问共享数据时互斥,同一时刻只能有一个线程访问共享数据;
读写锁允许多个线程同时读取共享数据,但有线程对数据写入时,其他线程不能对数据执行读写操作

3. C++多态机制

C++多态机制是通过虚函数实现,允许程序运行时根据对象类型动态调用函数。

类中声明函数为虚函数,则C++会在类中增加一个虚函数表,该表中存储所有的虚函数地址,每个对象都有一个指向虚函数表的指针。

多态机制可增加代码的灵活性和可扩展性,减少代码的冗余性

4. vector避免内存重新分配

vector可通过reverse预留足够多的内存空间,从而避免内存重新分配。

5. 指针与引用的区别

  • 指针:存储变量的地址,指向内存单元;
  • 引用:引用只是一个别名;

两者区别:可以有const指针,但没有const引用;指针可以为空,但引用必须被初始化;指针可以有多级,但引用只有一级。

6. 虚函数

构造函数不能是虚函数,因为构造函数是用来产生对象的,但虚函数是基于对象;
基类的析构函数可以是虚函数,可保证通过基类指针删除派生类对象时的正确资源回收,避免内存泄漏。

7. 进程和线程

进程是计算机中的一个独立的执行单元,它是操作系统进行资源分配和调度的基本单位;线程是进程的一个执行流,是 CPU 调度和分配的基本单位。

8. STL容器

STL容器分为两大类:序列式容器和关联式容器
序列式容器:vector(动态数组),deque(双端队列),list(双向链表),array(固定长度数组);
关联式容器:map(键值对升序集合),set(不重复元素升序集合),unordered_map(键值对无序集合),unordered_set(不重复元素无序集合),multiset(可重复元素升序集合),multimap(可重复键值对升序集合);

9. C和C++的区别

  1. C++是面向对象的语言,而C是面向过程的结构化编程语言;
  2. C++具有封装、多态和继承三大特性;
  3. 相比于C,C++增加类型安全功能,如:强制类型转换、智能指针;
  4. C++支持泛型编程,比如:模板类、函数模板等;

封装: 允许通过类隐藏实现细节,提供公共接口供其他函数使用,其数据封装和访问控制有助于防止不正确的访问。
多态: 包括静态多态(函数重载和操作符重载)和动态多态(虚函数运行时绑定);
继承: 允许一个类从另一个类继承数据成员和成员函数,并且可以扩展或修改其行为,提供了代码复用和层次化的结构。

10. 程序内存分配方式

  1. 栈区(stack):编译器在需要时分配,不需要时自动清除的存储区,通常是局部变量、函数参数等;
  2. 堆区(heap):由程序员分配、释放,程序结束时可能由系统回收,通常是由 new、malloc分配内存,由delete、free释放内存;
  3. 全局区(静态区):全局变量和静态变量被分配到同一块内存,程序结束后由系统释放;
  4. 常量存储区:存储常量字符串、字面值常量,不允许修改,由系统释放;
  5. 程序代码区:存放函数体的二进制代码。

11. 如何防止内存泄露

内存泄漏:程序运行过程中,由于某种原因导致程序无法释放已经不再使用的内存,导致系统内存资源浪费。

防止内存泄漏:C++11提供智能指针,可以有效确保不使用的对象可释放(shared_ptr的引用计数为0即释放资源);针对new的对象,不使用时需要使用delete释放。

12. vetcor的实现方式

vector是动态数组,属于STL(标准模板库) 的一种模板类型,内部通过动态数组存储元素,可在运行时动态改变底层数组容量,增加的元素超过当前最大容量,可自动分配内存(分配方式是在原来基础之上扩充两倍),一般提前预留容量(reserve)可避免二次内存分配,提高效率。

13. 虚函数概念

虚函数是指在另一个类中希望重写的成员函数,使用基类指针指向派生类对象时,调用虚函数时,实际调用的是派生类的函数。

类中一旦存在虚函数,变量便会多出一个虚函数表指针,空间会增大4字节(32位4字节,64位8字节)。

虚函数表数量:是一个类中虚函数的数量;

14. inline与宏的区别

  • inline内联函数:编译时展开,直接嵌入到目标代码,可以进行类型检查、语句是否正确等;
  • 宏:预编译时展开,简单的文本替换,无类型检查,存在一定的局限性和隐患。

15. 单例模式

单例模式(Singleton Pattern),设计模式之一,保证一个类仅有一个实例,并提供全局访问入口函数,该实例被所有程序模块共享。

16. 静态成员变量和函数

  • 静态成员变量:存储在全局区,必须要初始化(类外初始化,初始化时不带static);
  • 静态成员函数:声明和实现分离时,实现不带static,静态成员函数内部不能访问非静态成员变量,且不能为虚函数;
class base {
	static int base_variable;
};
int base::base_variable = 10;

17. C++类型转换

  • static_cast:隐式转换。常用于基本数据类型的强制类型转换;
  • dynamic_cast:基类指向派生类的转换;
  • const_cast:用于去掉变量的const;
  • reinterpret_cast:不安全的底层类型转换,可以将指针或者引用转换为其他类型的指针和引用;

18. new/delete和malloc/free的区别

  • new/delete:属于C++的操作符,其底层是malloc和free实现;new自动分配内存,无需指定大小;
  • malloc/free:属于标准库函数,由头文件stdlib.h引入;malloc需要自行指定动态分配内存的大小

19. vector和list区别

  • vector底层实现是数组;list是双向链表;
  • vector支持随机访问;list不支持;
  • vector是按顺序分配内存;list不是;
  • vector一次性分配内存,不够时才进行2倍扩充;list每次插入节点都会进行内存扩充;
  • vector插入删除性能差;list插入删除性能好;

20. 虚函数和纯虚函数的区别

  • 纯虚函数:类中只要存在纯虚函数,就是抽象类,抽象类无法实例化对象;且子类必须重写纯虚函数,否则子类也是纯虚函数;
  • 虚函数:基类的函数前加virtual,子类可重写该函数;
class base {
public:
	base() = default;
	virtual void function() = 0;
};

class base_a : public base {
public:
	base_a() = default;
	void function() override {
		std::cout << "function" << std::endl;
	}
};

21. 虚函数和重载的区别

  • 虚函数:虚函数是程序运行时根据对象的具体类型调用不同的函数,其在派生类中可被覆盖重写;
  • 重载:同一命名域下,同一函数名存在不同的形参列表(包括类型和数量),在编译时匹配;

虚函数用于实现多态;重载用于提供函数的不同形式。

你可能感兴趣的:(SLAM面试题库,c++,面试,开发语言)