C++ 面试题常用总结 详解(满足c++ 岗位必备,不定时更新)

📚 本文主要总结了一些常见的C++面试题,主要涉及到语法基础、STL标准库、内存相关、类相关和其他辅助技能,掌握这些内容,基本上就满足C++的岗位技能(红色标记为重点内容),欢迎大家前来学习指正,会不定期去更新面试内容。

 Hi~!欢迎来到碧波空间,平时喜欢用博客记录学习的点滴,欢迎大家前来指正,欢迎欢迎~~
✨✨ 主页:碧波
📚 📚 专栏:C++ 系列文章

目录

一、C ++ 语法基础

🔥 谈谈变量的使用和生命周期,声明和初始化

🔥 谈谈C++的命名空间的作用

🔥  include " " 和 <> 的区别

🔥 指针是什么?

🔥 什么是指针数组和数组指针

🔥 引用是什么?

🔥 指针和引用的区别

🔥 什么是函数指针和指针函数以及区别

🔥 什么是常量指针和指针常量以及区别

🔥 智能指针的本质是什么以及实现原理

🔥 weak_ptr 是否有计数方式,在那分配空间?

🔥 类型强制转换有哪几种?

🔥 函数参数传递时,指针、引用以及值传递有什么区别?

🔥 进程间的通信方式有那些?

🔥 线程间的通信方式有那些?

🔥 简单谈谈对线程的理解,线程间共享资源时该怎么处理?

🔥 常用关键字含义及其使用

🔥 const 可以修饰那些内容

🔥 class 和 struct 的区别?有了class 为啥仍还保留 struct?

二、标准库

🔥 什么是STL?

🔥 数组和vector的区别?

🔥 list 和 vector 的主要区别是什么?

🔥 在什么情况下使用 list 而不是 vector?

🔥 set 的底层数据结构是什么?

🔥 set 和 multiset 有什么不同?

🔥 map 和 unordered_map 的主要区别是什么?

三、内存

🔥 RAII是什么?

🔥  使用RAII的原因以及使用方法?

🔥 new 和 malloc 的区别?delete 和 free 的区别?

🔥 C++的内存框架分布情况

🔥 堆和栈的区别?

🔥 什么是内存对齐,内存对齐有什么好处?

🔥 什么是内存泄漏?写代码时应该怎么防止内存泄漏。

🔥 说说什么是深拷贝和浅拷贝?

四、类相关

🔥 讲讲C++的三大特性(封装、继承、多态)

🔥 多态是怎么实现的? 

🔥 什么是虚函数和纯虚函数?以及区别?

🔥 虚函数是怎么实现的?虚函数在那个阶段生成?

🔥 构造函数和析构函数的作用?

🔥 构造函数和析构函数那个可以被声明为虚函数,为什么?

🔥 构造函数和析构函数那个可以被重载,为什么?

🔥 谈谈什么是重载、重写、隐藏?

🔥 this 指针,为什么会存在this指针?

五、其他必备

🔥 什么是git? 以及git常用命令

🔥 什么是svn? git 和 svn的区别

🔥 Linux 系统下常用的命令会那些,举例?


一、C ++ 语法基础

🔥 谈谈变量的使用和生命周期,声明和初始化

全局变量:在程序的任何地方都能访问到的变量,它们通常在整个程序执行期间都是有效的。

局部变量:定义在函数或者代码块内部的变量,它们只能在声明它们的函数或代码块内部访问

静态变量:一般是在变量前加static 进行声明。

如下表格是各变量之前的差异点,熟练掌握每种变量的属性,就可以正确使用变量。

各变量间的区别全局变量局部变量静态全局静态局部
声明方式在函数外部声明,通常在文件的顶部或者在函数定义之外在函数或代码块的开始部分声明在变量声明时加上 static 关键字在变量声明时加上 static 关键字
代码示例

// 全局变量声明并初始化

int globalVar = 10;

void func() {

// 局部变量声明并初始化

int localVar = 20;

}

// 静态全局变量声明并初始化

static int globalVar = 10;

void func() {

// 静态局部变量声明并初始化

static int localVar = 20;

}

初始化在声明时初始化建议在声明时初始化,避免未知错误在声明时或者在第一次使用之前初始化在声明时或者在第一次使用之前初始化
内存存储位置存储在常量区(静态存储区)存储在栈区存储在常量区(静态存储区)存储在常量区(静态存储区)
作用域整个文件都可访问函数内部整个文件都可访问函数内部
生命周期全局生命周期,会一直存在到程序结束函数被调用期间全局生命周期具有全局生命周期,但作用域仍然受限于其声明的函数或代码块,保持其值在函数调用之间持久化

🔥 谈谈C++的命名空间的作用

命名空间

