(黑马C++)L09 C++类型转换 异常 输入输出流

news2024/11/29 8:42:55

一、C++类型转换

类型转换(cast)是将一种数据类型转换成另一种数据类型,一般情况下要尽量少的去使用类型转换,除非解决非常特殊的问题。

(1)静态转换(static_cast)

static_cast使用方式:static_cast<目标类型>(原始对象);

  • 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。
  • 没有父子关系的自定义类型不能进行转换。
#include <iostream>
using namespace std;

class Base{};
class Child : public Base{};

void test() {
    Base* base = NULL;
    Child* child = NULL;

    //把base转成child,向下类型转换,不安全
    Child* child2 = static_cast<Child*> (base);

    //把child转成base,向上类型转换,安全
    Base* base2 = static_cast<Base*> (child);
}


int main() {
    test();
    system("pause");
    return 0;
}
  • 用于基本数据类型之间的转换,如把int转换成char,或者把char转换成int。
#include <iostream>
using namespace std;

void test() {
    char a = 'a';
    double d = static_cast<double>(a);
    cout << "d = " << d <<endl;
}


int main() {
    test();
    system("pause");
    return 0;
}

(2)动态转换(dynamic_cast)

  • 动态转关非常严格,失去精度或者不安全都不可以转换。
  • 没有发生多态的情况下,可以用于子类转基类,但是不能用于基类转子类。
  • dynamic_cast如果发生了多态,可以让基类转为派生类,向下转换。
#include <iostream>
using namespace std;

class Base{
public:
    virtual void func() {};
};

class Child : public Base{
    virtual void func() {};
};

void test() {
    Base* base = NULL;
    Child* child = NULL;

    Base* base2 = new Child;
    Child* child = dynamic_cast<Child*> (base2);
}


int main() {
    test();
    system("pause");
    return 0;
}

(3)常量转换(const_cast)

用于修改类型的const属性。

  • 常量指针被转化为非常量指针,并且仍然指向原来的对象。
  • 常量引用被转换成非常量引用,并且仍然指向原来的对象。
  • 注意:不能直接对非指针和非引用的变量使用const_cast操作符直接移除它的const。
#include <iostream>
using namespace std;

void test() {
    //去除const
    const int * p = NULL;
    int * newp = const_cast<int*> (p);

    //加上const
    int * p2 = NULL;
    const int * newp2 = const_cast<const int*> (p2);
}


int main() {
    test();
    system("pause");
    return 0;
}

(4)重新解释转换(reinterpret_cast)

  • 最不安全的转换机制,最有可能出现问题,不推荐使用。
  • 主要用于将一种数据类型转换为另一种类型,可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针。

二、C++异常

(1)异常的基本使用

  • 异常处理就是处理程序中的错误,即程序运行过程中发生的一些异常事件(如:除0溢出,数组下标越界,读取的文件不存在。空指针,内存不足等)。
  • 在C语言中对错误的处理有两种方法:一是使用整型的返回值标识错误,二是使用errno宏,用不同的数字变量返回错误,该方法的局限是会出现不一致的问题,可能无法判断是返回值还是异常。
int myDevide(int a, int b) {
    if(b == 0) return -1;
    return a/b;
}
  • C++的异常机制:使用抛出异常和处理异常,抛出的异常必须进行处理,否则程序会出错。
  • 如果异常种类较多,都要进行处理,也可以使用以下方式:
catch(...) { //捕获异常
        cout << "其他类型异常捕获" << endl;
    }
#include <iostream>
using namespace std;

int myDevide(int a, int b) {
    if(b == 0) {
        throw -1; //抛出int类型的异常
    }
    return a/b;
}

void test() {
    int a = 10;
    int b = 0;
    try{
        myDevide(a, b);
    }
    catch(int) { //捕获异常
        cout << "int类型异常捕获" << endl;
    }
}

int main() {
    test();
    system("pause");
    return 0;
}
  • 异常处理可以在调用跳级,假设在有多个函数的调用栈中出现了某个错误,使用整型返回码要求在每一级函数中都进行处理,而使用异常处理的栈展开机制,只需要在一处进行处理就行,不需要每级函数都处理。
  • 以下代码会打印 double类型异常捕获,但是不想处理时可以继续向上抛出
  • 如果异常没有处理,会调用terminate函数,使程序中断
