学习C++:C++基础(四)类型转换、C++异常、文件读写

目录

1.1 C++类型转换

1.1.1 静态转换

1.1.2 动态转换

1.1.3 常量转换

1.1.4 重新解释转换(极其不安全,不推荐)

1.2 C++异常

1.2.1 什么是异常

1.2.2 C语言处理异常缺陷

1.2.3 C++处理异常

1.2.4 异常接口声明

1.2.5 异常的生命周期

1.2.6 异常的多态使用

1.2.7 系统标准异常

1.2.8 自定义异常类

1.3 C++输入输出流

1.3.1 流的概念和流类库的结构

1.3.2 标准输入流

1.3.3 标准输出流

1.4 文件读写


1.1 C++类型转换

1.1.1 静态转换

1.用处

用于类层次结构中父类与子类之间的指针或引用的转换。

上行(子转父)是安全的,下行是不安全的。

2.code:静态类型转换 static_cast---内置数据类型转换

语法:static_cast<目标数据类型>(要转成目标数据类型的变量)

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


int main() {
    test01();
    return 0;
}
/home/lhw/桌面/C++/day9/cmake-build-debug/day9
d = 97

允许内置数据类型转换

3. 类的下行转换(不安全但不报错)

lass Base{};
class Son : public Base{};
class Other{};

void test02()
{
    Base * base = NULL;
    Son * son = NULL;
    Son * son2 = static_cast<Son*>(base);
}

但base不能转换成other类型的,只支持父子类型的转换。

1.1.2 动态转换

1.语法及相比于静态类型转换的区别

dynamic_cast主要用于类层次的上行或下行转换

在类上行转换时,dynamic_cast与static_cast 是一致的

在类下行转换时,dynamic_cast比static_cast 更安全

2. code:动态类型转换 dynamic_cast---内置数据类型转换

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

报错,不允许内置数据类型转换!

3.类的下行转换

void test03()
{
    Base * base = NULL;
    Son * son = NULL;
    Son * son2 = dynamic_cast<Son*>(base);
}

父转子,不安全,报错!

4.发生多态类型转换总是安全的

 下面这段代码可以成功运行

class Base
{
    virtual void func()
    {

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

    }
};
class Other{};



void test03()
{
    Base * base = new Son;
    Son * son = NULL;
    Son * son2 = dynamic_cast<Son*>(base);
}

1.1.3 常量转换

1.用处

该运算符用来修改类型的const属性

常量指针被转化成非常量指针,并且仍然指向原来的对象

常量引用被转化为非常量引用,并且仍然指向原来的对象

warning:不能直接对非引用和非指针的变量使用const_cast操作符去直接移除它的const

2.code

void test05()
{
    const int * p =NULL;
    int * pp = const_cast<int*>(p);

    const int * ppp = const_cast<const int *>(pp);

}

3.语法

const转为非const语法

const_cast<类型 * >(const 类型 * )

非const转为const语法

const_cast<const 类型 * >(类型 * )

4. 不能直接对非引用和非指针的变量使用const_cast操作符去直接移除它的const

void test06()
{
    const int a = 10;
    int b = const_cast<int>(a);
}

报错

5.引用转化为const引用

void test07()
{
    int num = 10;
    int & numref = num;
    const int & num2 =const_cast<const int &>(numref);
}

1.1.4 重新解释转换(极其不安全,不推荐)

1.int转换为int *

void test08()
{
    int a = 10;
    int * p = reinterpret_cast<int*>(a);
}

2.相同地,不同类也能相互转换。十分不安全。

1.2 C++异常

1.2.1 什么是异常

异常就是处理程序中的错误。所谓错误就是指在程序运行中发生的一些异常事件(除0、数足下标越界)

1.2.2 C语言处理异常缺陷

int mydivision(int a,int b)
{
    if(b==0)
    {
        return -1;
    }
    else
    {
        return a/b;
    }

}

int main() {
    int a = 10;
    int b = -10;
    int ret = mydivision(a,b);
    if(ret == -1)
    {
        cout << "yichang" << endl;
    }
    return 0;
}

返回值不统一,返回值无法区分是结果还是异常。