避免命名冲突: 可以帮助避免不同部分的代码中出现相同的名称,从而防止命名冲突。
组织代码: 可以将相关的函数、类等封装在一起,提高代码的组织性和可读性。
模块化开发: 可以将代码划分为不同的模块,使得代码更易于维护和扩展。

命名空间使用

namespace MyNamespace {int x;void func();
}

标准命名空间

// 通常需要加上 std:: 前缀,std::cout << "Hello, world!" << endl;// 或者使用 using namespace std;using namespace std;
cout << "Hello, world!" << endl;

🔥  include " " 和 <> 的区别

#include <文件名> 是包含标准库头文件的方式,编译器会按照标准路径顺序搜索。

#include "文件名" 是包含用户自定义或者项目内部头文件的方式,优先在当前目录查找,然后才是按照标准路径顺序搜索。

🔥 指针是什么?

指针是一个 用来存储变量地址 的特殊数据类型。简单来说,指针变量存储的是内存地址,而不是常规的值。通过指针,我们可以直接访问和操作内存中的数据,而不必知道实际存储的值是什么。

可以使用 解引用操作符 * 来访问指针所指向的变量,使用地址运算符 & 来获取变量的地址

🔥 什么是指针数组和数组指针

指针数组是一个数组,其中的每个元素都是指针。这些指针可以指向不同的内存地址,通常用于存储一组相同类型的指针。

// ptrArray 是一个包含 5 个元素的数组,
// 每个元素都是 int* 类型的指针,可以分别指向不同的整数 int *ptrArray[5];

数组指针是一个指针,它指向数组的首地址。它本身是一个指针,但指向的内容是一个数组对象。

// arrPtr 是一个指针,指向一个包含 5 个整数的数组
int (*arrPtr)[5];

指针数组常用于需要动态管理一组指针的场景,而数组指针则用于处理数组的整体,特别是在函数参数传递和多维数组的处理中比较常见。

🔥 引用是什么?

引用提供了一个变量的别名。它使用 & 符号来定义。

// ref 是 num 的引用,即 ref 和 num 引用同一个内存位置的整数值int num = 10;
int &ref = num;  // ref 是 num 的引用
  • 引用必须在定义时初始化,并且一旦初始化后,它就不能再绑定到其他变量。

应用场景:

引用可以用于函数参数,允许在函数内部直接修改传递的变量,而不是复制一份值。

🔥 指针和引用的区别

引用不能指向空值(null),而指针可以。

引用在使用时不需要解引用操作(不需要 * 符号),而指针需要。

引用在定义时必须初始化,而指针可以在后续指向不同的对象。

🔥 什么是函数指针和指针函数以及区别

函数指针 是指 指向一个函数的指针变量。它可以指向一个特定类型和签名(参数类型和返回类型)的函数。函数指针的声明形式类似于指向其他类型的指针,但其类型是指向函数的指针类型。

// 声明一个函数指针类型
typedef void (*FuncPtr)(int);  // FuncPtr 是一个指向返回类型为 void,参数为 int 的函数指针类型// 定义一个函数
void myFunction(int x) {// 函数体
}int main() {// 声明一个函数指针变量并初始化FuncPtr ptr = &myFunction;// 通过函数指针调用函数ptr(10);  // 相当于调用 myFunction(10);return 0;
}

指针函数 指的是 返回类型 为 指向函数的指针 的函数。换句话说,指针函数是一个返回类型为函数指针的函数。

// 声明一个指针函数
int (*funcPtr)(int, int);  // funcPtr 是一个函数,返回类型为 int*,参数为两个 int// 定义一个函数
int add(int a, int b) {return a + b;
}// 另一个函数,返回一个函数指针
int (*getAddFunctionPointer())(int, int) {return &add;
}int main() {// 获取 add 函数的函数指针funcPtr = getAddFunctionPointer();// 通过函数指针调用函数int result = funcPtr(3, 4);  // 相当于调用 add(3, 4),result 等于 7return 0;
}

区别

函数指针是指指向函数的指针变量,而指针函数是一个返回类型为函数指针的函数。

函数指针在声明时需要指定其指向的函数的签名(参数类型和返回类型),而指针函数的返回类型是一个函数指针类型。

函数指针直接指向一个已存在的函数,可以通过该指针调用该函数;而指针函数返回一个函数指针,需要通过该函数指针再调用相应的函数。

🔥 什么是常量指针和指针常量以及区别

常量指针: 是指一旦指向了某个对象,就无法再指向其他对象的指针。

int x = 10;
int* const ptr = &x;  // ptr 是一个常量指针,指向 int 类型的对象// 无法再修改 ptr 指向的对象,但可以修改对象本身的值
*ptr = 20;  // 合法,修改了 x 的值为 20// 以下操作不合法,因为 ptr 是常量指针,不能改变指向
// ptr = &y;  // 错误,无法改变 ptr 的指向

指针常量:是指指向常量对象的指针,一旦指向了某个对象,不能通过该指针修改所指向对象的值。

