IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++第九次课作业 -> 正文阅读

[C++知识库]C++第九次课作业

1.虚函数

? 用virtual修饰的成员函数叫做虚函数

? 虚函数对类的影响是增加了一个指针的内存,也就是占了4个字节

? 不管有几个虚函数,都只占一个指针的内存大小

? 在类里 如无任何数据的情况下 类大小为1个字节(用于标识为类)

? 类中出现一个虚函数的时候(为4个字节)

#include<iostream>
using namespace std;
class Fox
{

public:

protected:

};
class Fox2
{

public:
    virtual void print1()
    {
        cout << "虚函数 " << endl;
    }
protected:

};




int main() 
{
    cout << sizeof(Fox) << endl;//占1个字节

    cout << sizeof(Fox2) << endl;//4个字节
    while (1);
    return 0;
}

虚函数表: 就是一个指针存储所有虚函数的首地址

简单来说就是我们一个类中无论存在多少个虚函数,它的大小并未改变,也就是说

我们可以理解为虚函数为指针调用,类似于C语言的函数指针,指向了这个虚函数

调用了这个虚函数。

#include<iostream>
using namespace std;
class Fox
{

public:

protected:

};
class Fox2
{

public:
    virtual void print1()
    {
        cout << "虚函数 " << endl;
    }
    virtual void print2()
    {
        cout << "虚函数2" << endl;
    }
protected:

};




int main() 
{
    cout << sizeof(Fox) << endl;//占1个字节
    cout << sizeof(Fox2) << endl;//4个字节
    Fox2 fox;
    int** pfox = (int**) &fox;
    typedef void(*PF)();

    PF pfunc = (PF) pfox[0][0];
    pfunc();//调用虚函数1
   

    pfunc= (PF)pfox[0][1];
    pfunc();//调用虚函数2

    while (1);
    return 0;
}

2.虚函数和多态

?多态 同一种行为(调用)产生的不同结果,我们称之为多态

?多态必要原则:父类中必须存在虚函数

?子类必须采用公有继承的方式,必须存在指针类的使用

#include<iostream>
#include<string>
using namespace std;
class Fox
{
public:Fox(string name) :name(name) {};
 virtual void coutname()//我们称成员函数为行为
 {
        cout << name << endl;
 }
protected:
    string name;
};
class FoxSon:public Fox
{
public:FoxSon(string Sname = "",string Fname="") :name(Sname), Fox(Fname) {};
      void coutname()   //不用再加入关键字 virtual
      {
      cout << name << endl;
      }
private:
    string name;
};
class Fox3 :public Fox
{
public:Fox3(string Sname = "", string Fname = "") :name(Sname), Fox(Fname) {};
      void coutname()   //不用再加入关键字 virtual
      {
          cout << name << endl;
      }
private:
    string name;
};
class PrintFoxName //打印狐狸 类
{
public:
    void printName(Fox* parent)
    {
        parent->coutname();
    }

};
 
int main() 
{
    //就近原则访问

    Fox fox("大狐狸");
    FoxSon foxson("小狐狸");
    fox.coutname();//访问自己的
    foxson.coutname();//访问自己的
    //指针类型访问:依旧是就近原则
    Fox* Pfxo = &fox;
    Pfxo->coutname();
    FoxSon* Pfoxsom = &foxson;
    Pfoxsom->coutname();
    //非正常赋值  子类对象初始化父类指针,换句话说 父类指针指向为子类对象
    Pfxo = &fox;
    Pfxo->coutname();
    Pfxo= &foxson;
    Pfxo->coutname();
    //我们会发现相同行为,结果却是不一样的,这就是我们说的多态,同样的调用行为,结果却是不一样的
    //多态的目的是用于统一接口,也就是说在程序需求改变的情况下,尽可能添加代码,而不是去修改原来已经写好的功能
    //例子,比如我要打印不同狐狸的名字:我只需要传入一个狐狸的对象进去,就可以实现多态调用
    PrintFoxName test;
    Fox3 fox3("狐狸三号");
    test.printName(&fox3);
    test.printName(&foxson);
    test.printName(&fox);
    //通过传入的对象不同 实现多态行为
}

