金山C++一面-面经总结
注意:本次总结使用全简写速记模式记-关键提点-具体自行补充
1、c++程序的内存分布?
代码
数据(已初始化 未初始化BSS)
堆(动态分配内存)
栈(存放局部变量 函数参数)
常量
2、堆和栈的区别?
总结:自大低效生(自大滴小生)
程序员自己操作堆 new
操作系统自动分配和释放栈
堆可以一直堆啊 可以到虚拟内存的大小
栈可不一样 几兆B吧
堆从低到高生长
栈从高向低移动
分配的效率肯定自己能操作的比较低
栈是os弄得 效率很高
生命周期自己管堆
函数执行周期一致
3、内存泄漏怎么办?
泄漏就是申请的堆内存没正确释放
原因:没delete 循环引用 父类得虚析构
方法:
valgrind --leak-check=full ./your_program
LeakSanitizer
AddressSanitizer
Visual Studio
解决:智能指针
RAII原则
虚析构
new delete匹配
4、智能指针,哪几种?
shared_ptr引用计数
weak_ptr 观察
unique_ptr 独占不可复制 可以移动语义
auto_ptr废弃了 转移所有权的问题 以外的内存
#include <memory>
#include <iostream>
using namespace std;void testshared()
{shared_ptr<int> p1(new int(30));shared_ptr<int> p2 = p1;weak_ptr<int> p3 = p1;unique_ptr<int> p4(new int(40));unique_ptr<int> another = move(p4);cout << "p1:" << *p1 << endl;cout << "p1 count:" << p1.use_count() << endl;cout << "p2 count:" << p2.use_count() << endl;if (!p4)cout << "empty" << endl;
}
int main()
{testshared();
}
5、循环引用计数最后是多少?
循环了 一般都是1 或者更高 因为互相引用
6、shared_ptr线程安全吗?
引用计数是安全的 atomic操作
但是修改对象本身可不行 不是原子的
但可以mutex 可以atomic
7、多线程使用shared_ptr如何保护数据安全?
- 使用互斥锁(Mutex)
- 使用 std::atomic<std::shared_ptr>
- 使用 std::shared_mutex(读写锁)
- 使用 std::condition_variable 进行线程同步
9、unique_ptr转移所有权?
可以通过移动语义
在对象之间进行所有权的转移,避免了不必要的资源拷贝操作。
std::move 函数将一个左值(lvalue)转换为右值(rvalue),从而允许 std::unique_ptr 的移动构造函数或移动赋值运算符被调用,实现所有权的转移。
10、move实现方式?
略
11、完美转发有什么用?
将参数“完美”地转发给内部调用的其他函数,同时保持参数的类型和值类别(左值或右值)不变。通过完美转发,可以避免不必要的拷贝操作,提高程序的性能和效率。
避免不必要的拷贝操作:
提高模板函数的通用性和灵活性:
支持委托构造函数:
实现高效的可变参数模板函数:
12、模板的特化和偏特化?
特化:为特定的类型提供一个专门的模板实现
全特化:为模板的所有参数提供具体的类型
偏特化:为模板的部分参数提供具体的类型,而其他参数仍然保持泛型。偏特化通常用于类模板,因为函数模板不支持偏特化。
template <typename T>
class Vector {// 通用代码,适用于任何类型T
};
template <>
class Vector<int> {// 针对int类型的特别实现
};
13、c++和c申请内存方式的区别?
在 C 语言中,内存的申请和释放主要通过标准库函数 malloc、calloc、realloc 和 free 来完成。这些函数都是库函数,需要包含相应的头文件(如 <stdlib.h> 或 <malloc.h>)。
在 C++ 中,内存的申请和释放主要通过 new 和 delete 关键字来完成。这些关键字是 C++ 语言的一部分,不需要包含额外的头文件。
14、c++释放数组和普通对象的区别?
delete obj;
delete[] arr;
主要区别
构造函数和析构函数的调用普通对象:new 为单个对象调用构造函数,delete 为单个对象调用析构函数。数组:new[] 为数组中的每个元素调用构造函数,delete[] 为数组中的每个元素调用析构函数。
内存管理普通对象:new 分配的内存块只需 delete 释放。数组:new[] 分配的内存块需要 delete[] 释放,以确保所有元素的析构函数都被调用。
15、动态多态虚表的位置在哪?
在只读数据段
中 编译器编译时就生成了 这是类的一部分 类的对象共享同一个虚表
每个包含虚函数的类的实例在内存中都会有一个隐式的虚函数表指针(vptr)。这个指针通常位于对象的最前面,以便快速访问虚函数表。
超强总结:
先有虚函数----类有了虚函数表------有了对象就有虚表指针在对象的内存中----通过虚表指针找到虚函数表 获取虚函数地址调用
16、有序数组去重不用额外空间?
int removeDuplicates(int* nums, int numsSize) {if (numsSize == 0) return 0;int slow = 0;for (int fast = 1; fast < numsSize; fast++) {if (nums[fast] != nums[slow]) {nums[++slow] = nums[fast];}}return slow + 1;
}