int x = 10;
const int* ptr = &x;  // ptr 是一个指向常量 int 的指针// 以下操作合法,可以修改 ptr 所指向对象的值
x = 20;  // 修改了 x 的值为 20// 以下操作不合法,因为 ptr 所指向的对象是常量,不能修改其值
// *ptr = 30;  // 错误,不能通过 ptr 修改其指向的对象的值// 以下操作合法,因为 ptr 本身不是常量,可以改变其指向
int y = 50;
ptr = &y;  // 合法,修改了 ptr 的指向为变量 y// 另一种写法 * 解引用,也是取值。int const * p;

区别

常量指针 强调指针本身是常量,指向对象的值可以改变;

指针常量 强调指针所指向的对象是常量,指针的指向可以改变

简单记忆:

距const 修饰右边最近的值是常量 ,不能被修改。

🔥 智能指针的本质是什么以及实现原理

智能指针是一种用于管理动态分配内存和自动释放资源的工具,其本质是利用了 RAII(资源获取即初始化)的设计模式。即在对象初始化的时候获取资源(比如动态分配的内存),在对象析构的时候自动释放资源

它提供了一个封装了指针的对象,通过其析构函数来确保在对象生命周期结束时,所管理的资源能够被正确释放,避免内存泄漏和资源泄漏。

实现原理

智能指针实现是引入计数引用机制。智能指针对象本身持有一个指向动态分配资源的指针,并维护一个引用计数。每当有一个新的智能指针指向同一块内存时,引用计数增加;当智能指针超出作用域或被显式销毁时,引用计数减少。当引用计数为零时,智能指针负责释放其管理的资源。

常见的智能指针类型:

std::shared_ptr:允许多个指针共享同一块资源,通过引用计数来管理资源的生命周期,适用于多个所有者的情况。

std::unique_ptr:独占所指向的对象,保证在任何时刻只有一个指针可以指向该对象,移动语义保证资源的所有权可以传递但不共享。

std::weak_ptr:用于协助 std::shared_ptr,不会增加引用计数,避免循环引用问题,通常用于解决 shared_ptr 的环状引用问题。

智能指针通过结合 RAII 设计模式引用计数技术,提供了一种高效、安全、方便的动态内存管理机制,是现代 C++ 开发中推荐使用的重要工具之一。

🔥 weak_ptr 是否有计数方式,在那分配空间?

std::weak_ptr 是一种观测型智能指针,用于解决 std::shared_ptr 的循环引用问题,它本身并不进行内存空间分配或引用计数,而是依赖于 std::shared_ptr 来管理资源的生命周期。

std::weak_ptr 可以通过 std::lock() 成员函数获取一个 std::shared_ptr。

调用 std::lock() 成员函数可以获取一个 std::shared_ptr 对象,该对象指向 std::weak_ptr 所观测的对象(如果它仍然存在)。

如果 std::weak_ptr 过期(即其管理的对象已经被释放),std::lock() 返回一个空的 std::shared_ptr

#include <iostream>
#include <memory>int main() {std::shared_ptr<int> shared = std::make_shared<int>(42);std::weak_ptr<int> weak = shared;// 使用 std::lock() 获取 std::shared_ptrstd::shared_ptr<int> locked = weak.lock();if (locked) {// 如果成功获取到 std::shared_ptr,则可以安全地使用它std::cout << "Value pointed by shared_ptr: " << *locked << std::endl;} else {std::cout << "The shared_ptr is no longer available." << std::endl;}// 在此之后,shared_ptr 可能会超出作用域,对象被销毁return 0;
}

🔥 类型强制转换有哪几种?

 有四种类型转换方式,它们分别是:

静态转换(static_cast)

常量转换(const_cast)

动态转换(dynamic_cast)

重新解释转换(reinterpret_cast)

🔥 函数参数传递时,指针、引用以及值传递有什么区别?

参数传递区别

 值传递:复制实参的值给形参,函数内部操作的是副本,不影响原始值。

 引用传递:形参是原始实参的别名,函数内部操作影响原始值。

 指针传递:形参是原始实参的地址,函数内部通过解引用操作影响原始值。

🔥 进程间的通信方式有那些?

各种通信方式的详细介绍,参加 线程进程以及多线程多进程 

管道
命名管道
消息队列
共享内存
信号量
套接字

🔥 线程间的通信方式有那些?

 各种通信方式的详细介绍,参加 线程进程以及多线程多进程 

共享内存

互斥锁

条件变量

🔥 简单谈谈对线程的理解,线程间共享资源时该怎么处理?

  线程的详细介绍,参加 线程进程以及多线程多进程 

线程(Thread)是操作系统调度的基本单位,是进程内的一条执行路径,每个进程至少包含一个线程。

