C++常见概念问题(2)

server/2024/9/24 9:25:49/

C++常见概念问题(2)

C++中异常处理

➢ 异常处理过程:
在执行程序发生异常,可以不在本函数中处理,而是抛出一个错误信
息,把它传递给上一级的函数来解决,上一级解决不了,再传给其上一级,
由其上一级处理。如此逐级上传,直到最高一级还无法处理的话,运行系统
会自动调用系统函数 terminate,由它调用 abort终止程序。这样的异常处
理方法使得异常引发和处理机制分离,而不在同一个函数中处理。这使得底
层函数只需要解决实际的任务,而不必过多考虑对异常的处理,而把异常处
理的任务交给上一层函数去处理。
➢ 异常处理机制的组成:try(检查),throw(抛出),catch(捕获)。
把需要检查的语句放在 try 模块中,检查语句发生错误,throw 抛出异
常,发出错误信息,由 catch 来捕获异常信息,并加以处理。一般 throw
抛出的异常要和 catch 所捕获的异常类型所匹配。

右值引用,移动语义,完美转发

右值(Rvalue):表示一个临时的、不能被取地址的值,通常是一个字面量、临时对象或表达式的结果。右值只能出现在赋值语句的右边。
右值引用:右值引用通过&&符号表示,它主要用于绑定到临时对象(即右值),从而允许程序员更高效地管理资源。

为什么要右值引用

  1. 如果只能左值引用,要使用一个对象的副本时,需要进行深拷贝,需要资源复制开销较大;右值引用允许直接绑定到临时对象(右值),这样就可以定义特殊的构造函数和赋值运算符,这些构造函数和赋值运算符可以“移动”而非复制资源。这种操作通常称为移动构造和移动赋值。移动操作通常比复制操作要快,因为它们可以避免不必要的资源复制,例如,直接传递动态内存的指针而不是复制整个数组。

移动语义

当编译器看到&&的时候会判定为右值引用,对编译器来说这是一个临时变量的标识,对于临时变量来说我们仅仅做一次浅拷贝就行,不需要深拷贝,从而解决了前面提到的临时变量拷贝构造产生的性能损失的问题。这就是所谓的移动语义,右值引用的一个重要作用是用来支持移动语义的。

完美转发

右值引用的另一个用途是实现完美转发。完美转发允许函数模板接收任意类型的参数(包括左值和右值),并将参数转发给另一个函数,同时保持参数的原始属性(左值或右值)。

这在编写通用的库函数时非常有用,因为它允许函数透明地转发参数,使得库函数的用户不必关心参数是左值还是右值。

例如:

template<typename T>
void wrapper(T&& arg) {process(arg);  // 这里直接传递会导致错误的左值/右值属性process(std::forward<T>(arg));  // 正确的完美转发
}

在这个例子中,forwardValue使用右值引用和std::forward来完美转发其参数给processValue函数。

explicit 关键字有什么用

explicit 关键字在 C++ 中用于控制构造函数和转换函数的隐式调用。它的主要作用是防止编译器进行隐式类型转换,从而提高代码的安全性和可读性。

有了malloc/free为什么还要new/delete?

mallocfree是C语言标准库中的函数,用于动态内存管理。newdelete是C++语言中的运算符,它们同样用于动态内存管理,但与mallocfree相比,它们提供了一些额外的特性和优势:

  1. 类型安全

    • new可以自动进行类型检查,确保分配的内存用于正确的类型。如果尝试分配一个错误的类型,编译器会报错。
    • malloc不进行类型检查,因此如果传递给malloc的指针类型不正确,可能会导致运行时错误。
  2. 异常处理

    • 在C++中,new可以抛出异常,如果内存分配失败,可以通过std::bad_alloc异常来处理。这使得内存分配失败时的错误处理更加灵活。
    • malloc在分配失败时返回NULL,调用者需要检查返回值来处理错误。
  3. 构造函数调用

    • 使用new分配内存时,会自动调用对象的构造函数来初始化新分配的对象。
    • malloc只分配内存,不会调用任何构造函数。
  4. 析构函数调用

    • 当使用delete删除对象时,会自动调用对象的析构函数来释放对象资源。
    • free仅释放内存,不会调用析构函数。
  5. 引用计数

    • 在C++中,使用new[]delete[]进行数组内存管理时,会自动处理引用计数,防止内存泄漏。
    • mallocfree不提供这种机制,需要手动管理数组的生命周期。
  6. 兼容性

    • newdelete是C++的一部分,它们在C++程序中提供了一致的内存管理模型。
    • mallocfree是C语言的一部分,在C和C++程序中都可以使用,但它们不是C++类型系统的一部分。
  7. 智能指针

    • C++中的智能指针(如std::unique_ptr, std::shared_ptr等)使用newdelete进行内存管理,提供了更高级的内存管理功能,如自动资源管理、异常安全等。

