c++第十六天(异常处理与c++11新特性)

22 篇文章 2 订阅
6 篇文章 0 订阅

目录

一、异常处理

异常处理的作用: 异常处理分离了接收和处理错误代码。为了替代error。

C++ 异常处理涉及到三个关键字:try、catch、throw。

1、throw: 当问题出现时,程序会抛出一个异常。通过使用 throw 关键字来完成。

格式:throw  错误代码。

2、try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。

格式:try { 测试代码 } 

3、catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。

格式:catch(错误代码类型  变量){ }

4、如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。

错误原因

二、c++11新特性

1、auto变量

2、枚举循环

3、final & override

4、随机数功能(比rand随机一点)

5、智能指针 unique_ptr 与 shared_ptr

区别:如果是定义一个普通指针指向堆空间,则需要手动释放该指针才能调用析构函数,因为该指针的生命周期是delete。如果是智能指针,则不需要手动释放该指针也可以调用析构函数。

shared_ptr 例子:(共享型)

补充:自定义删除器

6、类型转换

7、lambda表达式(QT上的槽会使用)

总结:


一、异常处理

异常处理的作用: 异常处理分离了接收和处理错误代码。为了替代error。

C++ 异常处理涉及到三个关键字:try、catch、throw。

1、throw: 当问题出现时,程序会抛出一个异常。通过使用 throw 关键字来完成。

格式:throw  错误代码。

2、try: try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块。

格式:try { 测试代码 } 

3、catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。

格式:catch(错误代码类型  变量){ }

4、如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。

try   //测试代码 
{
   //发现异常   throw 抛出异常 
    
}catch( ExceptionName e1 )  //catch 捕抓异常
{
   // catch 块
} 

demo

//设计一个字符串类,当用户访问下标越界时,抛出异常
#include<iostream>
using namespace std;
int main()
{
     try{
        string s = "hello";
   
        int i=0;
        while(1){
            if(s[i]=='\0'){
                throw 101; 
                break;  
            }
            i++;
        } 
    }
    catch(int e)
    {
        cout << "下标越界" << e << endl;
    }
    catch(...) //可以接收任何的数据类型
    {
        cout << "下标越界" << endl;
    }
}

可以在任意的地方抛出异常,当前抛出的异常未被处理时,程序会死亡。

标准错误格式
 try
 {
     测试代码
 }
catch(const std::exception& e)  //捕捉系统的所有异常。 
{
    std::cerr << e.what() << '\n';
}

错误原因

 

 demo2

#include <iostream>

using namespace std;

int main()
{
      
 //int  *p = new  int[1000000000000000000]; 
    try{ 
        //分配的空间过大,会抛出系统异常 
        int  *p = new  int[1000000000000000000]; 
    }catch(const std::exception& e){  //处理系统异常 
    
        //输出异常的原因
        std::cerr << e.what() << '\n';
    }
    cout << "程序继续运行" << endl;
}

二、c++11新特性

1、auto变量

作用:自动推导数据类型,方便使用。但在定义的时候要初始化。

auto   a = 10086;  
auto   b = "hello world";   
作用:自动推导变量的类型,一定要有数值才能推导。
auto c; //错误,没有数据值,无法推导

补充: decltype 分析变量的类型 
int i = 1;
decltype(i) b = 2; // b是int    decltype(i) 就是 int 

2、枚举循环

例子:

#include <iostream>
using namespace std;

int main(int argc, char const *argv[])
{
    int a[3] = {1,2,3};
     枚举数组a中的各个元素,并赋值给 i  
    for(int i:a){
        cout << i << endl;
    }
    return 0;
}

demo 

#include<iostream>
#include<list>
using namespace std;

int main()
{
    list<string> ls;
    ls.push_back("小明");
    ls.push_back("小红");
    ls.push_back("小芳");
    ls.push_back("小贝");

    //枚举格式:for(数据类型 对象 : 容器对象) { }
    ls.sort();    //排序函数
    for(string vet:ls){     //枚举
        cout << vet << endl;
    }

    cout << "--------翻转-----------\n";
    ls.reverse();    //翻转函数
    for(string vet:ls){
        cout << vet << endl;
    }
}

3、final & override

(1)final 用于修饰一个类,表示禁止该类进一步派生和虚函数的进一步重载,即禁止派生和虚函数重载。

final:类不能被继承
    
class Base final {
    virtual void func() {
        cout << "base" << endl;
    }
};

class Derived : public Base{ // 编译失败,final修饰的类不可以被继承
    void func() override {
        cout << "derived" << endl;
    }

};

(2)override 用于修饰派生类中的成员函数,标明该函数重写了基类函数,如果一个函数声明了override但父类却没有这个虚函数,编译报错,使用override关键字可以避免开发者在重写基类函数时无意产生的错误。即子类使用了父类没有的虚函数,则报错。

override 例子声明函数是重写函数

class Base {
    virtual void func() {
        cout << "base" << endl;
    }
};