线程间共享资源时,需要注意数据的一致性和并发访问的安全性,避免数据竞争和不可预料的结果。一般会采用 互斥锁 来避免多线程访问导致的资源调用错误。

🔥 常用关键字含义及其使用

static:关键字在不同上下文中有不同含义,主要用于:

 静态变量:属于类或函数的静态存储区域,生命周期延续到程序结束。

 静态函数:属于的静态成员函数,与类的实例无关,可以直接通过类名访问。

const :用来声明常量。

 常量变量:一旦初始化就不能修改的变量。

 常量成员函数:在成员函数声明或定义中的 const 关键字表示该函数不会修改对象的状态。

sizeof: 是一个操作符,用于计算数据类型或变量的字节大小。

sizeof(type):返回类型或变量 type 所占的字节数

例如:sizeof(int) 返回 int 类型的字节数,在大多数系统中是4个字节。

final:用于指示某个类、虚函数或者虚继承在派生时不可被继承或重写。

 类:final class MyClass final { ... };,表示该类不能被继承。

 虚函数:virtual void myFunc() final;,表示该虚函数在子类中不能被重写。

override:是C++11引入的关键字,用于 显式指示函数 是在派生类中覆盖了基类中的虚函数。

在派生类中重写基类的虚函数时,可以使用 override 关键字,以确保正确性和可读性。

virtual: 用于声明虚函数,即在基类中声明的函数,可以在派生类中被重写(override)

在基类中声明虚函数:virtual void foo();

派生类中可以选择重写基类的虚函数:void foo() override;

虚函数支持动态绑定,即在运行时根据对象的实际类型决定调用哪个版本的函数。

volatile: 用来声明一个变量是易变的,可能会被意外修改,通常用于多线程或者硬件相关的编程。

如:volatile int sensorValue;,告诉编译器不要对 sensorValue 进行优化,因为它可能会在程序控制之外被改变。

explicit:用来声明构造函数为显式构造函数,防止隐式类型转换。

 例如:explicit MyClass(int value);,禁止编译器根据上下文进行隐式转换,必须显式调用构造函数。

inline:声明可以用于 函数定义 或者 成员函数 定义,建议编译器将 函数的代码插入到每个调用点处,而不是通过函数调用的方式进行调用。

如:inline int add(int a, int b) { return a + b; }

extern C 的作用

用于告诉编译器按照C语言的方式进行链接,主要用于解决C++代码和C代码混合编译的问题。

define 和 typedef 的区别

define: 用于定义宏,预处理阶段替换源代码中的标识符;

如:#define PI 3.14159

typedef : 用于给数据类型取别名。后续可以直接使用别名进行声明变量。

如:typedef unsigned int UINT;    UINT num;   // 等价于unsigned int num。

🔥 const 可以修饰那些内容

变量、指针和引用、函数、类对象或类指针、函数返回值。

🔥 class 和 struct 的区别?有了class 为啥仍还保留 struct?

class 和 struct 用来定义 结构体,主要区别在于默认的访问权限不同(class 默认私有,struct 默认公有)

它们的区别主要体现在 成员默认的访问权限 继承方式 上:

在 class 中,默认的成员访问权限是 private,在 struct 中,默认的成员访问权限是 public

 当没有显式指定继承方式时,class 默认是 private 继承,而 struct 默认是 public 继承。

虽然 classstruct 在语法上有一些细微差别,但其实它们提供了类似的功能。保留 struct 主要是为了提供语法上的灵活性和向后兼容性。

二、标准库

🔥 什么是STL?

STL 是 C++ 标准模板库的缩写。它是 C++ 标准库的一部分,提供了一组通用的模板类函数,实现了常用的数据结构和算法,以及一些函数对象迭代器概念,使得 C++ 程序员可以更加高效地进行编程。

容器

如 vector(动态数组)、deque(双端队列)、list(双向链表)、set(集合)、map(映射)等,这些容器提供了高效的数据存储和访问方式,各自适用于不同的需求和使用场景。

算法

如排序、搜索、合并等,这些算法独立于任何特定的容器类型,可以利用这些算法直接对容器进行操作。

迭代器

提供了一种统一的方式来遍历容器中的元素。

函数对象

即重载了函数调用操作符 () 的对象。比如比较、排序等。

适配器

用于修改容器的接口或行为,使其能够满足特定的需求。

🔥 数组和vector的区别?

大小:数组的大小在创建时是固定的,而vector的大小是动态可变的。

内存分配:数组的内存分配在栈上(静态数组)或堆上(动态数组),而vector的内存总是动态分配的,通常在堆上。

功能vector提供了许多内置的操作,如动态扩展、插入和删除等,而数组只能直接访问元素,不支持动态调整大小。

性能vector在需要扩展时可能会重新分配内存,而数组的内存分配则不会改变。

🔥 listvector 的主要区别是什么?

存储方式: list 是一个双向链表,而 vector 是一个动态数组。