#include <iostream>
using namespace std;

int myDevide(int a, int b) {
    if(b == 0) {
        throw 3.14;
    }
    return a/b;
}

void test() {
    int a = 10;
    int b = 0;
    try{
        myDevide(a, b);
    }

    catch(int) { //捕获异常
        cout << "int类型异常捕获" << endl;
    }

    catch(double) { //捕获异常
        //如果不想处理这个异常需要向上抛出
        //throw; 此时会返回main函数中double类型异常捕获
        cout << "double类型异常捕获" << endl;
    }
}

int main() {
    try{
        test();
    }
    catch(double) {
        cout << "main函数中double类型异常捕获" << endl;
    }
    system("pause");
    return 0;
}

(2)对自定义异常进行捕获

可以抛出自定义对象,捕获自定义异常。

#include <iostream>
using namespace std;

//自定义异常
class myException {
public:
    void printError() {
        cout << "自定义异常" << endl;
    }
};

int myDevide(int a, int b) {
    if(b == 0) {
        throw myException(); //匿名对象
    }
    return a/b;
}

void test() {
    int a = 10;
    int b = 0;
    try{
        myDevide(a, b);
    }

    catch(myException e) { //捕获异常
       e.printError();
    }
}

int main() {
    test();
    system("pause");
    return 0;
}

(3)栈解璇

异常被抛出后,从进入try块起,到异常被抛出前,这期间在栈上构造的所有对象,都会被自动析构,析构的顺序与构造的顺序相反,这一过程称为栈的解璇。

(4)异常的接口声明

  • 为了加强程序的可读性,可以在函数声明中列出可能抛出异常的所有类型,例如:void func() throw(A, B, C) } {};这个函数func只能抛出类型A, B, C及其子类型的异常。
  • 如果函数声明中没有包含异常接口声明,则此函数可以抛出任何类型的异常。
  • 一个不抛出任何类型异常的函数可声明为void func() throw() {}。
  • 如果一个函数抛出了它的异常接口声明不允许抛出的异常,unexcepted函数会被调用,该函数默认调用terminate函数中断程序。

(5)异常变量生命周期

#include <iostream>
using namespace std;

class myException {
    myException() {
        cout << "默认构造函数调用" <<endl;
    }

    myException(const myException& e) {
        cout << "拷贝构造函数调用" <<endl;
    }

    ~myException() {
        cout << "析构函数调用" <<endl;
    }
};

void doWork() {
    throw myException();
}

void test() {
    try{
        doWork();
    }
    catch(myException e) {
        cout << "捕获异常" << endl;
    }
}

int main() {
    test();
    system("pause");
    return 0;
}

以上函数会输出以下结果,因为myException()调用默认构造,myException e调用拷贝构造,此时拷贝构造会多花费一份开销,所以一般写成myException &e,此时不会再调用拷贝构造

默认构造函数调用
拷贝构造函数调用
捕获异常
析构函数调用
析构函数调用

 当异常写成以下形式时,会先析构,执行结果为默认构造函数调用,析构函数调用,捕获异常。

#include <iostream>
using namespace std;

class myException {
    myException() {
        cout << "默认构造函数调用" <<endl;
    }

    myException(const myException& e) {
        cout << "拷贝构造函数调用" <<endl;
    }

    ~myException() {
        cout << "析构函数调用" <<endl;
    }
};

void doWork() {
    throw &myException();
}

void test() {
    try{
        doWork();
    }
    catch(myException* e) {
        cout << "捕获异常" << endl;
    }
}

int main() {
    test();
    system("pause");
    return 0;
}

(6)异常的多态使用

利用多态可以实现printError同一个接口调用,抛出不同的错误对象。

#include <iostream>
using namespace std;

//异常基类
class BaseException{
public:
    virtual void printError() {
        
    }
};

class NullPointerException : public BaseException {
public:
    virtual void printError() {
        cout << "空指针异常" << endl;
    }
};

void doWork() {
    throw NullPointerException();
}

