C++ 核心知识点全解析(八)

1. C++ 中函数模版和类模版有什么区别?

两者的区别主要还是在于用法和实例化方式不同:

1. 定义和使用:

  • 函数模板:用于创建可以接受不同类型参数的函数。我们定义一个一次性模板,然后生成多个函数版本。
cpp 复制代码
template <typename T>
T max(T a, T b) 
{
    return (a > b) ? a : b;
}

在上面的例子中,max函数可以用于任何支持 ">" 运算符的类型。

  • **类模板:**用于创建可以接受不同类型参数的类。通过创建一次模板类,我们可以生成多个不同类型的类实例。
cpp 复制代码
template <typename T>
class Stack 
{
private:
 std::vector<T> elements;
public:
 void push(T const& elem) { elements.push_back(elem); }
 void pop() { elements.pop_back(); }
 T top() const { return elements.back(); }
};

这个例子里,Stack类可以处理不同类型的堆栈元素。

2. 实例化方式:

  • **函数模板:**编译器在实际调用函数时,根据传入参数的类型自动生成具体类型的函数。
cpp 复制代码
int a = 10, b = 20;
std::cout << max(a, b); // 调用 max<int>(int a, int b)
  • **类模版:**在使用类模版时,我们需要显示的声明模版类型。
cpp 复制代码
Stack<int> intStack;
Stack<double> doubleStack;

2. 请介绍下 C++ 模板中的 SFINAE?它的原则是什么?

SFINAESubstitution Failure Is Not An Error的缩写,意思是在模板定义中,类型替换失败并不是一个编译错误。

通过SFINAE,可以实现某些代码只针对特定类型有效,而对其他类型无效,增强泛型编程的灵活性。

SFINAE的原则说白了就是:当替换模板参数失败时,编译器不会把这个失败当作一个错误,而是会继续尝试其他的模板匹配。如果找不到匹配的模板,才会报错。


3. C++ 的 strcpy 和 memcpy 有什么区别?

两者都用于复制数据,但使用场景略有不同:

  1. strcpy用于复制字符串(null-terminated字符数组)。它会从源字符串复制字符到目标字符数组,直到遇:到终止符'\0'。

  2. memcpy用于复制任意类型的数据块,不限于字符串。它会复制指定长度的内存块(以字节为单位),不会检查终止符。


4. C++ 中为什么要使用 std::array?他有什么优点?

std::array是 C++11 标准引入的新特性,它有很多优点:

  1. 固定大小: std::array是一个固定大小的序列容器,一旦创建,大小就不能改变,它使用的是栈内存。它与:std::vector 不同,std::vector是动态大小的。

  2. 性能优势: std::array在性能上很接近 C 风格的数组,因为它使用连续的栈内存布局。

  3. 类型安全: 与 C 风格数组相比,std::array提供了类型安全的at()接口。

  4. 接口友好: std::array提供了STL容器的标准接口,如size()begin()end()等,使用上也非常方便。

  5. 与现代C++特性结合: 作为STL的一部分,std::array可以很自然地和其他标准库功能配合使用,比如范围for循环、算法函数等。


5. C++ 中堆内存和栈内存的区别是什么?

堆内存和栈内存的区别主要体现在分配方式、管理方式、生命周期和性能等方面:

1. 分配方式:

  • **栈内存:**由编译器在程序运行时自动分配和释放。典型的例子是局部变量的分配。

  • 堆内存: 需要程序员手动分配和释放,使用 new delete操作符。在C++11之后,也可以使用智能指针来管理堆内存。2. 管理方式:

  • **栈内存:**由编译器自动管理,程序员无需担心内存泄漏,生命周期由作用域决定。

  • **堆内存:**由程序员手动管理,如果没有正确释放内存,会导致内存泄漏。

2. 生命周期

  • **栈内存:**变量在离开作用域之后自动销毁。

  • **堆内存:**只要不手动释放,内存会持续存在,直到程序终止。