移动构造函数

**移动构造函数:**有时候我们会遇到这样一种情况,我们用对象a初始化对象b后对象a我们就不在使用了,但是对象a的空间还在呀(在析构之前),既然拷贝构造函数,实际上就是把a对象的内容复制一份到b中,那么为什么我们不能直接使用a的空间呢?这样就避免了新的空间的分配,大大降低了构造的成本。这就是移动构造函数设计的初衷。拷贝构造函数中,对于指针,我们一定要采用深层复制,而移动构造函数中,对于指针,我们采用浅层复制。
但是指针的浅层复制是非常危险的。浅层复制之所以危险,是因为两个指针共同指向一片内存空间,若第一个指针将其释放,另一个指针的指向就不合法了(pointer dangling)。所以我们只要避免第一个指针释放空间就可以了。避免的方法就是将第一个指针(比如a->value)置为NULL,这样在调用析构函数的时候,由于有判断是否为NULL的语句,所以析构a的时候并不会回收a->value指向的空间(同时也是b->value指向的空间)

为什么拷贝构造函数必须是引用?

为了防止递归调用。

如果不用引用,就会是值传递的方式,但是值传递会调用拷贝构造函数生成临时对象,从而又调用一次拷贝构造函数。就这样无穷的递归下去。

如果是指针类型

就变成了一个带参数的构造函数了。。。

比如A(A* test)

被free回收的内存是立即返还给操作系统吗

被free回收的内存会首先被ptmalloc使用双链表保存起来,当用户下一次申请内存的时候,会尝试从这些内存中寻找合适的返回。这样就避免了频繁的系统调用,占用过多的系统资源。同时ptmalloc也会尝试对小块内存进行合并,避免过多的内存碎片

向上类型转换和向下类型转换

**向上类型转换:**upcast,把派生类的指针或引用转换成基类的指针或者引用是安全的(或者说基类指针指向派生类);因为用转换后的指针只能访问基类部分的函数时候,都可以访问,肯定安全。这个是隐式转换,c++认为是安全的。

**向下类型转换:**downcast,把基类指针或引用转换成派生类表示(或者说把派生类指针指向基类)时,由于没有动态类型检查,所以是不安全的。如果转换后的指针访问派生类新增加的函数,这个时候基类中本来没有这个函数,那就会出错。

很形象,向上就是派生类指针变成基类指针,向下就是基类指针变成派生类指针。

内存泄漏?出现内存泄漏如何调试?

内存泄露一般指的是堆内存的泄露,即用户自己开辟的内存空间。应用程序使用malloc、realloc、new等函数从堆中分配到一块内存后,必须调用free或delete进行回收,否则这块内存不能继续被使用。内存泄漏会因为减少可用内存的数量从而降低计算机的性能。最终,在最糟糕的情况下,过多的可用内存被分配掉导致全部或部分设备停止正常工作,或者应用程序崩溃。内存泄漏可能不严重,甚至能够被常规的手段检测出来。在现代操作系统中,一个应用程序使用的常规内存在程序终止时被释放。这表示一个短暂运行的应用程序中的内存泄漏不会导致严重后果。在C++中出现内存泄露的主要原因就是程序猿在申请了内存后(malloc(), new),没有及时释放没用的内存空间,甚至消灭了指针导致该区域内存空间根本无法释放。

