C++特性,四类cast转换、智能指针、try—catch、auto关键字、decltype关键字

本文介绍了C++中的四种类型转换static_cast、dynamic_cast、const_cast和reinterpret_cast的区别和应用,详细解析了智能指针auto_ptr、unique_ptr、shared_ptr和weak_ptr的工作原理及其使用场景,并探讨了try-catch异常处理机制。

四类cast转换

static_cast静态类型转换:

1、void* 转换为任意类型的指针

2、任意类型的指针转换为void*

3、编译器允许的跨类型转换,比如char类型转换为int类型,double转int型

4、做基类与派生类的转换,派生类转换成基类是安全的,基类转换成派生类是不安全的,因为往往子类的内容比父类多。static_cast不提供安全性检查

int n = 10;
char c = 'a';
void *p = nullptr;
int *k = static_cast<int*>(p);
n = static_cast<int>(c); //n就变成了97,ASCLL码中字符a的位置,    相当于int n = (int)c;

dynamic_cast动态类型转换:

主要用于面向对象中多态应用场景,用于基类指针和派生类指针或者基类引用和派生类引用的安全转换,提供动态的安全检查。**派生类转换成基类是安全的,基类转换成派生类是不安全的,因为往往子类的内容比父类多。**dynamic_cast提供安全检查。

const_cast:

const_cast 用于去除 const(volatile)属性,将只读变为可读写,const_cast只针对指针、引用和this指针

既然都设为const,为什么还要用const_cast强行转回去?

可能调用了一个参数不是const的函数,而我们要传进去的实际参数确实const的,但是我们知道这个函数是不会对参数做修改的。于是我们就需要使用const_cast去除const限定,以便函数能够接受这个实际参数。

reinterpret_cast

暴力完成两个完全无关类型的指针之间或指针和数之间的互转,比如用char类型指针指向double值。它对原始对象的位模式提供较低层次上的重新解释(即reinterpret),完全复制二进制比特位到目标对象,转换后的值与原始对象无关但比特位一致,前后无精度损失。

即:简单的完成比特位复制,按照不同类型的读法会变成不同的值,但是比特位是一致的。

智能指针

为什么要引入智能指针:

程序员自己管理堆内存可以提高了程序的效率,但堆内存的管理是麻烦的,经常会遇到一些问题

  • 悬空指针被使用:有些内存资源已经被释放,但指向它的指针并没有改变指向,并且后续还在使用;

  • 二次释放:有些内存资源已经被释放,后期又试图再释放一次(重复释放同一块内存会导致程序运行崩溃);

  • 堆内存泄露(忘记释放):没有及时释放不再使用的内存资源造成内存泄漏,程序占用的内存资源越来越多;程序发生异常时内存泄露。

C++中有四个智能指针:auto_ptr, unique_ptr, shared_ptr, weak_ptr ,其中后三个是 C++11 支持的智能指针,第一个已经被C++11弃用。

智能指针原理

智能指针是一个类,用来存储指向动态分配对象的指针(堆内存),该类负责自动释放动态分配的对象,防止堆内存泄漏。动态分配的资源,交给一个类对象去管理,当类对象声明周期结束时,自动调用析构函数释放资源。

unique_ptr原理

unique_ptr 是一个独占型的智能指针(不允许其他的智能指针共享其内部的指针), unique_ptr 对其持有的堆内存具有唯一拥有权,也就是说引用计数永远是 1 。因此一个非空的 unique_ptr 总是拥有它所指向的资源(保证同⼀时间内只有⼀个智能指针可以指向该对象),如果其离开作⽤域时,其指向对象将象将被销毁。

基本用法

在这里插入图片描述

初始化

通过构造函数、std::unique_ptr 辅助函数和 reset 方法来初始化 unique_ptr :

// 初始化方式1
unique_ptr<T> my_ptr(new T); // 正确 
unique_ptr<T> ptr = my_ptr; // 报错,不能拷贝

// 初始化方式2
std::unique_ptr<int> sp2;
sp2.reset(new int(123));

// 初始化方式3
unique_ptr<T> my_other_ptr = std::move(my_ptr); // 正确 

// 初始化方式4
auto upw1(std::make_unique<Widget>());//相当于std::unique_ptr<Widget> upw2(new Widget); 

shared_ptr原理

shared_ptr 实现共享式拥有概念,即共享被管理对象:同一时刻可以有多个 shared_ptr 拥有动态分配的对象的所有权,当最后一个 shared_ptr 对象销毁时,被管理对象自动销毁。shared_ptr 是为了解决 unique_ptr(具体介绍见 unique_ptr 章节)在对象所有权上是独占的局限性,在使⽤引⽤计数的机制上提供了可以共享所有权的智能指针。