访问时间: list 不支持常数时间的随机访问,vector 支持常数时间的随机访问。

插入和删除: 在 list 中,在任意位置插入或删除元素。而在 vector 中,不太适合在中间插入或删除元素。

🔥 在什么情况下使用 list 而不是 vector

当你需要在容器的中间频繁插入和删除元素,而不是在末尾插入或删除时,list 更适合。

当你不需要随机访问元素,而是需要频繁进行插入和删除操作时,list 是更好的选择,因为它的这些操作在常数时间内完成。

🔥 set 的底层数据结构是什么?

set 通常使用红黑树或其他自平衡的二叉搜索树实现。它确保所有元素是唯一的,并且保持排序状态,因此插入、删除和查找操作的时间复杂度是对数时间 (O(log n))。

🔥 setmultiset 有什么不同?

set 中的元素是唯一的,不能有重复的元素。

multiset 允许有重复的元素。

🔥 mapunordered_map 的主要区别是什么?

底层数据结构: map 通常使用红黑树或其他自平衡的二叉搜索树实现,而 unordered_map 使用哈希表实现。

访问时间: map 提供对数时间 (O(log n)) 的查找、插入和删除操作,保持元素的排序。unordered_map 提供平均常数时间 (O(1)) 的查找、插入和删除操作,但不保持元素的排序。

排序: map 中的元素是按照键的顺序排序的,而 unordered_map 中的元素没有特定的顺序。

三、内存

🔥 RAII是什么?

RAII 的核心思想是:资源的获取(如内存、文件句柄、网络连接等)与对象的初始化绑定在一起。具体来说,资源在对象的构造函数中获取,并在对象的析构函数中释放。

🔥  使用RAII的原因以及使用方法?

使用 RAII 的原因

自动管理资源:利用对象的生命周期自动管理资源,避免忘记释放资源。

异常安全:即使发生异常,RAII 也能确保资源被正确释放,防止资源泄漏。

代码简洁:通过封装资源管理,减少代码重复和复杂性。

使用方法

定义资源管理类:创建一个类,负责资源的分配和释放。

在构造函数中分配资源:例如,分配内存、打开文件等。

在析构函数中释放资源:确保资源在对象生命周期结束时得到释放。

🔥 new 和 malloc 的区别?delete 和 free 的区别?

new:是 C++ 操作符,用于动态分配内存并调用构造函数。它会返回指向新分配内存的指针,并且可以用 delete 释放内存。new 会调用对象的构造函数初始化对象。

malloc:是 C 标准库函数,用于动态分配内存,但不调用构造函数。它返回一个 void* 指针,需要强制转换为所需类型的指针。使用 malloc 分配的内存需要用 free 释放。

delete:用于释放 new 分配的内存,并调用析构函数。对于数组使用 delete[]

free:用于释放 malloc 分配的内存。free 不会调用析构函数。

int* p = new int(10); // 使用 new
delete p;             // 使用 deleteint* q = (int*)malloc(sizeof(int)); // 使用 malloc
free(q);                          // 使用 free

🔥 C++的内存框架分布情况

C++ 的内存框架主要包括以下几个区域:

栈(Stack):用于存储局部变量和函数调用信息。栈内存分配速度快,但空间有限。

堆(Heap):用于动态分配内存。程序员可以控制分配和释放内存,但需要显式管理。内存的生命周期由程序员控制。

静态存储区(Static Storage Area):用于存储静态变量和全局变量。这些变量在程序运行期间一直存在。

常量区(Constant Data Area):用于存储常量数据,如字符串常量。

🔥 堆和栈的区别?

分配方式自动分配和释放手动分配和释放(使用 new/malloc 和 delete/free)。
生命周期函数调用时分配,函数返回时释放。由程序员控制。
大小通常较小(受限于栈大小)。通常较大(受限于系统可用内存)。
速度较快。较慢(需要管理和合并空闲块)。
用途局部变量、函数调用信息。动态内存分配。

🔥 什么是内存对齐,内存对齐有什么好处?

内存对齐 是将数据结构的内存地址按某个特定的对齐边界(通常是数据类型的大小)进行排列。对齐可以提高内存访问效率,因为许多处理器在对齐的数据上访问更快。

好处

提高性能:对齐的数据结构能更有效地利用缓存和减少内存访问时间。

防止错误:某些硬件架构要求数据必须对齐,否则会引发硬件异常或性能下降。

🔥 什么是内存泄漏?写代码时应该怎么防止内存泄漏。

内存泄漏 是指程序在动态分配内存后,没有释放这些内存,导致系统资源浪费并最终可能导致程序崩溃或变慢。

防止内存泄漏的方法

使用 RAII:通过对象的生命周期自动管理资源。

智能指针:使用 std::unique_ptr 和 std::shared_ptr 管理动态内存,避免手动释放内存。