内存泄漏的原因

  1. malloc/new和delete/free没有匹配
  2. new[] 和 delete[]也没有匹配
  3. 没有将父类的析构函数定义为虚函数,当父类的指针指向子类对象时,delete该对象不会调用子类的析构函数

检测手段

  1. 良好的程序设计能力,把new和delete全部都封装到构造函数和析构函数中,保证任何资源的释放都在析构函数中进行
  2. 智能指针(万一被问道,这也是一个问题)
  3. valgrind ,这个可以打印出发生内存泄露的部分代码
  4. linux使用swap命令观察还有多少可以用的交换空间,两分钟内执行三四次,肉眼看看交换区是不是变小了
  5. 使用/usr/bin/stat工具如netstat、vmstat等。如果发现波段有内存被分配且没有释放,有可能进程出现了内存泄漏。

http://www.ppmy.cn/server/121280.html

相关文章

初试AngularJS前端框架

文章目录 一、框架概述二、实例演示&#xff08;一&#xff09;创建网页&#xff08;二&#xff09;编写代码&#xff08;三&#xff09;浏览网页&#xff08;四&#xff09;运行结果 三、实战小结 一、框架概述 AngularJS 是一个由 Google 维护的开源前端 JavaScript 框架&am…

程序人生-2024我的个人总结

可能现在写个人总结比较早&#xff0c;但是眼看着还有三个月&#xff0c;今年就过去了&#xff0c;所以决定提前写写&#xff0c;今年对于我来说是不平凡的一年&#xff0c;先是加薪&#xff0c;之后求婚&#xff0c;以为快要走上人生巅峰的时候&#xff0c;被裁员&#xff0c;…

重修设计模式-结构型-适配器模式

重修设计模式-结构型-适配器模式 将不兼容的接口转换为可兼容的接口&#xff0c;让原本由于接口不兼容而不能一起工作的类可以一起工作 适配器模式&#xff08;Adapter Pattern&#xff09;允许将一个类的接口变换成客户端所期待的另一种接口&#xff0c;从而使原本因接口不匹配…

中秋节特别游戏:给玉兔投喂月饼

&#x1f5bc;️ 效果展示 &#x1f4dc; 游戏背景 在中秋这个充满诗意的节日里&#xff0c;玉兔因为贪玩被赶下人间。在这个温柔的夜晚&#xff0c;我们希望通过一个小游戏&#xff0c;让玉兔感受到人间的温暖和关怀。&#x1f430;&#x1f319; &#x1f3ae; 游戏设计 人…

后端开发刷题 | 没有重复项数字的全排列

描述 给出一组数字&#xff0c;返回该组数字的所有排列 例如&#xff1a; [1,2,3]的所有排列如下 [1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2], [3,2,1]. &#xff08;以数字在数组中的位置靠前为优先级&#xff0c;按字典序排列输出。&#xff09; 数据范围&#xff1a;数字…

【小米手机无法连接电脑】一般问题和驱动MTP问题的结局ue

一般 那一般就需要换个电脑。看一看自己的手机是不是能连得上。如果连得上&#xff0c;就拿刚才的那根线连到你的电脑上&#xff0c;并且换一个电脑的接口&#xff0c;再试试。 如果连不上&#xff0c;换一下原装的数据线再试试。 如果还是不行&#xff0c;需要把自己的手机打…

直接在tomcat下面访问jsp

复制一份tomcat为tomcat-8.5.99test 记住修改tomcat-8.5.99test下面bin/startup.sh&#xff08;Linux/Mac&#xff09;或 bin/startup.bat&#xff08;Windows&#xff09; 在 Linux/Mac export CATALINA_BASE/path/to/tomcat1 $CATALINA_HOME/bin/startup.sh 在 Windows: …

【通信基础】精讲通信天线种类及CAN总线和集群关系

前言 在通信行业中&#xff0c;天线的种类非常多&#xff0c;涵盖了从简单的无线电天线到复杂的相控阵天线。这些天线的种类和形态各异&#xff0c;以满足不同频率、应用场景和通信需求。以下是一些主要的天线种类&#xff1a; 1. 通信天线种类 1.1 偶极子天线 (Dipole Ant…