void test() {
    try{
        doWork();
    }
    catch(BaseException& e) { //父类的引用调用子类的对象
        e.printError();
    }
}

int main() {
    test();
    system("pause");
    return 0;
}

(7)C++标准异常库

标准库中提供了很多的异常类,它们是通过类继承组织起来的,异常类继承层级结构图如下:

  •  在上述继承中,每个类都提供了构造函数,拷贝构造函数,析构函数和赋值运算符的重载;
  • logic_error类以及其子类、runtime_error类以及其子类,他们的构造函数接收一个string类型的参数,用于异常信息的描述;
  • 所有的异常都有一个what方法,返回 const char* 类型的值,描述异常信息。

 exception的直接派生类:

 logic_error的派生类:

 runtime_error 的派生类:

 以out_of_range异常为例,看一下它具体的使用方法,使用时需要包含头文件<stdexcept>

#include <iostream>
#include <string>
using namespace std;
//系统提供的异常
#include <stdexcept>

class Person {
public:
    Person(string name, int age) {
        this->m_Name = name;
        if(age < 0 || age > 200) {
            //抛出越界异常
            throw out_of_range("年龄越界!");
        }
        else {
            this->m_Age = age;
        }
    }

    string m_Name;
    int m_Age;
};

void test01() {
    try{
        Person p("张三", 300);
    }
    catch(out_of_range& e) {
        cout << e.what() << endl;
    }
}

int main() {
    test01();
    system("pause");
    return 0;
}

三、C++输入与输出流

程序的输入指的是从输入文件将数据传送给程序,程序的输出指的是从程序将数据传送给输出文件。

C++的输入输出包含三种:

标准I/O:从键盘输入数据,输出到显示器屏幕

文件I/O:以外存磁盘文件为对象进行输入和输出

串I/O:对内存中指定的空间进行输入和输出,通常指定字符数组作为存储空间进行信息存储

(1)标准输入流

  • 缓冲区:输入和输出的所有数据,都不是直接放在程序中,而是先放在缓冲区中,然后再从缓冲区中取出数据。
  • cin.get():一次读取一个字符,输入abc 输出a
void test01()
{
    char ch;
    ch = cin.get(); //输入as
    cout << "ch = " << ch << endl; //输出a

    ch = cin.get();
    cout << "ch = " << ch << endl; //输出s

    ch = cin.get();
    cout << "ch = " << ch << endl; //输出换行

    ch = cin.get();
    cout << "ch = " << ch << endl; //等待下一次输入
}
  • cin.get(一个参数):读一个字符,与上面效果一样
  • cin.get(两个参数):可以读字符串
void test() {
    char buf[1024];
    //cin >> buf;  //采用>>运算符输入字符窜,遇到空格就会停止
    //cout << buf << endl;
    
    cin.get(buf, 1024); //不会拿走换行,换行还在缓冲区
    cout << buf<< endl;
}
  • cin.getline():读取一行字符串
void test() {
    char buf[1024];
    cin.getline(buf, 1024); //会把换行符也读取并扔掉换行符
    cout << buf<< endl;
}
  • cin.ignore():默认忽略缓冲区的一个字符,带参数N,代表忽略N个字符
void test() {
    cin.ignore(); //输入as
    char c = cin.get();
    cout << c << endl; //输出s
}
  • cin.peek():从缓冲区查看一个字符,但不会从缓冲区取走
void test()
{
    char c = cin.peek();  //输入as
    cout <<"c = "<< c <<endl; //输出a

    ch = cin.get();
    cout << "c = " << c << endl; //输出a
}  
  • cin.pushback():将缓冲区取出的字符放回原位置
void test()
{
    char c = cin.get();  //输入helloworld  h被拿走
    cin.putback(c); //h被放回

    char buf[1024];
    cin.getline(buf, 1024); //读取字符串
    cout << "buf = " << buf << endl;
}
  • 标准输入流案例
  • (1)判断用户输入的是字符串还是数字
void test() {
    cout << "请输入一串数字或者字符串" << endl;
    //判断第一个字母(peek)
    char c = cin.peek();
    if(c >= '0' && c <= '9') {
        int num;
        cin >> num; //在缓冲区读取
        cout << "您输入的是数字,数字为:" << num <<endl;
    }
    else {
        char buf[1024];
        cin >> buf;
        cout << "您输入的是字符串,字符串为:" << buf <<endl;
    }
}
  • (2)让用户输入1-10的数字,如果输入有误,重新输入