内存分析工具:使用工具如 Valgrind、AddressSanitizer 等检测和分析内存泄漏。

良好的编码实践:确保每个 new 都有对应的 delete,每个 malloc 都有对应的 free

🔥 说说什么是深拷贝和浅拷贝?

浅拷贝

定义:复制对象时,只复制对象的指针值,而不复制指针指向的内容。结果是原始对象和拷贝对象共享相同的资源。

问题:修改一个对象的资源会影响到另一个对象,可能导致资源冲突和错误。

深拷贝

定义:复制对象时,创建对象指针指向的资源的完整副本。这样,原始对象和拷贝对象各自拥有自己的资源。

好处:避免共享资源带来的问题,保证对象的独立性。

四、类相关

🔥 讲讲C++的三大特性(封装、继承、多态)

封装:将数据(成员变量)和对数据的操作(成员函数)捆绑在一起,并对外部隐藏数据的具体实现。

继承:通过继承,子类可以继承父类的属性和行为,从而实现代码的重用和扩展。

多态:允许不同的对象以相同的接口进行操作,即同一操作可以作用于不同的对象上,表现出不同的行为。

🔥 多态是怎么实现的? 

多态的实现主要依赖于虚函数和动态绑定。多态允许程序在运行时决定调用哪个具体的函数实现。

运行时多态:通过使用虚函数实现,允许基类指针或引用调用派生类中的重写函数。这是通过虚函数表(vtable)和虚指针(vptr)机制实现的。

🔥 什么是虚函数和纯虚函数?以及区别?

虚函数

定义:在基类中声明为 virtual 的成员函数,允许派生类重写(override)该函数。虚函数实现运行时多态。

作用:通过基类指针或引用调用虚函数时,实际调用的是派生类中的重写函数。

纯虚函数

定义:在基类中声明为 virtual 并且等于 0 的虚函数,例如 virtual void func() = 0;

作用:定义接口,要求所有派生类必须实现纯虚函数。使得基类成为抽象类,不能实例化。

🔥 虚函数是怎么实现的?虚函数在那个阶段生成?

虚函数表(vtable):每个类(如果包含虚函数)会有一个虚函数表,它是一个指向虚函数实现的指针数组。每个对象会有一个指向虚函数表的指针(vptr)。

动态绑定:通过 vptr 指针,运行时可以查找虚函数表中的具体函数地址,决定调用哪个函数实现。

生成阶段:

编译阶段:编译器生成虚函数表和虚指针。虚函数表在类定义时生成,虚指针在对象实例化时设置。

🔥 构造函数和析构函数的作用?

构造函数

作用:在创建对象时初始化对象的状态。构造函数可以用于分配资源、初始化数据成员等。

特性:构造函数与类名相同,无返回值,可以被重载。

析构函数

作用:在对象生命周期结束时清理对象的资源。析构函数负责释放构造函数分配的资源,如内存、文件句柄等。

特性:析构函数的名字前加 ~,无返回值,不能被重载。

🔥 构造函数和析构函数那个可以被声明为虚函数,为什么?

析构函数可以被声明为虚函数:如果基类的析构函数是虚函数,确保派生类的析构函数在基类指针或引用被销毁时正确调用。防止资源泄漏和不完全析构。

构造函数不能被声明为虚函数:构造函数在对象创建时调用,此时对象还未完全构造,无法设置虚函数表,因此构造函数不能是虚函数。

🔥 构造函数和析构函数那个可以被重载,为什么?

析构函数不可以被重载:每个类只能有一个析构函数,负责对象生命周期结束时的清理操作。如果允许重载,会导致析构时不确定性。

构造函数可以被重载:允许创建对象时通过不同的参数进行初始化。通过重载构造函数,可以提供多种初始化方式。

🔥 谈谈什么是重载、重写、隐藏?

重载:同一作用域内多个函数名相同但参数列表不同。

覆盖:派生类重写基类的虚函数,提供特定实现。

重定义:派生类中定义了与基类同名但参数列表不同的函数,导致基类函数不可见。

🔥 this 指针,为什么会存在this指针?

定义this 指针是指向当前对象的指针。在类的成员函数内部,this 指针指向调用该函数的对象。

访问对象成员:允许成员函数访问对象的属性和其他成员函数。

区分成员变量和参数:在成员函数中,this 指针可以用来区分同名的成员变量和函数参数。

实现链式调用:可以返回 *this 实现链式调用。

五、其他必备

🔥 什么是git? 以及git常用命令

Git 是一个分布式版本控制系统,用于跟踪文件的更改,特别是源代码。它允许多人协作开发,通过分支和合并功能管理项目的不同版本和特性。

