C++ 在线工具 | 菜鸟工具
1.独占指针和const的区别?
独占指针是一种智能指针,同一时间只能有一个独占指针,指向某个特定对象,自动释放所指向对象的内存,不可以拷贝复制;
const是修饰符,表示变量的不可修改,修饰类的成员函数时,表示该函数不修改对象状态,该函数可以被const对象调用;
2.智能指针介绍;
share_ptr:共享式智能指针,使用计数机制自动管理内存的释放,允许多个shared_ptr对象共享同一个资源,适用于多个对象需要共享同一资源场景,比如多个模块需要访问同一个配置文件对象;
unique_ptr:独占式指针,保证所管理的对象只能有一个所有者,离开作用域时,它会自动释放所指向资源,不能拷贝复制,但可以移动,适用于管理动态分配的单个对象,例如在函数内部动态分配一个对象并返回给调用者;
weak_ptr:弱引用智能指针,不参与资源的引用计数,解决share_ptr的循环引用问题,用weak_ptr观察shared_ptr所管理的对象,不会影响对象的生命周期,当shared_ptr所管理的对象被释放后,weak_ptr会自动变为空指针;
使用智能指针有效防止内存泄漏;智能指针还可以用于管理其他类型的资源,如文件句柄、网络连接等。当智能指针离开作用域时,会自动关闭文件或断开网络连接,确保资源的正确释放;函数可以返回智能指针,将资源的所有权转移给调用者,由调用者负责资源的释放,使代码更加清晰和安全;
- 容器类对象:在容器中存储对象指针时,使用智能指针可以方便地管理容器中对象的生命周期。当对象从容器中移除或容器本身被销毁时,智能指针会自动释放对象。
3.强制类型转换;
动态类型转换dynamic_cast:
静态类型转换static_cast:
常量类型转换const_cast:
4.左右值区别,移动语义
左值在程序运行时有确定内存地址,右值是临时对象,没有内存地址的值;
移动语义通过“移动构造函数”和“移动赋值操作符”引入,目的是从右值中“窃取”资源,而不是复制资源,从而提高程序效率。
- 移动构造函数:
- 用于从右值对象中“移动”资源到新创建的对象中。
- 它的典型签名是:
ClassName(ClassName&& other);
- 移动构造函数通常会将源对象的资源指针置为
nullptr
或其他有效但为空的状态,以避免资源重复释放。
- 移动赋值操作符:
- 用于将右值对象的资源“移动”到左值对象中。
- 它的典型签名是:
ClassName& operator=(ClassName&& other);
- 同样,需要确保源对象处于有效但未定义的状态。
std::move
:std::move
是一个标准库函数,用于将一个左值强制转换为右值引用,从而可以对其执行移动操作。- 需要注意的是,
std::move
并不真正“移动”任何内容,它只是进行了一个类型转换。
- 使用场景:
- 当涉及到大量数据或需要频繁复制的对象时(如容器、字符串等),移动语义可以显著提高性能。
- 移动语义避免了不必要的深拷贝,减少了内存分配和释放的开销。
5.析构函数为什么定义为虚函数?
保证父类指针指向子类对象时,子类的析构函数可以被调用,释放内部内存;
6.sizeof(int),sizeof(指针)?
32位:4,4;
64位:4,8
7.优先队列priority_queue使用?
小顶堆(greater),大顶堆(less)
8.deque和queue区别?
双端队列(可以直接读取两头位置),单端队列
9.decltype查询类型?
decltype
是C++11引入的一个关键字,用于查询表达式的类型,并在编译时返回该类型。这在泛型编程和模板编程中尤为有用,因为它允许程序员在不明确指定类型的情况下获取表达式的类型。
10.Vector的内存管理?
clear();//清空size
resize();//设定size大小
reserve();//设定capacity大小
shrink_to_fit();//设置容器的capacity向size大小缩小;
与空容器swap(vector<int>());//彻底清空容器内存;
push_back元素时,如果内存足够,内存不变;如果内存不够,vector会重新申请一块二倍的size大小的capacity空间,将原来的元素拷贝(移动)复制过去,将要push的元素对应拷贝(移动)复制过去,清除掉之前占用的内存空间;
11.emplace_back()和push_back()的区别?
push_back()
用于将一个已存在的对象添加到容器的尾部。如果传递的是对象本身,会调用对象的拷贝构造函数;如果传递的是右值(如临时对象),则会调用移动构造函数。如果对象不存在,则会先创建一个临时对象,再进行拷贝或者移动;
emplace_back()
允许直接在容器的尾部构造对象,而不需要先创建一个临时对象再进行拷贝或移动。它会直接在容器的内存空间中调用对象的构造函数,避免了不必要的拷贝或移动操作,提高了性能。
区别总结
- 性能方面:
emplace_back()
通常比push_back()
更高效,尤其是对于构造复杂对象或需要动态分配内存的对象,因为它避免了不必要的拷贝或移动操作。 - 使用方式:
push_back()
接受一个已存在的对象作为参数,而emplace_back()
接受构造对象所需的参数,直接在容器内部构造对象。
应用场景
push_back()
:当你已经有一个现成的对象,并且想要将其添加到容器中时,使用push_back()
比较合适。emplace_back()
:当你需要在容器中创建新对象,并且希望避免不必要的拷贝或移动开销时,应该优先使用emplace_back()
。特别是在处理大型对象或自定义类型时,emplace_back()
的性能优势会更加明显。
12.stoll()将字符串转为long long类型
13.如何有效防止栈溢出?
在 C++ 中,栈溢出通常是由于栈空间被过度使用导致的,常见原因包括递归调用过深、在栈上创建过大的局部变量等。
(1)优化递归算法,迭代替代递归;
(2)控制递归深度;
(3)避免在栈上创建过大的局部变量,将大对象分配到堆上使用动态内存分配
#include <iostream>
// 在栈上创建大数组(可能导致栈溢出)
// const int SIZE = 1000000;
// int stackArray[SIZE];
// 在堆上创建大数组
const int SIZE = 1000000;
int* heapArray = new int[SIZE];
int main() {
// 使用堆上的数组
for (int i = 0; i < SIZE; ++i) {
heapArray[i] = i;
}
// 释放堆上的内存
delete[] heapArray;
return 0;
}
14.如何优化代码内存消耗?
结构体表达使用;
避免不必要的拷贝构造,比如函数传参引用;
代码设计,提高时间复杂度,空间复杂度;
15.指针函数,函数指针
指针函数:函数的返回值是指针;
函数指针:有一个指针指向函数,可以直接使用指针调用函数,比如回调函数的使用;
16.内联函数inline的使用,内联函数可以递归吗?可以为虚函数吗?
内联函数不可以递归,也不可以为虚函数,因为内联函数需在编译时候会在函数调用位置展开,递归会导致代码过于膨胀,虚函数在运行时确定调用函数,所以不可以;
17.模板函数可以为虚函数吗?
不可以,跟内联函数原因一致,模板函数在编译期间确定模板类型;
18.C与C++的struct的区别?
struct在C中没有访问权限概念,所有成员都是公共的访问权限的概念,C++(默认是public)可以设置访问权限private, protected。
19.C++中,class与struct的区别?
class默认访问权限是private,struct默认是public;struct一般用于简单数据结构的聚合,class侧向面向对象的特性,封装继承多态这些;
20.排序的效率,哈希表红黑树的理解?
红黑树的底层结构是二叉搜索树;
21.锁的使用
(1)std::mutex 互斥锁
构造函数:不允许拷贝构造,不允许move拷贝,默认unlocked状态;
lock()函数:调用线程将锁住该互斥量。线程调用该函数会发生下面 3 种情况:
如果该互斥量当前没 有被锁住,则调用线程将该互斥量锁住,直到调用 unlock之前,该线程一直拥有该锁。
如果当 前互斥量被其他线程锁住,则当前的调用线程被阻塞住。
如果当前互斥量被当前调用线程锁 住,则会产生死锁(deadlock)。
unlock()函数:解锁,释放对互斥量的所有权
try_lock()函数:尝试锁住互斥量,如果互斥量被其他线程占有,则当前线程也不会被阻塞。线程调用该 函数也会出现下面 3 种情况:
如果当前互斥量没有被其他线程占有,则该线程锁住互斥量,直 到该线程调用 unlock 释放互斥量。
如果当前互斥量被其他线程锁住,则当前调用线程返回 false,而并不会被阻塞掉。
如果当前互斥量被当前调用线程锁住,则会产生死锁(deadlock)。
(2)std::lock_guard 条件锁
(3)std::condition_variable 同步条件变量
参考:
21.如何排查bug
gdb调试的使用