void test() {
    int num;
    while(true) {
        cout << "请输入数字:" << endl;
        cin >> num; //如果输入的是char形,标志位会被修改
        if(num > 0 && num <= 10) {
            cout << "输入的数字为:" << num << endl;
            break;
        }
        else {
            cout << "对不起,请重新输入!" << endl;
            cin.clear(); //重置标志位
            cin.sync();  //清空缓冲区
            //cout <<"标志位:" << cin.fail() << endl; //标志位0正常 1不正常
        } 
    }
    
}

(2)标准输出流

  • cout.flush():刷新缓冲区,Linux下有效
  • cout.put():向缓冲区写字符
void test() {
    cout.push('a').put('b'); //输入ab
}
  • cout.write():从buffer中写num个字节到当前的输出流中
void test() {
    char buf[1024] = "helloworld";
    cout.write(buf, strlen(buf));
}
  •  格式化输出:使用控制符方法;使用流对象的有关成员函数
  • 使用成员函数
void test()
{
    int num = 99;
    cout.width(20);  //设置宽度,前面加18个空格
    cout.fill('*');  //设置填充,将空格填充为*
    cout.setf(ios::left); //左设置对齐  set format
    cout.unsetf(ios::dec); //卸载十进制
    cout.setf(ios::hex);  //安装十六进制
    cout.setf(ios::showbase);//显示进制基数,如果是十六进制,前面加上0x
    cout.unsetf(ios::hex);  //卸载十六进制
    cout.setf(ios::oct);  //安装八进制
    cout << num << endl;
}
  • 使用控制符(包含头文件iomanip)
void test03()
{
    int num = 99;
    cout << setw(20)                   //设置宽度
         << setfill('~')               //设置填充
         << setiosflags(ios::showbase) //显示进制基数
         << setiosflags(ios::left)     //左对齐
         << hex                        //安装16进制
         << num << endl;
}

(3)文件的读写操作

文件读写定义了三个类,分别是ofstream、ifstream、fstream。

  • 打开文件

用一个流对象打开一个文件的成员函数是open (filename, mode);其中 filename 是一个字符串,表示要打开的文件的名称,mode 是一个可选参数,由以下标志组合而成:

  • 写文件操作示例如下:
#include <iostream>
using namespace std;
//文件读写头文件
#include <fstream>

//写文件
void test01() {
    //以输出方式打开文件
    ofstream ofs("./test.txt", ios::out | ios::trunc);
    
    //后期指定打开方式
    ofstream ofs;
    ofs.open("./test.txt", ios::out | ios::trunc);

    //判断是否打开成功
    if(!ofs.is_open()) {
        cout << "打开失败!" << endl;
    }
    
    //输入内容
    ofs << "姓名:abc" <<endl;
    ofs << "年龄:20" <<endl;
    ofs << "性别:男" <<endl;

    //写入完毕后关闭文件
    ofs.close();
}
  • 读文件操作示例如下:
void test02() {
    ifstream ifs;
    ifs.open("./test.txt", ios::in);

    //判断是否打开成功
    if(!ifs.is_open()) {
        cout << "打开失败" << endl;
    }
    
    //第一种方式 -- 用数组存储
    char buf[1024];
    while(ifs >> buf) { //按行读取
        cout << buf <<endl;
    }

    //第二种方式
    char buf2[1024];
    while(!ifs.eof()) { //eof读到文件的尾部
        ifs.getline(buf2, sizeof(buf2));
        cout << buf2 << endl;
    }

    //第三种方式 -- 按单个字符读取(不推荐)
    char c;
    while((c = ifs.get()) != EOF) { //EOF 文件尾
        cout << c;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/166217.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

联合证券|内外利好共振 今年A股可更乐观一点

在经历了开年首周的快速拉升后&#xff0c;上星期A股商场全体高位盘整。职业板块从普涨转为快速轮动&#xff0c;前期领涨的新能源及大消费主线均出现了必定程度的回撤&#xff0c;由金融、信创板块接力“领跑”。 展望后市&#xff0c;指数在盘整后能否持续上攻&#xff1f;外…

解决前后端分离Vue项目部署到服务器后出现的302重定向问题

解决前后端分离Vue项目部署到服务器后出现的302重定向问题问题描述问题原因定位问题解决方案校验修改效果相关阅读写在最后问题描述 最近发现自己开发的vue前后端分离项目因为使用了spring security 安全框架&#xff0c;即使在登录认证成功之后再调用一些正常的接口总是会莫名…

Xilinx 7系列FPGA之Spartan-7产品简介

以最低的成本获得无与伦比的性能和功耗效率如果您对功耗或性能的要求与成本要求一样严苛&#xff0c;那么请使用 Spartan -7 FPGA。该系列采用 TSMC&#xff08;台积电&#xff09; 的 28nm HPL 工艺制造&#xff0c;将小尺寸架构的Xilinx 7 系列FPGA 的广泛功能和符合 RoHS 标…

结构体专题详解

目录 &#x1f94e;什么是结构体&#xff1f; ⚾结构体的声明 &#x1f3c0;简单结构体的声明 &#x1f3d0;结构体的特殊声明 &#x1f3c8;结构体嵌套问题 &#x1f3c9;结构体的自引用 &#x1f3b3;结构体的内存大小 &#x1f94c;结构体的内存对齐 ⛳内存对齐的优点 ⚽还…

SAP 服务器参数文件详细解析

一、SAP参数的说明 SAP参数的学习需要了解SAP参数的作用、参数的启动顺序、参数的配置&#xff1b; 1、参数的启动顺序 a) 启动Start profileb) 启动default profilec) 启动instance profile 2、参数的位置 a) 启动参数Start profile的位置&#xff1a;/usr/sap//SYS/prof…

计讯物联数字乡村解决方案赋能乡村振兴

项目背景 数字乡村是乡村振兴的战略方向&#xff0c;是推动农村现代化的重要途径。当前&#xff0c;数字乡村建设正在加速推进&#xff0c;打造乡村数字治理新模式&#xff0c;提升乡村的数字化水平&#xff0c;进一步推动乡村振兴进入高质量发展新赛道。计讯物联作为数字乡村…

机器学习的几个公式

今天看了几个公式的推演过程&#xff0c;有些推演过程还不是很明白&#xff0c;再着担心自己后面会忘记&#xff0c;特来此记下笔记。python 是由自己特定的公式符号的&#xff0c;但推演过程需要掌握&#xff0c;其实过程不过程不是重点&#xff0c;只要是要记得公式的含义&am…

SocketCAN 命名空间 VCAN VXCAN CANGW 举例

文章目录NAMESPACESocketCAN最新 can-utils 安装VCAN 举例VXCAN 举例CANGW 举例参考NAMESPACE namespaces, 命名空间, 将全局系统资源包装在抽象中, 使命名空间中的进程看起来拥有自己全局资源的独立实例. 命名空间的一个用途是实现容器. Linux 命名空间类型及隔离物(Isolate…

译文 | Kubernetes 1.26:PodDisruptionBudget 守护不健康 Pod 时所用的驱逐策略

对于 Kubernetes 集群而言&#xff0c;想要确保日常干扰不影响应用的可用性&#xff0c;不是一个简单的任务。上月发布的 Kubernetes v1.26 增加了一个新的特性&#xff1a;允许针对 PodDisruptionBudget (PDB) 指定不健康 Pod 驱逐策略&#xff0c;这有助于在节点执行管理操作…

电商云仓是如何包装发货的?

包装不时是为了维护产品&#xff0c;而它从工厂地板移动到大型仓库&#xff0c;并最终经过批发或批发店抵达消费者。但是&#xff0c;自21世纪初以来&#xff0c;消费者希望与那些不时吸收着某种情感的品牌联络在一同&#xff0c;同时央求他们在心理上对品牌中止投资&#xff0…

【Java AWT 图形界面编程】Canvas 组件中使用 Graphics 绘图 ④ ( AWT 绘图窗口闪烁问题 )

文章目录一、AWT 绘图窗口闪烁问题二、完整代码示例一、AWT 绘图窗口闪烁问题 使用 Graphics 第一次绘图 完成后 , 如果在循环中 持续调用 Canvas#repaint() 函数刷新界面 , 代码如下 : import java.awt.*;public class HelloAWT {public static void main(String[] args) thr…

MySQL进阶——存储引擎

MySQL有9种存储引擎&#xff0c;不同的引擎&#xff0c;适合不同的场景&#xff0c;我们最常用的&#xff0c;可能就是InnoDB&#xff0c;应该是从5.5开始&#xff0c;就成为了MySQL的默认存储引擎。 show engines可以查询MySQL支持的这几种存储引擎&#xff0c;从表头能看出来…

SVN工程转Git工程Github托管

SVN工程转Git工程&Github托管1. 介绍2. autoAudioTest之SVN转Github步骤Step 1 工作环境(ubuntu)Step 2 安装升级必要软件Step 3 转换脚本Step 4 检查软件运行环境Step 5 生成authors.txtStep 6 SVN转换Git格式Step 7 Github新建空工程Step 8 Git提交已有工程Step 9 Git提交…

Dubbo快速入门看这一篇文章就够了

网站用户少,流量小,抗压力差(eg: ssm)网站用户量进一步增长,流量增多,服务器不能平滑扩容(eg: 多个ssm)网站用户和流量随时间稳步升高,需要随时进行服务器扩容(eg: rpc/http) 第2节 分布式框架解决的问题 1 2 3 4 5 6随着互联网架构的越来越复杂,由原来的单一架构 ...到... 流动…

M320、M601、HD1(RTU)功能对比

M320、M601、HD1_RTU硬件 / 软件功能对比一、硬件1.HD1-RTU2.Haas506-M3203.Haas506-M6014.对比区别二、软件1.对比区别一、硬件 1.HD1-RTU 详情参考HaaS506-HD1 (RTU) - 硬件介绍 2.Haas506-M320 详情参考HaaS506-M320 - 开发板介绍 3.Haas506-M601 详情参考HaaS506…

(二十二)简单算法和Lambda表达式

目录 前言: 1.选择排序 2.二分查找 3.Lambda表达式 前言: 算法是一个程序和软件的灵魂&#xff0c;要成为一名优秀的程序员&#xff0c;只有对基础算法全面掌握&#xff0c;才能在设计程序和编写代码的过程中显得得心应手。常用的基础算法有快速排序算法、堆排序算法、归并排…

每日一问-ChapGPT-20230115-关于断舍离

文章目录每日一问-ChapGPT系列起因每日一问-ChapGPT-20230115-关于断舍离人类脑是适合专心做一件事&#xff0c;还是适合并行做多件事做事情的优先顺序怎样安排chapGPT你是怎么学到这么多知识的chapGPT你拥有智慧吗chapGPT你是实时更新自己的模型吗chapGPT你有情感吗chapGPT你有…

C++程序卡死、UI界面卡顿问题的原因分析与总结

目录 1、概述 2、软件卡死问题 2.1、死循环 2.2、死锁 3、客户端软件的UI界面卡顿问题 3.1、UI线程在频繁地写日志到文件中&#xff0c;导致UI线程时不时的卡顿 3.2、从网上拷贝的代码中调用Sleep函数&#xff0c;导致UI界面有明显的卡顿 4、总结 VC常用功能开发汇总&a…

COCO_03 制作COCO格式数据集 dataset 与 dataloader

文章目录1 引言2 pycocotools介绍3 Dataset 构建4 Dataloader 构建4.1 解决batch中tensor维度不一致的打包问题4.2 collate_fn()函数分析AppendixA. convert_coco_poly_maskB. COCO_Transform参考1 引言 在之前的文章中&#xff0c;我们认识了COCO数据集的基本格式https://blo…

【设计模式】创建型模式·工厂模式

设计模式学习之旅(四) 查看更多可关注后查看主页设计模式DayToDay专栏 一.引子 需求&#xff1a;设计一个咖啡店点餐系统。 设计一个咖啡类&#xff08;Coffee&#xff09;&#xff0c;并定义其两个子类&#xff08;美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】&…