1.2.3 C++处理异常

1.C++处理异常代码

int mydivision(int a,int b)
{
    if(b==0)
    {
        throw 1;
    }
    else
    {
        return a/b;
    }

}

int main() {
    int a = 10;
    int b = -10;
    try
    {
        mydivision(a,b);
    }
    catch (int)
    {
        cout << "type int exxception" << endl ;
    }
    catch (char)
    {
        cout << "type char exxception" << endl ;
    }
    return 0;
}

运行:无运行结果

代码修改为:

int mydivision(int a,int b)
{
    if(b==0)
    {
        throw 1;
    }
    else
    {
        return a/b;
    }

}

int main() {
    int a = 10;
    int b = 0;
    try
    {
        mydivision(a,b);
    }
    catch (int)
    {
        cout << "type int exxception" << endl ;
    }
    catch (char)
    {
        cout << "type char exxception" << endl ;
    }
    return 0;
}

执行:

/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
type int exxception

代码修改为:

int mydivision(int a,int b)
{
    if(b==0)
    {
        throw 'a';
    }
    else
    {
        return a/b;
    }

}

int main() {
    int a = 10;
    int b = 0;
    try
    {
        mydivision(a,b);
    }
    catch (int)
    {
        cout << "type int exxception" << endl ;
    }
    catch (char)
    {
        cout << "type char exxception" << endl ;
    }
    catch(...)
    {
        cout << "type others exxception" << endl ;
    }
    return 0;
}

运行:

/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
type char exxception

进程已结束,退出代码0

2.总结

I .必须有处理异常的catch函数,否则报错。(比如throw 3.14,必须要有catch double)

II.catch用...表示其他异常的处理,类似于switch default ...

3.嵌套异常

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

}

void test01()
{
    int a = 10;
    int b = 0;
    try
    {
        mydivision(a,b);
    }
    catch (int)
    {
        cout << "type int exxception" << endl ;
    }
    catch (char)
    {
        cout << "type char exxception" << endl ;
    }
    catch (double )
    {
        cout << "type double exxception" << endl ;
    }
}

int main()
{
    try
    {
        test01();
    }
    catch (double)
    {
        cout << "type double exxception in main" << endl ;
    }
    return 0;
}

我们看下面这一段代码,这里的结构是这样的,main函数调用test01,test01中有异常,这里异常由谁处理呢?

/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
type double exxception

明显,由test01处理,但如果想向上抛出异常,要加一个throw。

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

}

void test01()
{
    int a = 10;
    int b = 0;
    try
    {
        mydivision(a,b);
    }
    catch (int)
    {
        cout << "type int exxception" << endl ;
    }
    catch (char)
    {
        cout << "type char exxception" << endl ;
    }
    catch (double )
    {
        throw;
        cout << "type double exxception" << endl ;
    }
}