git常用命令:

  • git clone [url]:克隆远程仓库到本地。
  • git add [file]:将文件添加到暂存区。
  • git commit -m "message":提交更改到本地仓库,-m 用于添加提交信息。
  • git status:查看工作目录和暂存区的状态。
  • git diff:查看文件的更改内容。
  • git remote add [name] [url]:添加远程仓库。
  • git fetch [remote]:从远程仓库获取最新的更改,但不合并。
  • git pull:从远程仓库获取并合并更改。
  • git push:将本地提交推送到远程仓库。
  • git log:查看提交历史。
  • git revert [commit]:撤销指定提交,但保留历史记录。
  • git reset [commit]:重置当前分支到指定提交。

🔥 什么是svn? git 和 svn的区别

SVN 是一个集中式版本控制系统,用于跟踪文件的更改和管理版本。与 Git 不同,SVN 采用集中式存储模式,所有版本历史都存储在中央服务器上。

git 一般用于存放代码,svn一般用于存放文档等。

🔥Linux 系统下常用的命令会那些,举例?

  • ls:列出目录内容。
  • cd [dir]:切换目录。
  • pwd:显示当前工作目录。
  • cp [source] [destination]:复制文件或目录。
  • mv [source] [destination]:移动文件或目录。
  • rm [file]:删除文件,rm -r [dir] 删除目录及其内容。
  • cat [file]:显示文件内容。
  • vim [file]:使用 Vim 编辑器编辑文件。
  • chmod [permissions] [file]:改变文件权限。
  • chown [owner]:[group] [file]:改变文件所有者和组。
  • ps:显示当前进程。
  • top:实时显示系统进程和资源使用情况。
  • df -h:显示文件系统的磁盘使用情况。
  • du -sh [dir]:显示目录的大小。
  • kill [pid]:结束进程,pid 是进程ID。
  • ping [host]:测试网络连接。
  • ifconfig 或 ip a:显示网络接口信息。
  • netstat:显示网络状态和端口使用情况。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/400843.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【MySQL 03】库的操作 (带思维导图)

文章目录 &#x1f308; 一、创建数据库&#x1f308; 二、查看数据库&#x1f308; 三、使用数据库&#x1f308; 四、修改数据库&#x1f308; 五、删除数据库&#x1f308; 六、备份数据库&#x1f308; 七、恢复数据库&#x1f308; 八、字符集和校验规则⭐ 1. 查看系统默认…

HAProxy 效能飞跃先锋队

目录 一 负载均衡 1.1 四层负载 1.2 七层负载 1.3 四层负载和七层负载的区别 二 Haproxy简介 2.1 概念和内容 2.2 haproxy的基本配置信息 2.2.1 global 配置 2.2.2 proxies 配置 三 Haproxy的算法 3.1 静态算法 3.2 动态算法 3.3 其他算法 四 高级功能及配置 4.…

Mysql原理与调优-索引原理及使用

目录 1.绪论 2.索引原理 2.1 索引采用的数据结构 2.1.1 B树 1.什么是B树 2.B树的优缺点 2.1.2 B树 1.什么是B树 3.B树的优缺点 2.2.3 hash 2.2.4 总结 2.2 InnoDB数据存储结构(聚簇索引) 2.2.1 自底向上探寻索引 3.2.2 查询一条数据的完整流程 3.2.3 聚簇索引 2…

奥运科技观察:AI PC,如何成为当代体育精神的数字捍卫者?

作者 | 曾响铃 文 | 响铃说 数字孪生帮助体育馆建设、超高清直播……这届奥运会科技感拉满&#xff0c;几乎所有前沿技术都能在奥运的赛事运营中发现。 而AI大时代&#xff0c;AI如何帮助帮助奥运会顺利举办、如何帮助运动员拥有更好的表现&#xff0c;同样值得业界关注&…

haproxy最强攻略

1、负载均衡 负载均衡&#xff08;Load Balance&#xff0c;简称 LB&#xff09;是高并发、高可用系统必不可少的关键组件&#xff0c;目标是 尽力将网络流量平均分发到多个服务器上&#xff0c;以提高系统整体的响应速度和可用性。 负载均衡的主要作用如下&#xff1a; 高并发…

# Spring Cloud Alibaba Nacos_配置中心与服务发现(四)

Spring Cloud Alibaba Nacos_配置中心与服务发现&#xff08;四&#xff09; 一、Nacos 配置管理-集群部署 1、 把 nacos 应用程序包&#xff0c;复制3份&#xff0c;分别命名为 nacos1, nacos2, nacos3 分别在 conf 目录下&#xff0c;修改 application.properties 配置文件…

数据结构——循环队列

目录 循环队列的基本知识 循环队列的实现 定义 各个接口的实现 循环队列的基本知识 循环队列的定义 循环队列&#xff08;Circular Queue&#xff09;是一种使用固定大小的数组实现的队列&#xff0c;它将数组的首尾相连&#xff0c;形成环形&#xff0c;以充分利用空间并实…

Spring Boot的配置文件