class Derived : public Base{
    void func() override { // 确保func被重写
        cout << "derived" << endl;
    }
    void fu() override { // error,基类没有fu(),不可以被重写
        
    }
};

(3)explicit 禁止隐式转换

#include <iostream>
#include <list>
using  namespace std;

class A {
    A(int value) { // 没有explicit关键字,可以隐式转换
        cout << "value" <<value    << endl;
    }
   explicit A(string value) { // explicit关键字,不能隐式转换 
        cout << "value"<< value << endl;
    }
};

int main() {

   A a = 1; // 可以隐式转换,调用 A(int value)  
   string  s="hello";
   A b = s;
   return 0;

}

4、随机数功能(比rand随机一点)

#include <time.h>
#include <iostream>
#include <random>
using namespace std;

int main() {
    //产生随机因子
    std::default_random_engine random(time(nullptr));
    //设置随机范围
    std::uniform_int_distribution<int> int_dis(0, 100); // 整数均匀分布
    std::uniform_real_distribution<float> real_dis(0.0, 1.0); // 浮点数均匀分布

    for (int i = 0; i < 10; ++i) {
        //输出随机数
        cout << int_dis(random) << ' ';
    }
    cout << endl;

    for (int i = 0; i < 10; ++i) {
        cout << real_dis(random) << ' ';
    }
    cout << endl;

    return 0;
}

5、智能指针 unique_ptr 与 shared_ptr

作用:自动释放所指向的堆空间

(1)unique_ptr 这是个独占式的指针对象,在任何时间、资源只能被一个指针占有,当

unique_ptr离开作用域,指针所包含的内容会被释放。(只能一个指针指向该空间)。

(2)shared_ptr使用了引用计数,每一个shared_ptr的拷贝都指向相同的内存,每次拷贝都会

触发引用计数+1,每次生命周期结束析构的时候引用计数-1,在最后一个shared_ptr析构的

时候,内存才会释放。(可以多个指针指向该空间)。

(3)语法: 
  unique_ptr<数据类型>  变量名(地址); 

  unique_ptr<数据类型>  变量名 = make_unique<类型>(参数);  

  shared_ptr<数据类型>  变量名(地址); 

  shared_ptr<数据类型>  变量名 = make_shared<类型>(参数); 

例子:定义一个智能指针 
unique_ptr<int> u_p(new int); 
unique_ptr<int> u_p1 = make_unique<int>(); 

shared_ptr<int> s_p(new int); 
shared_ptr<int>  s_p1 = make_unique<int>(); 

区别:如果是定义一个普通指针指向堆空间,则需要手动释放该指针才能调用析构函数,因为该指针的生命周期是delete。如果是智能指针,则不需要手动释放该指针也可以调用析构函数。

unique_ptr 例子:(独占型) 

//头文件
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex> 
using namespace std;

class A {
public:
   A () {cout << "A init"  << endl;}
   ~A() {
       cout << "A delete" << endl;
   }
};
//测试 
void  test(){
   cout << "test" << endl;
    //使用普通指针
   A *p =  new A;
}

void test1()
{
    cout << "test1" << endl;
    //使用智能指针
    unique_ptr <A> p (new A);  //当test1 函数结束时会自动释放 p 所指向的堆空间
    //定义一个智能指针q指向 p 
    unique_ptr <A> q = p; //错误的unique_ptr 是唯一的智能指针 
}

int main() {
 
    test(); //test 函数结束,普通指针不会自动释放 
    test1(); //test 函数结束,智能指针会自动释放 
    return 0;
}

shared_ptr 例子:(共享型)

//头文件
#include <iostream>
#include <memory>
#include <thread>
#include <chrono>
#include <mutex> 

using namespace std;

class A {
public:
    A() { cout << "A init"  << endl; }
   ~A() {
       cout << "A delete" << endl;
   }
};
//测试 
void test(){
   cout << "test" << endl;
    //使用普通指针
   A *p = new A;
}

void test1()
{
    cout << "test1" << endl;
    //使用智能指针
    shared_ptr <A> p(new A);  //当test1 函数结束时会自动释放 p 所指向的堆空间

    cout << p.get()  << endl;  //返回指针所指向的地址 

    cout << p.use_count() << endl;//返回指针的记数
    //定义一个智能指针q指向 p 
    shared_ptr<A> q=p; //shared_ptr不是独占可以不断叠加

    cout << p.get()  << endl;  //返回指针所指向的地址
 
    cout << p.use_count() << endl;//返回指针的记数

}

int main() 
{
    test(); 
    test1();
    //自定义删除器
    std::shared_ptr<int> ptr(new int, [](int *p){ cout << "delete"<<p<<endl; delete p; });

    //打印指针所指向的地址 
    cout << ptr.get() << endl; 
   return 0;
}

补充:自定义删除器

std::shared_ptr <int> ptr( new int, [] (int *p) { 
    delete p; } );

