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

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等。如果发现波段有内存被分配且没有释放,有可能进程出现了内存泄漏。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值