3. 性能:

  • **栈内存:**内存分配和释放速度极快,性能上优于堆内存。

  • **堆内存:**涉及到复杂的内存管理和分配机制,性能上较慢。


6. C++ 的栈溢出是什么?

栈溢出 (Stack Overflow) 是在程序执行过程中,栈空间被耗尽的一种现象。

栈空间是操作系统为每个线程分配的有限内存,用于存储函数调用、局部变量等。当递归过深(例如无限递归) 或局部变量占用内存过大时,栈空间就会被用尽,导致栈溢

在 C++ 中,典型的栈溢出情况包括:

  1. 无限递归调用:函数不断调用自身,导致栈帧无限增长。

  2. 大局部变量:定义了过多或过大的局部变量,超过了栈内存的限制。


7. 什么是 C++ 的回调函数?为什么需要回调函数?

回调函数是一种通过函数指针或者函数对象(例如 std::function或 lambda 表达式)将一个函数作为参数传递给另一个函数的机制。

实际上,就是把函数的调用权从一个地方转移到另一个地方,这个调用会在未来某个时刻进行,而不是立即执行。之所以称为"回调",可以理解为某种倒叙执行:先安排好函数的调用,不立即执行,等到合适的时机再"回头"执行。

需要回调函数的主要原因包括:

  1. **异步编程:**在异步操作中,比如网络请求、文件读取、事件处理等,可以在操作完成后调用回调函数,而主程序可以继续执行其它任务,避免等待操作完成。

  2. **解耦代码:**回调函数有助于将代码模块化和解耦,允许我们创建更灵活和可复用的代码。例如,一个通用的排序算法可以接受一个比较函数,允许用户自定义排序逻辑。

  3. **事件驱动编程:**在GUI或者其他事件驱动程序中,回调函数经常用于处理用户输入事件,如点击、鼠标移动、键盘输入等。


8. C++ 中为什么要使用 nullptr 而不是 NULL?

主要原因是 nullptr 有明确的类型,它是 std::nullptr_t 类型,可以避免代码中出现类型不一致的问题。


9. 什么是大端?什么是小端?

通俗点讲就是数据在内存中的存放顺序。

  1. **大端序:**高字节存储在内存的低地址处,低字节存储在高地址处。例如,对于16进制数0x12345678,大端序在内存中的存储方式是:12 34 56 78。

  2. **小端序:**低字节存储在内存的低地址处,高字节存储在高地址处。对于同样的16进制数0x12345678,小端序在内存中的存储方式是:78 56 34 12。


10. C++ 中 include<a.h> 和 include"a.h" 有什么区别?

两者都是用来包含头文件的指令,区别在于搜索头文件的路径。

  1. #include<a.h>:编译器会在预定义的系统目录中搜索头文件,这种路径搜索方式适用于标准库和第三方库的头文件。

  2. #include"a.h":编译器会在当前源文件所在目录和自定义目录中搜索头文件。

总结来说,#include<a.h>查找系统目录,而#include"a.h"查找当前目录和自定义目录。


相关推荐
Ljwuhe1 小时前
C++类与对象(上)
开发语言·c++
十启树1 小时前
QGis开发环境部署
开发语言·gis·qgis
killer_queen48041 小时前
AI_agent(三) MCP协议(二)
c++·agent·mcp·a2a
亚比囧2 小时前
Java基础--面向对象(二)
java·开发语言
乐观勇敢坚强的老彭2 小时前
c++寒假营day05
开发语言·c++·算法
枫叶丹42 小时前
【Qt开发】Qt界面优化(七)-> Qt样式表(QSS) 样式属性
c语言·开发语言·c++·qt
重生之后端学习2 小时前
74. 搜索二维矩阵
开发语言·数据结构·算法·职场和发展·深度优先
@atweiwei2 小时前
rust所有权机制详解
开发语言·数据结构·后端·rust·内存·所有权
上海云盾-高防顾问2 小时前
DNS异常怎么办?快速排查+解决指南
开发语言·php