3.纯虚函数和?ADT? ??

纯虚函数也是虚函数的一种,就是没有函数体的虚函数我们成为纯虚函数

抽象类:无法构建对象,却能构建对象指针

纯虚函数没有被重写,无论被继承多少次 都是纯虚函数,虚函数无论被继承多少次都是虚函数

子类想要创建对象,必须重写父类的纯虚函数
ADT: 具有强迫性,所有子类重写函数必须和父类的一模一样

可以理解为你需要统一使用别人实现类留下的一个接口,你就必须自己重写该纯虚函数

#include<iostream>
#include<string>
using namespace std;
class Fox //没法构造对象 我们称之为抽象类
{
public:
    virtual void Coutname() = 0;//纯虚函数

protected:
  
};
class Foxson:public Fox
{

public:
    void Coutname()   //重写了纯虚函数
    {
        cout <<"狐狸1" << endl;
    }

protected:
    
};
class Foxsontow :public Fox
{

public:
    void Coutname()   
    {
        cout << "狐狸2" << endl;
    }



};

 
int main() 
{
    Foxson son;
    Foxsontow sontow;//子类必须重写了父类纯虚函数,子类才能构建对象
    Fox* pfox = nullptr;
    pfox = &son;
    pfox->Coutname();
    pfox = &sontow;
    pfox->Coutname();
    
}

4.虚析构函数?

? 只存在虚析构函数,不存在虚构造函数

?应用于当继承了子类对象初始化父类指针的情况下

?也就是说用当我们析构父类时,是不会调用子类的析构函数,导致内存泄漏的问题

所以我们需要用虚析构函数避免此类问题出现

没有虚析构函数代码示例

#include<iostream>
#include<string>
using namespace std;
class Fox
{
public:
    void Mallocm() 
    {
        num = new int(100);
    }
    ~Fox()
    {
        cout << "父类析构" << endl;
        delete num;
    }
  

protected:
    int* num;
};
class Foxson:public Fox
{

public:
    void Smallocm()
    {
        sonnum = new int(100);
    }
    ~Foxson()
    {
        cout << "子类析构" << endl;
        delete num;
    }
protected:
    int* sonnum;
};

 
int main() 
{
    Fox* pfox = new(Foxson);
    delete pfox;//此处只会调用父类析构,会有内存泄露问题


    while (1);
}

?有虚析构函数代码示例

#include<iostream>
#include<string>
using namespace std;
class Fox 
{
public:
    void Mallocm() 
    {
        num = new int(100);
    }
    virtual ~Fox()
    {
        cout << "父类析构" << endl;
    }

    
protected:
    int* num;
};
class Foxson:public Fox
{

public:
    void Smallocm()
    {
        sonnum = new int(100);
    }
    ~Foxson()
    {
        cout << "子类析构" << endl;
        delete num;
    }
protected:
    int* sonnum;
};

 
int main() 
{
    Fox* pfox = new(Foxson);
    delete pfox; // virtual ~Fox() 增加父类的虚析构函数,当父类指针消亡时,自动调用子类析构函数
                 //避免出现内存泄露问题


    while (1);
}

C++类型转换

在C++里面有一种概念 就是专人做专事,传闻C++使用类型转换更加的安全

const_cast? ? ?用于去除常属性 或者增加常属性

#include<iostream>
using namespace std;
class Str
{
public://Str(const char *str):str(str) {}
    Str(const char* str) :str(const_cast<char*>(str)) {}
      void print() 
      {
          cout << str << endl;
      }
protected:
    char* str;
};
class Num
{
public:Num(int num) :num(num) {}
  
    void print()  //常成员函数
    {
        cout << num << endl;
    }
protected:
    int num;
};