(1)不要用普通指针初始化多个智能指针,会出现 double_free. 
   // int *q =  new  int; .
    shared_ptr<int> q(new int);
    shared_ptr<int> p(q);
    shared_ptr<int> pp(q);

(2)尽量使用make_shared,少用new。

(3)不要delete get() 返回来的裸指针。

(4)不是new出来的空间要自定义删除器。

6、类型转换

旧式类型转换: 
(数据类型)变量名; // C语言风格类型转换 

新式转换:
const_cast<数据类型>(变量名);
static_cast<数据类型>(变量名);
dynamic_cast<数据类型>(变量名);

新式转换是C++的全新特性,这几个转换关键字含义如下:
const_cast : 专用于去除指针或引用的 const 属性
              1.const_cast 只能作用于指针或引用类型 
    
static_cast : 与旧式转换相近,但提供了更易于查找的语法,并能有效识别不兼容类型。
      		  1.主要用于内置数据类型之间的相互转换。如:char ,short , int , ..... 
          
dynamic_cast : 专用于父子类的类型转换
    		  1.将基类的指针或引用安全地转换成派生类的指针或引用,但是基类必须有虚函数

 reinterpret_cast

功能:与C语言一样任意转换.

reinterpret_cast <type> (expression)

非常激进的指针类型转换,在编译期完成,可以转换任何类型的指针,所以极不安全。非极端情况不要使用。

int *ip;
char *pc = reinterpret_cast<char*>(ip);

7、lambda表达式(QT上的槽会使用)

语法:
auto func = [capture] (params) opt -> ret 
            { func_body; };

func是可以当作lambda表达式的名字,作为一个函数使用
capture是捕获列表
params是参数表
opt是函数选项(mutable之类)
ret是返回值类型
func_body是函数体。
  
    
capture是捕获列表:
[]不捕获任何变量
[&]引用捕获,捕获外部作用域所有变量,在函数体内当作引用使用,可以修改值
[=]值捕获,捕获外部作用域所有变量,在函数内内有个副本使用  ,不可以修改值
[=, &a]值捕获外部作用域所有变量,按引用捕获a变量
[a]只值捕获a变量,不捕获其它变量
[this]捕获当前类中的this指针
    
opt选择:
int a = 0;
auto f1 = [=](){ return a; };    值捕获a
cout << f1() << endl;
auto f2 = [=]() { return a++; };          修改按值捕获的外部变量,error
auto f3 = [=]() mutable { return a++; };  添加mutable 选项可以修改

lambda 表达式的应用

#include <iostream>
#include <list>
using  namespace std;

int main() {
    list<int> vec;
    vec.push_back(10);
    vec.push_back(45);
    vec.push_back(4);
    vec.push_back(48);
    vec.sort(); //排序
    //枚举
    for(int i:vec){
        cout << i << endl;
    }
	//自定义排序的规则 
    vec.sort( [] ( int a,int b ) { return a>b; } );
    //枚举
    for(int i:vec){
        cout << i << endl;
    }
}

demo

//利用lambada表达式实现两个数相加
#include<iostream>
using namespace std;

int main()
{
    int a=10;
    int b=20;
    int sum;
方法1,利用capture捕获
    [&a,&b,&sum](){  //或者直接[&]
        sum = a+b; 
    }();    //()调用函数
    cout << sum << endl;
方法1.1,用mutable修改值
    [=]() mutable{
        a = 22;
        b = 33;
        cout << a+b << endl;
    }();

方法2,利用opt选择
    auto f1 = [](int a1,int b1){    //(里面是参数列表)
        int sum1 = a1 + b1;
        return sum1;          //返回
    };
    cout << f1(11,22) << endl;  //像普通函数一样传参
方法2.1 外部调用
    int x=1,y=2;
    auto f2 = [&x,&y](){
        x = 10;
        y = 20;
        return x+y;
    };
    cout << f2() << endl;
方法2.2 (传参)
    [](int x1,int y1){
        x1 = 10;
        y1 = 20;
        return x1+y1;
    }(x,y);

}

总结:

1、c++的异常处理是为了提高程序员工作效率,它是先把错误放在后面解决,先完成一个个模块,因为c++有良好的封装性,一个功能有问题不会影响其他功能的实现,因此比error好。

2、c++11的新特性采用了一些新的方法,如auto,在使用迭代器的时候就不用输入很长的数据类型,它能自动转换,提高效率。如枚举循环,以前是python等脚本编程才有的特性,在2011年c++推出,减少程序员代码量。

3、lambda表达式Lambda表达式就是对函数式接口中抽象方法的实现,是对其匿名内部类的一个简写,只保留了方法的参数列表和方法体,其他的成分可以省略。不用想函数名,提高执行效率。

4、智能指针的使用方便了程序员,new完之后不用担心内存泄露,造成错误。随机数的功能适用游戏中,比较随机。final & override 提前把需要的写好,避免造成编译错误。

c++的内容基本就是这些,进阶的话要写一些小游戏和项目,下一阶段是QT,未完待续...

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值