C++ 修炼 七

一、虚函数表   

什么是虚函数表:在C++的类中,一旦成员函数中有虚函数,这个类中就会多一个虚函数表指针,这个指针指向一个虚函数表,表里面记录了这个类中所有的虚函数。当这个类被继承,它的子类中也会有一个虚函数表(不管子类中有没有虚函数),如果子类的成员函数中有函数签名与父类的虚函数一样,就会用子类中的函数替换它在虚函数表中的位置,这样就达到了覆盖的效果 函数还是在的,代码段中
    当通过类指针或引用调用函数时,会根据对象中实际的虚函数表记录来调用函数(每个对象有独自的虚函数表,根据虚函数表中的指针,找到父类的虚函数,替换它),这样就达到了多态的效果

类的对象内部会有指向类内部的虚表地址的指针。通过这个指针调用虚函数。
虚函数的地址存放于虚函数表之中

    Test* p = new Test;       定义一个指针指向对象,再指向虚函数表,再找每一个函数,调用
    int* p1 = (int*)*(int*)p;
    ((funcp)(*(p1+1)))();

二、虚析构

当使用delete释放一个父类指针时,不管实际指向的对象是子类还是父类都只会调用父类的析构函数(多态肯定会出现的问题)
    如果子类的析构函数有需要负责释放的内存,就会造成内存泄漏
    为了解决这个问题,可以把父类的析构函数设置为虚函数,析构函数进行覆盖时不会比较函数名
    父类的析构函数被子类的析构函数覆盖,子类先释放,释放完了,就没覆盖了,父类释放
    当父类的析构为虚函数时,通过父类指针或引用释放子类对象时,会自动调用子类的析构函数,子类的析构函数执行完成后也会调用父类的析构函数
    注意:析构函数可以是虚函数,但构造函数不可以
    构造函数,父类先执行,会发现子类覆盖,变成子类执行,子类会让父类先执行,构成死循环

三、强制类型转换

注意:C++中为了兼容C语言,(目标类型)源类型 依然可以继续使用,但C语言的强制类型转换安全性差,因此建议使用C++中的强制类型转换
    注意:C++之父认为如果代码设计的完善,根据不需要用到强制类型转换,而C++的强制类型转换之所以设计得很复杂,是为了让程序员多关注代码本身的设计,尽量少使用
    C++中的强制类型转换保证没有很大安全隐患
static_cast<目标类型>(源类型)    short* sp=static_cast<short*>(p)  编译器会对源类型和目标类型做兼容性检查,不通过则错
                整型与整型 、指针与指针 一个体系内的转换
    void* p = NULL;
    short* sp = static_cast<short*>(p);

dynamic_cast<目标类型>(源类型) 编译器会对源类型和目标类型是否同为指针或引用,并且存在多态型的继承关系  
         多态,父子类 (虚函数)
const_cast<目标类型>(源类型)编译器会对源类型和目标类型是否同为指针或引用,除了常属性外其它必须完全相同否则报错  const
reinterpret_cast<目标类型>(源类型)编译器会对源类型和目标类型是否同为指针或整数进行检查,也就是说把整数转换成指针或把指针转换成整数

    动态链接,静态链接  (动态编译,静态编译)   
    1、静态库,动态库   
    2、多态
        静态编译:指针或引用的目标是确定,在编译时间就确定所有类型检查、函数调用
        动态编译:指针或引用的目标是不确定的(多态),只有运行时才确定,具体是哪个子类

四、I/O流

I/O流的打开模式:
        ios::app    打开文件用于追加,不存在则创建,存在不清空    写在文件尾,读在文件头
        ios::ate    打开时定义到文件末尾
        ios::binary    以二进制模式进行读写
        ios::in    以读权限打开文件,不存在则失败,存在不清空    从文件中到终端   >>
        ios::out    以写权限打开文件,不存在则创建,存在则清空     从终端到文件中  <<
        ios::trunc    打开文件时清空

    fstream/ifstream/ofstream 类用于进行文件操作
    构造函数或成员函数 open 用于打开文件
    good成员函数检查流是否可用
    eof成员函数用于输入流是否结束;使用eof()函数检测文件是否读结束

    >>操作符用于从文件中读取数据到变量
    <<操作符用于输出数据到文件

    IO流有一系列格式化控制函数,类似:左对齐、右对齐、宽度、填充、小数点位数。

    二进制读写:成员函数 read/write
        read(char_type *__s,streamsize __n)
        gcount成员函数可以获取上次流的二进制读写操作的字节数

        write(char_type *__s,streamsize __n)
        good 成员函数可以获取到写操作是否成功

    随机读写:seekp(off_type,ios_base::seekdir)
    功能:设置文件的位置指针
    off_type:偏移值
        正值向右,负值向左
    seekdir:基础位置
        ios::beg    文件开头
        ios::cur    文件当前位置
        ios::end    文件末尾

    获取文件位置指针:tellp
    该成员函数返回当前文件流的位置指针(字节数)
    也可以借助此函数获取文件的大小
// 调整文件的位置指针到末尾
    fs.seekp(0,ios::end);

    cout << "文件的总字节数:" << fs.tellp() << endl;

五、类型信息 typeid

用于获取数据的类型信息,返回type_info类型临时对象
name成员函数,可以获取类型的名字,内建类型名字使用缩写  4Base  5Basea
同时还支持 ==  !=  用来比较是否是同一种类型

如果用于判断父子类的指针或引用,它不能准确判断出实际的对象类型
但可以判断具有多态继承关系的父子类的指针或引用,它的实际对象
Base b;
(typeid(b) == typeid(Base))-----1

#include <iostream>
#include <typeinfo>

using namespace std;

class Base
{
public:
    virtual ~Base(void)
    {

    }
};

class Test:public Base
{

};

int main()
{
    Base b;
    Test t;
    cout << (typeid(b) == typeid(t)) << endl;//----------------0
    cout << (typeid(b) == typeid(Test)) << endl;//------------0
    cout << (typeid(t) == typeid(Base)) << endl;//------------0

    Base* p = new Test;
    cout << (typeid(p) == typeid(Test*)) << endl;//-----------0
    cout << (typeid(*p) == typeid(Test)) << endl;//------------在有虚函数是1,没有为0
}

六、异常处理

抛异常
        throw 数据
        抛异常对象
        抛基本类型
        注意:不能抛出局部对象的指针或引用
        注意:如果异常没有被捕获处理,程序就会停止
    捕获异常
        try{
            可以抛出异常的代码
        }
        catch(类型 变量名)//根据数据类型进行捕获
        {
            处理异常,如果无法处理可以继续抛出异常
        }

        注意:捕获异常的顺序是自上而下的,而不是最精准的匹配,针对子类异常捕获时,子类要放在父类的前面
    
    函数的异常声明:
    返回值类型 函数名(参数列表)throw(类型1,类型2.。。)
    注意:如果不写异常声明表示什么类型的异常都可能抛出
    注意:如果写了异常声明表示只抛出某些类型的异常,一旦超出异常声明的范围,程序会直接停止,无法捕获
    注意:throw() 表示什么类型都不会抛出
    
    不会检查异常声明

设计异常类:

 class Error
    {
        int errno;
        char errmsg[255];
    public:
        Error(int errno =-1,const char* msg="未知错误")
        {
            this->errno=errno;
            strcpy(errmsg,msg);
        }
        int getError(void)
        {
            return errno;
        }
        const char* getErrmsg(void)
        {
            return errmsg;
        }
    };

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值