int main()
{
    try
    {
        test01();
    }
    catch (double)
    {
        cout << "type double exxception in main" << endl ;
    }
    return 0;
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
type double exxception in main

4.设计一个我的异常类

class myException
{
public:
    void ex()
    {
        cout << " my exception !" << endl ;
    }
    
};
int mydivision(int a,int b)
{
    if(b==0)
    {
        throw myException();
    }
    else
    {
        return a/b;
    }

}

这里抛出一个异常的匿名对象。

void test01()
{
    int a = 10;
    int b = 0;
    try
    {
        mydivision(a,b);
    }
    catch (int)
    {
        cout << "type int exxception" << endl ;
    }
    catch (char)
    {
        cout << "type char exxception" << endl ;
    }
    catch (double )
    {
        throw;
        cout << "type double exxception" << endl ;
    }
    catch(myException e)
    {
        e.ex();
    }
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
 my exception !

5.总结

若有异常则通过throw操作创建一个异常对象并输出

将可能抛出异常的程序放在try块之中

如果在try块执行期间没有引起异常,那么跟在try后面的catch语句不会执行

catch字句会根据出现的先后顺序检查,匹配的catch语句捕获并处理异常(或者向上抛出异常)

如果匹配的处理未找到,则运行函数terminate将自动被调用,其缺省功能调用abort终止程序。

处理不了的异常,可以在catch的最后一个分支,使用throw向上抛

6.栈解旋

从try代码块开始,到throw抛出异常之前,所有栈上的数据都会被释放掉。

1.2.4 异常接口声明

当只允许抛出某种类型的异常时,我们看看代码

int mydivision(int a,int b) throw(int)
{
    if(b==0)
    {
        throw (3);
    }
    else
    {
        return a/b;
    }

}

void test01()
{
    int a = 10;
    int b = 0;
    try
    {
        mydivision(a,b);
    }
    catch (int)
    {
        cout << "type int exxception" << endl ;
    }
    catch (char)
    {
        cout << "type char exxception" << endl ;
    }
    catch (double )
    {
        throw;
        cout << "type double exxception" << endl ;
    }
    catch(myException e)
    {
        e.ex();
    }
}

int main()
{
    test01();
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
type int exxception

进程已结束,退出代码0

这里我们只允许throw出int类型的异常。若我们更改代码如下:

class myException
{
public:
    void ex()
    {
        cout << " my exception !" << endl ;
    }

};

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

}

void test01()
{
    int a = 10;
    int b = 0;
    try
    {
        mydivision(a,b);
    }
    catch (int)
    {
        cout << "type int exxception" << endl ;
    }
    catch (char)
    {
        cout << "type char exxception" << endl ;
    }
    catch (double )
    {
        throw;
        cout << "type double exxception" << endl ;
    }
    catch(myException e)
    {
        e.ex();
    }
}

代码运行报错

/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
terminate called after throwing an instance of 'double'

当然也可以捕获很多异常

int mydivision(int a,int b) throw(int,double)

1.2.5 异常的生命周期

class myException
{
public:
    myException()
    {
        cout << "myexception default begin " << endl ;
    }
    myException(const myException & e)
    {
        cout << "myexception copy begin " << endl ;
    }
    ~myException()
    {
        cout << "myexception end " << endl;
    }
    void ex()
    {
        cout << " my exception !" << endl ;
    }
};

void dowork()
{
    throw myException();
}

void test02()
{
    try
    {
        dowork();
    }
    catch (myException e)
    {
        cout << "myexception buhuo" << endl;
    }
}

我们看这段代码的运行结果

/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
myexception default begin 
myexception copy begin 
myexception buhuo
myexception end 
myexception end 

其实是这样运行的,调用dowork时会调用默认构造,catch里面以值传递参数会调用拷贝构造,然后打印信息,再释放掉。但此时代码运行效率不高,我们可以加一个引用,提高效率。

void test02()
{
    try
    {
        dowork();
    }
    catch (myException&e)
    {
        cout << "myexception buhuo" << endl;
    }
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
myexception default begin 
myexception buhuo
myexception end 

当然我们也可以通过指针。

void dowork()
{
    throw &myException();
}
void test02()
{
    try
    {
        dowork();
    }
    catch (myException * e)
    {
        cout << "myexception buhuo" << endl;
    }
}

但这么调用的话,不推荐,再在catch调用myexception的方法会报错。

1.2.6 异常的多态使用

class BaseException
{
public:
    virtual void printError() = 0;
};

class nullptrException : public BaseException
{
public:
    virtual void printError()
    {
        cout << "nullptrException" << endl;
    }
};

class arrayoverflowException : public BaseException
{
public:
    virtual void printError()
    {
        cout << "arrayoverflowException" << endl;
    }
};

void dowork()
{
    throw nullptrException();
}


void test01()
{
    try
    {
        dowork();
    }
    catch (BaseException & exception)
    {
        exception.printError();
    }
}

1.2.7 系统标准异常

1.C++标准异常库

引入头文件

#include <stdexcept>

2.越界异常

class Person
{
public:
    Person(int age)
    {
        if(age <0 || age>150)
        {
            throw out_of_range("age error!");
        }
        this -> age = age;
    }
    int age;
};

void test02()
{
    try
    {
        Person p(151);
    }
    catch(out_of_range & e)
    {
        cout <<  e.what() << endl;
    }
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
age error!

利用多态原理,可以直接传入父类对象。

 catch(exception & e)

其他异常可以参考手册。

1.2.8 自定义异常类

1.自定义异常类是干什么的

扩充系统标准异常,重写C++标准异常的虚函数。

 2.重写什么及重写注意事项

void test11()
{
    ifstream ifs;
    ifs.open("/home/liuhongwei/桌面/1.txt",ios::in);
    if(!ifs.is_open())
    {
        cout << "fail to open the file" << endl;
        return;
    }
    char buf[1024] = {0};
    while(ifs.getline(buf,sizeof(buf)))
    {
        cout << buf << endl ;
    }
}

1.3 C++输入输出流

1.3.1 流的概念和流类库的结构

1.流的概念

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

2.C++的输入输出包含以下三方面的内容

标准I/O:对系统指定的标准设备的输入和输出,即从键盘输入数据,输出到屏幕显示器。

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

串I/O:对内存中指定的空间进行输入和输出

1.3.2 标准输入流

1.举例子:cin(无参数)

void test02()
{
    char c = cin.get();
    cout << "c = " << c << endl;
}

等待从键盘上输入字符,并从缓冲区读走一个字符

/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
as
c = a
void test02()
{
    char c = cin.get();
    cout << "c = " << c << endl;
    c = cin.get();
    cout << "c = " << c << endl;
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
caa
c = c
c = a
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
c
c = c
c = 

第二次输出的是换行符

/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9

c = 

a
c = a

进程已结束,退出代码0

第一次回车,读取换行符,第二次输入a,读取a

2.举例子:cin(两参数)

void test02()
{
    char buf[1024];
    cin.get(buf,1024);
    cout << buf << endl;
}

  将键盘输入读入buf中,

/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
hahhahnefu
hahhahnefu

进程已结束,退出代码0

但它对换行符是如何操作的呢?我们做以下测试:

void test02()
{
    char buf[1024];
    char c;
    cin.get(buf,1024);
    cout << buf << endl;
    c = cin.get();
    if(c == '\n')
    {
        cout << "\\n at buffer" << endl;
    }
    else
    {
        cout << "\n not at buffer" << endl;
    }
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
ccc
ccc
\n at buffer

因此得出结论:利用cin.get()获取字符串的时候会把换行符留在缓冲区中

3. 举例子:cin.getline(缓冲区,长度)

void test02()
{
    char buf[1024];
    char c;
    cin.getline(buf,1024);
    cout << buf << endl;
    c = cin.get();
    if(c == '\n')
    {
        cout << "\\n at buffer" << endl;
    }
    else
    {
        cout << "\n not at buffer" << endl;
    }
}

利用cin.getline()获取字符串的时候会把一行值读入并把换行符遗弃,也不会留在缓冲区。

/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
hello world 
hello world 
a
\n not at buffer

4. 举例子:cin.ignore() 

void test03()
{
    cin.ignore();
    char c = cin.get();
    cout << "c = " << c << endl;
}

忽略一个字符

/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
as
c = s

ignore也可以有参数,我们来演示一下:

void test03()
{
    cin.ignore(2);
    char c = cin.get();
    cout << "c = " << c << endl;
}

忽略了两个字符

/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
ming
c = n

5. 举例子:cin.peek() 

void test04()
{
    char c = cin.peek();
    cout << "c = " << c << endl;
    c = cin.get();
    cout << "c = " << c << endl;
    c = cin.get();    
    cout << "c = " << c << endl;
}

 作用,偷窥一眼一个字符,但不把字符从缓冲区拿走。

/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
cs
c = c
c = c
c = s

6. 举例子:cin.pushback() 

void test05()
{
    char c = cin.get();
    cin.putback(c);

    char buf[1024] = {0};
    cin.getline(buf,1000);
    cout << buf << endl;
}

放回到缓冲区一个字符

/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
lhwnb
lhwn

7.案例:判断用户输入的是数字还是字符串

void test06()
{
    cout << "Please input a string or number:" << endl;
    char c = cin.peek();
    if(c>=0&&c<=9)
    {
        int num;
        cin >> num;
        cout << "number input : " << num << endl;
    }
    else
    {
        char buf[1024] = {0};
        cin >> buf;
        cout << "string input :" << buf << endl;
    }

}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
Please input a string or number:
ccccc
string input :ccccc

进程已结束,退出代码0

8.案例2:输入0-10错误重新输入

获取当时的标志位:0为正常,1为异常 cin.fail()来获取

清空缓冲区 cin.clear()   重置标志位 cin.sync()

1.3.3 标准输出流

1.cout.put()向缓冲区写一个字符

void test08()
{
    cout.put('c');
}


int main()
{
    test08();
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
c

也可以连环使用

void test08()
{
    cout.put('c').put('e');
}

2. cout.write()向buffer中写num个字节到当前输出流中

void test08()
{
    char buf[] = "hello world";
    cout.write(buf, strlen(buf));
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
hello world

3. 通过流成员格式化输出

void test08()
{
    int num = 99;
    cout.width(20);
    cout << num << endl;
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
                  99

99前面有18个空格。

void test08()
{
    int num = 99;
    cout.width(20);
    cout.fill('*');
    cout << num << endl;
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
******************99

填充为*

void test08()
{
    int num = 99;
    cout.width(20);
    cout.fill('*');
    cout.setf(ios::left);

    cout << num << endl;
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
99******************

左对齐

void test08()
{
    int num = 99;
    cout.width(20);
    cout.fill('*');
    cout.setf(ios::left);
    cout.unsetf(ios::dec);
    cout.setf(ios::hex);
    
    cout << num << endl;
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
63******************

卸载十进制,安装十六进制。

void test08()
{
    int num = 99;
    cout.width(20);
    cout.fill('*');
    cout.setf(ios::left);
    cout.unsetf(ios::dec);
    cout.setf(ios::hex);
    cout.setf(ios::showbase);
    cout << num << endl;
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
0x63****************

显示基数

4.使用控制符格式化输出

要包含头文件 iomanip

void test09()
{
    int num = 99;
    cout << setw(20) << num ;
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
                  99
void test09()
{
    int num = 99;
    cout << setw(20) << setfill('*') << num ;
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
******************99
void test09()
{
    int num = 99;
    cout << setw(20) << setfill('*') << setiosflags(ios::showbase) << num ;
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
******************99
void test09()
{
    int num = 99;
    cout << setw(20) << setfill('*') << setiosflags(ios::showbase) << setiosflags(ios::left) << num ;
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
99******************
void test09()
{
    int num = 99;
    cout << setw(20) << setfill('*') << setiosflags(ios::showbase) << setiosflags(ios::left) << hex << num ;
}
/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
0x63****************

1.4 文件读写

1.包含输入输出流

fstream

2.写文件

void test10()
{
    ofstream ofs;
    ofs.open("/home/liuhongwei/桌面/1.txt",ios::out|ios::trunc);  //设置打开方式和路径
    if(!ofs.is_open())
    {
        cout << "fail to open the file" << endl;
    }
    ofs << "name : sunwukong" << endl;
    ofs << "age : 16" << endl;
    ofs.close();

}

我们运行这段代码,发现桌面上多了一个1.txt文件,里面出内容是:

name : sunwukong
age : 16

3.读文件

void test11()
{
    ifstream ifs;
    ifs.open("/home/liuhongwei/桌面/1.txt",ios::in);
    if(!ifs.is_open())
    {
        cout << "fail to open the file" << endl;
        return;
    }
    char buf[1024] = {0};
    while(ifs >> buf)
    {
        cout << buf << endl ;
    }
}

方式1:利用右移运算符的重载,一个一个读出来。

/home/liuhongwei/桌面/slam/day9/cmake-build-debug/day9
name
:
sunwukong
age
:
16

方式2:用getline方法

void test11()
{
    ifstream ifs;
    ifs.open("/home/liuhongwei/桌面/1.txt",ios::in);
    if(!ifs.is_open())
    {
        cout << "fail to open the file" << endl;
        return;
    }
    char buf[1024] = {0};
    while(ifs.getline(buf,sizeof(buf)))
    {
        cout << buf << endl ;
    }
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

APS2023

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

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

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

打赏作者

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

抵扣说明:

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

余额充值