int main()
{
    //去掉常属性
    const int a = 50;
    //a = 100;//不允许被修改的常属性
    int* pa = const_cast<int*>(&a);//去掉了const属性 才能赋值给这个int*指针
    const int* pa2 = &a;
    *pa = 100;//其实并没有改变a的值
    cout << a << endl;
    //增加常属性一般用于字符串的操作
    Str str = "狐狸";//在没去掉常属性之前,无法使用
    str.print();
    //也可以给指针去掉常属性
    const Num* pnum = new Num(100);
    //pnum->print();//错误,常对象只能调用常成员函数
    Num* newpnum =const_cast<Num*> (pnum);//去掉常属性
    newpnum->print();//调用方法
}

static_cast? 用于数据类型的转换,它无法去除常属性

该转换下行转换不安全 在使用父类中没有的成员或者数据操作会导致中断问题

#include<iostream>
using namespace std;
class Fox
{
public:
    virtual void print()
    {
        cout << "父类" << endl;
    }
    void print2() 
    {
        cout << "测试1" << endl;
    }

private:

};
class Foxson :public Fox
{
public:
    
   void print()
    {
        cout << "子类" << endl;
    }
   void print3()
   {
       cout << "测试3" << endl;
   }

private:

};


int main() 
{
//基本数据转换
    int num = 5;
    double x = static_cast<double>(num);
//空指针类型
    double* PD = new double;
    void* VD =static_cast<void*>(PD);
//const类型转换 //不能去除const属性
    int x1 = 10;
    const int xx = static_cast<const int>(x);
//上行转换 (子类转父类)
    Fox* pfox = new Fox;
    Foxson* Pfoxson = new Foxson;
    pfox = static_cast<Fox*>(Pfoxson);
 //下行转换(父到子)
    Pfoxson = static_cast<Foxson*>(pfox);
    Pfoxson->print();
    Pfoxson->print3();
    Pfoxson->print2();

}

dynamic_cast? 主要用于上下行转换,和交叉转换

? ? ? ? ? ? ? ? ? ? ? ? 该转换可以检测出父类是否拥有虚函数,如果没有虚函数,父类下行转换子类报错

? ??

#include<iostream>
#include<string>
using namespace std;
class Fox
{      

public:
    Fox(string name="父类") :name(name) {}
    virtual void print() { cout << name << endl; }
    void printfdata() { cout << name << endl; }
protected:
    string name;
};
class FoxSon :public Fox
{
public:FoxSon(string name="子类") :name(name) {}
      void print() { cout << name << endl; }
      void printfdata() { cout << name << endl; }
protected:
        string name;
}; 
class A
{
public:
    virtual void print() 
    {
        cout << "A" << endl;
    }

};
class B
{
public:
    void print()
    {
        cout << "B" << endl;
    }

};
class C :public A, public B
{
    void print()
    {
        cout << "C" << endl;
    }


};
int main() 
{   //下行转换
    Fox* Pfox = new Fox;
    Pfox->print(); 
    Pfox->printfdata();
    FoxSon* PFoxSon = dynamic_cast<FoxSon*>(Pfox);
    if(PFoxSon!=nullptr)
    {
    PFoxSon->print();//下行转换调用父类全部错误
    PFoxSon->printfdata();
    }//dynamic_cast类型转换成功才会赋值,不然放回空 该PFoxSon并没有转换成功
    //交叉转换 用于多继承的才有交叉转换这个概念
    // 我们可以通过子类对象为桥梁 实现父类指针的互转
    
    A* PA = new C;
    PA->print();
    B* PB = dynamic_cast<B*>(PA);
    PB->print();//实现父类之间的转换我们称为交叉互转
    //while (1);
    return 0;
}

? ? ?reinterpret_cast

? ? ?将一个指针转换成数字,也可以重新转换成一个指针,可以用于函数指针上

#include<iostream>
using namespace std;
int Pmax(int a,int b) 
{
	return a > b ? a : b;
}
int main()
{
	int num = reinterpret_cast<int>(Pmax);
	cout << hex << num << endl;//转换成16进制会发现一模一样,我们也可以将这个数字转换成函数指针
	cout << Pmax << endl;//函数的地址
	auto Pmax = reinterpret_cast<int(*)(int, int)>(num);
	cout << Pmax(4, 5) << endl;//正常使用

}

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-06 15:04:14  更:2021-12-06 15:05:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:27:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码