目录 一、配置文件 1.properties为后缀的配置文件 1.1基本语法 1.2读取配置文件 1.3properties的优缺点 1.4加中文注释出现乱码 2.yml格式的配置文件 2.1基础语法 2.2读取配置文件 2.2.1对象存储到配置文件中 2.3yml的优缺点 2.4用不用加单引号或者双引号呢&#xf…

【C语言篇】编译和链接以及预处理介绍(上篇)

文章目录 前言翻译环境和运行环境翻译环境编译预处理&#xff08;预编译&#xff09;编译词法分析语法分析语义分析 汇编 链接 运行环境预处理&#xff08;预编译&#xff09;详解预定义符号#define定义常量#define定义宏带有副作用的宏参数宏替换的规则宏和函数的对比 写在最后…

opencv基础的图像操作

1.读取图像&#xff0c;显示图像&#xff0c;保存图像 #图像读取、显示与保存 import numpy as np import cv2 imgcv2.imread(./src/1.jpg) #读取 cv2.imshow("img",img) #显示 cv2.imwrite("./src/2.jpg",img) #保存 cv2.waitKey(0) #让程序进入主循环(让…

RAG系列之四:深入浅出 Embedding

在 RAG 系列之三&#xff1a;文本切分中介绍了如何将文本切分成更小的语义单元&#xff0c;接下来便是将拆分的文本块进行向量化。 什么是文本向量化&#xff1f; 文本向量化就是将文本数据转成数字数据&#xff0c;例如&#xff1a;将文本 It was the best of times, it was…

Android全面解析之context机制(二): 从源码角度分析context创建流程(上)

前言 这篇文章从源码角度分析context创建流程。 在上一篇Android全面解析之Context机制(一) :初识context一文中讲解了context的相关实现类。经过前面的讨论&#xff0c;读者对于context在心中有了一定的理解。但始终觉得少点什么&#xff1a;activity是什么时候被创建的&…

Python数据可视化案例——地图

目录 简单案例&#xff1a; 进阶案例&#xff1a; 继上文数据可视化案例&#xff0c;今天学习用pyecharts练习数据可视化案例2-构建地图。 简单案例&#xff1a; 首先构建一个简单的地图。 代码&#xff1a; import json from pyecharts.charts import MapmapMap() data[…

培训学校课程管理系统-计算机毕设Java|springboot实战项目

&#x1f34a;作者&#xff1a;计算机毕设残哥 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目、 源…

大数据面试SQL(八):求连续段的起始位置和结束位置

文章目录 求连续段的起始位置和结束位置 一、题目 二、分析 三、SQL实战 四、样例数据参考 求连续段的起始位置和结束位置 一、题目 有一张表t2_id记录了id&#xff0c;id不重复&#xff0c;但是会存在间断&#xff0c;求出连续段的起始位置和结束位置。 样例数据&…

结构体structure、共用体union

目录 结构体 结构体类型的定义形式 结构体类型的大小 内存计算例子 共用体union 用共用体判断大小端 结构体和共用体对比 qsort&#xff08;&#xff09; 结构体 结构体类型——用来描述复杂数据的一种数据类型 构造类型&#xff08;用户自定义类型&#xff09; struc…

CUDA+tensorflow+python+vscode在GPU下环境安装及问题汇总与解答

2024.8.14 因为要做深度学习&#xff0c;需要安装tensorflowgpu的环境&#xff0c;每次都搞不好整的很生气&#xff0c;本次将安装过程中参考的一些大佬的博客和安装过程中遇到的问题及解决方案总结一下&#xff0c;希望以后不要在这件事情上浪费时间。安装环境其实也没有想象中…

Halcon图像平滑与去噪

Halcon图像平滑与去噪 文章目录 Halcon图像平滑与去噪1. 均值滤波2. 中值滤波3. 高斯滤波5. 光照不均匀 有时拍摄的图像中会存在很多杂点和噪声&#xff0c;对于比较均匀的噪声&#xff0c;可以考虑用软件的算法进行 消除。例如&#xff0c;可以用图像平滑的方法进行去噪&#…

uniapp 自定义全局弹窗

自定义全局弹窗可在js和.vue文件中调用&#xff0c;unipop样式不满足&#xff0c;需自定义样式。 效果图 目录结构 index.vue <template><view class"uni-popup" v-if"isShow"><view class"uni-popup__mask uni-center ani uni-cust…

数学建模——启发式算法(蚁群算法)

算法原理 蚁群算法来自于蚂蚁寻找食物过程中发现路径的行为。蚂蚁并没有视觉却可以寻找到食物&#xff0c;这得益于蚂蚁分泌的信息素&#xff0c;蚂蚁之间相互独立&#xff0c;彼此之间通过信息素进行交流&#xff0c; 从而实现群体行为。 蚁群算法的基本原理就是蚂蚁觅食的过程…