shared_ptr 采用引用计数的方法:允许多个智能指针指向同一个对象,每当多一个智能指针指向该对象时,指向该对象的所有智能指针内部的引用计数加1,每当减少一个智能指针指向该对象时,相应的引用计数会减1,当计数为0的时会自动的释放动态分配的资源。 多个线程之间,递增和减少资源的引用计数是安全的。

weak_ptr 原理

share_ptr 虽然已经很好用了,但是有一点 share_ptr 智能指针还是有内存泄露的情况:例如当两个 shared_ptr 相互引用时,析构时两个资源时引⽤计数会减⼀,但是两者引⽤计数还是为1,永远不可能下降为0,也就是资源永远不会释放,从而导致内存泄漏。

weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象(弱引用)。进行该对象的内存管理的是那个强引用的 shared_ptr, weak_ptr 只是提供了对管理对象的一个访问手段(也就是说它只引用不计数)。weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。

如果一块内存被 shared_ptr 和 weak_ptr 同时引用,当所有 shared_ptr 析构了之后,不管还有没有 weak_ptr 引用该内存,内存也会被释放。所以 weak_ptr 不保证它指向的内存一定是有效的,在使用之前使用函数 lock() 或 expired() 检查 weak_ptr 是否为空指针。

try——catch

try{
...
}
catch{
...
}

try语句块是用来判断是否有异常;

catch语句块捕捉异常,并进行处理;

throw是抛出异常;

#include <stdlib.h>
#include <iostream>
using namespace std;
 
double fuc(double x, double y)                        //定义函数
{
    if(y==0)
    {
        throw y;                                    //除数为0,抛出异常
    }
    return x/y;                                    //否则返回两个数的商
}
 
int main()
{
    double res;
    try                                            //定义异常
    {
        res=fuc(2,3);
        cout<<"The result of x/y is : "<<res<<endl;
        res=fuc(4,0);                                //出现异常
    }
    catch(double)                                    //捕获并处理异常
    {
        cout<<"error of dividing zero.\n";
        exit(1);                                    //异常退出程序
    }
    return 0;
}

catch 的数据类型需要与throw出来的数据类型相匹配的。

catch(…)能够捕获多种数据类型的异常对象,所以它提供给程序员一种对异常对象更好的控制手段,使开发的软件系统有很好的可靠性。因此一个比较有经验的程序员通常会这样组织编写它的代码模块,如下:

void Func()
{
  try
  {
    // 这里的程序代码完成真正复杂的计算工作,这些代码在执行过程中
    // 有可能抛出DataType1、DataType2和DataType3类型的异常对象。
  }
  catch(DataType1& d1)
  {
  }
  catch(DataType2& d2)
  {
  }
  catch(DataType3& d3)
  {
  }
  /*********************************************************
  	防止有未预料到的异常,所以在最后增加一个无参的catch
  *********************************************************/
  catch(…)
  {
  }
}

auto关键字

性质
1、 auto可以在声明变量的时候根据变量初始值的类型自动为此变量选择匹配的类型,类似的关键字还有decltype
2、 auto的自动类型推断发生在编译期,所以使用auto并不会造成程序运行时效率的降低
3、与其他变量区别:
1、auto声明的变量必须要初始化,否则编译器不能判断变量的类型。
2、auto不能被声明为返回值,auto不能作为形参,auto不能被修饰为模板参数

decltype关键字

decltype被称作类型说明符,它的作用是选择并返回操作数的数据类型。

主要用法:

  1. decltype + 变量 var
  2. decltype + 表达式 expr
  3. decltype + 函数名 func_name

①当使用decltype(var)的形式时,decltype会直接返回变量的类型(包括顶层const和引用),不会返回变量作为表达式的类型。

②当使用decltype(expr)的形式时,decltype会返回表达式结果对应的类型。

③当使用decltype(func_name)的形式时,decltype会返回对应的函数类型,不会自动转换成相应的函数指针。

// sum的类型就是函数f返回的类型
decltype(f()) sum = x;

decltype是为了解决复杂的类型声明而使用的关键字,称作decltype类型说明符。

decltype可以作用于变量、表达式及函数名。①作用于变量直接得到变量的类型;②作用于表达式,结果是左值的表达式得到类型的引用,结果是右值的表达式得到类型;③作用于函数名会得到函数类型,不会自动转换成指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

allwellright

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值