C++面试真题分享20260320

**目录

1、 指针和引用的区别 2

2、 函数内部返回指针和引用会怎么样 2

3、 一级指针、二级指针、三级指针的使用(举例说明) 3

4、 讲一下智能指针,实现原理是什么? 3

5、 C++四种命名的、更安全的类型转换运算符 4

6、 static、const 5

7、 什么是虚构函数,实现原理是什么 5

8、 为什么析构函数要设置为虚函数 6

9、 如果实例化一个类,编译器会生成哪些内容? 6

10、 使用过那些容器、什么时候选择使用那个 7

11、 QT元对象系统 7**

1、指针和引用的区别

特点:

指针是变量,指针本身占用内存,4字节或者8字节,指向的是对象的地址,其所指向的地址可变,地址里面存储的数据也可变,也可以为空(nullptr),使用之前需要进行判断,可以使用解引用符(*)来访问指针所指向的地址里面的值。

使用场景:

通常用于动态内存分配,数组操作和函数传参等

int x = 10;

int y = 90;

int *ptr = &x;
ptr = 99;//通过解引用符( )修改其所指向地址中存放的数据

ptr = &y;//修改指针指向内容

特点:

引用是变量的别名,对引用的所有操作都直接作用于原变量,定义是就要初始化,并且在这个生命周期当中不能重新绑定到另一个变量,没有空引用的概念,引用通常不占用额外的内存。

使用场景:

通常用于函数传参、操作符重载以及创建别名等

int x = 10;

int &ref = x;

ref = 90 //x 的值被修改为90;

2、函数内部返回指针和引用会怎么样

1、返回局部变量的指针或者引用都是严重错误

int* badFunc()

{

int x = 10; // 栈上分配

return &x; // 函数结束x被销毁,返回悬垂指针

}

2、返回动态分布内存的指针,可以但是调用这需要注意释放,建议使用智能指针std::unique_ptr返回。

int* createArray(int size)

{

return new int[size]; // 调用者需记得delete[]

}

3、返回静态/全局变量或成员变量的引用是安全的

const std::string& getName()

{

static std::string name = "default"; // 静态生命周期

return name; // 安全

}

3、一级指针、二级指针、三级指针的使用(举例说明)

指针的层级反应了简介访问的深度,用于修改不同"级别"的数据

1、一级指针:修改所指向的内容

void setValue(int* p, int val)

{

*p = val; // 修改p指向的int

}

2、二级指针:修改指针本身

void createBuffer(char** buffer, int size)

{
buffer = new char[size]; // 修改外部指针
}
// 调用:
char
buf;

createBuffer(&buf, 1024);

3、三级指针:较少用,用于修改指向指针的指针

void resizeMatrix(int*** matrix, int newRows)

{
matrix = new int [newRows]; // 修改行指针数组

for(int i=0; i<newRows; i++)

(*matrix)[i] = new int[cols];

}

4、讲一下智能指针,实现原理是什么?

智能指针是基于RAII的自动化内存管理工具,核心原理是用栈对象的生命周期管理堆资源。

1、std::unique_ptr:独占所有权

提供对动态分配的单⼀对象所有权的独占管理。通过独占所有权,确保只有⼀个

std::unique_ptr 可以拥有指定的内存资源。

原理:禁用拷贝(=delete),只允许移动。析构时自动delete。

简化实现:

template

class SimpleUniquePtr {

T* ptr;

public:

~SimpleUniquePtr() { delete ptr; }

// 禁止拷贝

SimpleUniquePtr(const SimpleUniquePtr&) = delete;

// 允许移动

SimpleUniquePtr(SimpleUniquePtr&& other) : ptr(other.ptr) {

other.ptr = nullptr;

}

};

2、std::shared_ptr:共享所有权

允许多个智能指针共享同⼀块内存资源。内部使⽤引⽤计数来跟踪对象被共享的次数,当计数为零时,资源被释放。提供更灵活的内存共享,但可能存在循环引⽤的问题。

原理:引用计数。内部包含两个指针:指向对象、指向控制块。

控制块:包含引用计数器、弱引用计数器、删除器。

关键:引用计数增减是原子操作,保证线程安全。

3、std::weak_ptr:弱引⽤智能指针

⽤于解决 std::shared_ptr 可能导致的循环引⽤问题。

std::weak_ptr 可以从 std::shared_ptr 创建,但不会增加引⽤计数,不会影响资源的释放。通过 std::weak_ptr::lock() 可以获取⼀个 std::shared_ptr 来访问资源。

5、C++四种命名的、更安全的类型转换运算符

关键字:static_cast、dynamic_cast、reinterpret_cast和 const_cast

1、static_cast 最常用,用于良性转换

没有运⾏时类型检查来保证转换的安全性

进⾏上⾏转换(把派⽣类的指针或引⽤转换成基类表示)是安全的

进⾏下⾏转换(把基类的指针或引⽤转换为派⽣类表示),由于没有动态类型检查,所以是不安全的。

基本类型转换:double d = static_cast(i);

向上转型:Base* b = static_cast<Base*>(d);

非const转const

使⽤:

  1. ⽤于基本数据类型之间的转换,如把int转换成char。
  2. 把任何类型的表达式转换成void类型。
    2、dynamic_cast 安全向下转型
    在进⾏下⾏转换时,dynamic_cast具有类型检查(信息在虚函数中)的功能,⽐static_cast更安全。
    转换后必须是类的指针、引⽤或者void*,基类要有虚函数,可以交叉转换。
    dynamic本身只能⽤于存在虚函数的⽗⼦关系的强制类型转换;对于指针,转换失败则返回nullptr,对于引⽤,转换失败会抛出异常。
    3、const_cast 移除const/volatile
    常量指针转换为⾮常量指针,并且仍然指向原来的对象。常量引⽤被转换为⾮常量引⽤,并且仍然指向原来的对象。去掉类型的const或volatile属性。
    4、reinterpret_cast 低层重新解释
    最危险,如指针转整数、函数指针转换 几乎不用于应用程序开发,在驱动或特定库中使用

6、static、const

static:

1、静态局部变量:函数内只初始化一次,保持值

int getNextID() {

static int id = 0; // 只执行一次

return ++id;

}

2、静态成员变量:类所有对象共享,需类外定义

class Device {

static int deviceCount; // 声明

};

int Device::deviceCount = 0; // 定义

3、静态成员函数:属于类而非对象,不能访问非静态成员

const:

1、修饰变量:声明变量为常量,不可修改

2、修饰指针:

常量指针:const int* p:指向内容不可变

指针常量:int* const p:指针本身不可变

3、修饰成员函数:不能修改非静态的成员变量

4、修饰引用:const T&,常用作函数参数避免拷贝

7、什么是虚构函数,实现原理是什么

虚函数是实现运行时多态的机制,允许通过基类指针/引用调用派生类的覆盖函数。

1、虚函数表:每个含有virtual函数的class都有⼀个vtbl(虚函数表),vtbl中存储的是指向各个虚函数的函数指针;

2、虚表指针:每个对象内部有一个隐藏的vptr,指向其类的虚表。

3、内存布局

class Base { // 有虚函数

int data;

virtual void func1();

virtual void func2();

};

// 对象内存:[vptr][data...]

// vptr -> Base的虚表:[&Base::func1, &Base::func2]

4、当对象调⽤某个virtual函数的时候,编译器根据对象的vptr找到类的vtbl,在vtbl中寻找适当的函数指针

调用过程:

Base* b = new Derived();

b->func1(); // 实际是:(*b->vptr[0])()通过vptr找到虚表

通过偏移找到函数地址

调用该地址

8、为什么析构函数要设置为虚函数

当通过基类指针删除派生类对象时,如果基类析构函数非虚,会导致派生类部分内存泄漏。

9、如果实例化一个类,编译器会生成哪些内容?

实例化一个类时,编译器/运行时主要做以下事情:

1、内存分配:

栈上:编译器计算大小,调整栈指针

堆上:调用new运算符,底层是malloc

2、初始化过程(按顺序):

a. 如果有虚函数,设置vptr指向正确的虚表

b. 按声明顺序初始化成员变量:

基本类型:不初始化(随机值),除非在初始化列表

类类型:调用其构造函数

c. 执行构造函数体

3、特殊成员函数的生成:

如果没定义,编译器生成:

默认构造函数

拷贝构造函数

拷贝赋值运算符

析构函数

移动操作(C++11后)

10、使用过那些容器、什么时候选择使用那个

根据访问模式、操作频率、内存布局选择容器

容器 底层 关键特性 使用场景

vector​ 动态数组 连续内存,随机访问O(1),尾部插入快,中间插入慢 默认选择,存储B超图像帧序列

deque​ 分块数组 头尾插入O(1),随机访问较快 需要频繁头尾操作,如实时数据缓冲区

list​ 双向链表 任意位置插入O(1),内存不连续,无随机访问 极少用,除非需高频中间插入删除

map​ 红黑树 按键排序,查找O(log n) 需要有序遍历或范围查找,如有序配置表

unordered_map​ 哈希表 平均O(1)查找,无序 快速查找,如设备ID->控制器映射

11、QT元对象系统

Qt元对象系统是Qt框架实现信号槽、运行时类型信息、属性系统等高级特性的基石。它由三个核心部分组成:Q_OBJECT宏、元对象编译器moc、以及运行时支持库。

Q_OBJECT宏:在类声明中标记,表示这个类需要元对象系统支持

元对象编译器moc:在编译前扫描头文件,为含Q_OBJECT的类生成额外的.moc.cpp文件

QMetaObject类:运行时存储类的元信息(类名、方法、属性、信号槽等)

相关推荐
Irissgwe1 小时前
c++特殊类设计
java·开发语言·c++
2301_816651222 小时前
C++中的享元模式变体
开发语言·c++·算法
大傻^2 小时前
Spring AI Alibaba ChatClient实战:流式输出与多轮对话管理
java·人工智能·后端·spring·springai·springaialibaba
m0_583203132 小时前
C++中的访问者模式变体
开发语言·c++·算法
小帅学编程2 小时前
英语学习笔记
java·笔记·学习
浅念-2 小时前
C ++ 智能指针
c语言·开发语言·数据结构·c++·经验分享·笔记·算法
不染尘.2 小时前
最小生成树算法
开发语言·数据结构·c++·算法·图论
学编程就要猛2 小时前
JavaEE初阶:文件操作和IO
java·java-ee
ba_pi2 小时前
每天写点什么2026-03-19-Doris三种存储模型
java·数据库·mysql