文章目录

  • 一、继承
    • 1 继承中的对象模型
    • 2 继承中构造和析构顺序
    • 3 继承中同名属性和函数处理方式(隐藏)
    • 4 继承同名静态成员属性和函数处理方式
    • 5 多继承语法
    • 6 菱形继承(虚继承)
  • 二、多态
    • 1 多态的原理剖析(虚函数表指针)
    • 2 纯虚函数和抽象类
    • 3 虚析构和纯虚析构(解决父类指针管理堆区子类析构函数)
  • 三、文件操作
    • 1 文本文件
      • 1.1 写文件(ofstream)
      • 1.2 读文件(ifstream)
    • 2 二进制文件
      • 2.1 写文件(ofstream)
      • 2.2 读文件(ifstream)

一、继承

  • 利用继承的技术,减少重复代码
  • 语法: class 子类名:继承方式 父类名

继承方式一共有三种:

  • 公共继承(public)
  • 保护继承(protected)
  • 私有继承(private)

1 继承中的对象模型

  • 父类中私有成员也是被子类继承下去了,只是由于编译器给隐藏后访问不到
  • 在父类中所有非静态的成员属性都会被子类继承下去
// 继承中的对象模型
class Base
{public:int m_A;protected:int m_B;private:int m_C;
};class Son : public Base
{public:int m_D;
};
void test01()
{// 在父类中所有非静态的成员属性都会被子类继承下去// 父类中私有成员属性 是被编译器隐藏了,因此是访问不到。但是确实被继承下去了cout << "size of Son= " << sizeof(Son) << endl;
}
int main()
{test01();system("pause");return 0;
}

2 继承中构造和析构顺序

  • 创建子类对象,也会调用父类的构造函数
  • 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反。(栈的先进后出)

3 继承中同名属性和函数处理方式(隐藏)

  • 访问子类同名成员,直接访问即可 s.m_A
  • 访问父类同名成员,需要加作用域 s.Base::m_A
    • 子类会隐藏掉父类中所有同名成员函数(重载的部分也隐藏)
  • 若两个函数参数相同,但是基类函数不是虚函数,则会被隐藏。和重写的区别在于基类函数是否是虚函数。
  • 若两个函数参数不同,无论基类函数是不是虚函数,都会被隐藏,两个函数在同一个类中。和重载的区别在于两个函数不在同一个类中。
// 继承中同名成员处理
class Base
{public:Base(){m_A = 100;}void func(){cout << "Base-func()调用" << endl;}void func(int a){cout << "Son-func(int a)调用" << endl;}int m_A;
};class Son : public Base
{public:Son(){m_A = 200;}void func(){cout << "Son-func()调用" << endl;}int m_A;
};// 1. 同名成员属性处理
void test01()
{Son s;cout << "Son 下 s.m_A= " << s.m_A << endl;// 如果通过子类对象,访问到父类中同名成员,需要加作用域cout << "Base 下 s.m_A=" << s.Base::m_A << endl;
}// 2. 同名成员函数处理
void test02()
{Son s;s.func(); // 直接调用是子类中的同名成员s.Base::func();// 如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数// 如果想访问到父类中被隐藏的同名成员函数,需要加作用域// s.func(100);s.Base::func(100);
}
int main()
{test01();test02();system("pause");return 0;
}

4 继承同名静态成员属性和函数处理方式

静态成员变量的特点

  • 所以对象都共享同一份数据
  • 编译阶段就分配内存
  • 类内声明,类外初始化

静态成员函数的特点

  • 只能访问静态成员变量,不能访问非静态成员变量
  • 所以对象共享同一份函数实例

静态成员和非静态成员出现同名,处理方式一致:(只不过有两种访问方式:对象 和 类名)

  • 访问子类同名成员 直接访问即可 s.m_A Son::m_A
  • 访问父类同名成员 需要加作用域 s.Base::m_A Son::Base::m_A
// 继承中同名静态成员处理
class Base
{public:static int m_A;static void func(){cout << "Base-static void func()" << endl;}static void func(int a){cout << "Base-static void func(int a)" << endl;}
};
int Base::m_A = 100;class Son : public Base
{public:static int m_A;static void func(){cout << "Son-static void func()" << endl;}
};
int Son::m_A = 200;// 1. 同名静态成员属性
void test01()
{cout << "通过对象访问: " << endl;// 1、通过对象访问数据Son s;cout << "Son 下 m_A= " << s.m_A << endl;cout << "Base 下 m_A=" << s.Base::m_A << endl;// 2、通过类名访问cout << "通过类名访问: " << endl;cout << "Son 下 m_A= " << Son::m_A << endl;// 第一个::代表通过类名方式来访问,第二个::代表访问父类作用域下cout << "Base 下 m_A=" << Son::Base::m_A << endl;
}// 2. 同名静态成员函数处理
void test02()
{cout << "通过对象访问: " << endl;// 通过对象访问Son s;s.func();s.Base::func();// 通过类名访问cout << "通过类名访问: " << endl;Son::func();Son::Base::func(); //// 当子类与父类拥有同名的成员函数,子类会隐藏掉父类中所有同名成员函数,加作用域可以访问到父类中同名函数。Son::Base::func(100);
}
int main()
{test01();test02();system("pause");return 0;
}

5 多继承语法

  • C++实际开发中不建议用多继承
  • 语法class 子类:继承方式 父类1,继承方式 父类2...
  • 多继承可能会引发父类中有同名成员出现,需要加作用域区分s.Base1::m_A s.Base2::m_A
// 多继承语法
class Base1
{public:Base1(){m_A = 100;}int m_A;
};class Base2
{public:Base2(){m_A = 200;}int m_A;
};// 子类 需要继承Base1和Base2
// 语法: class 子类:继承方式 父类1,继承方式 父类2...
class Son : public Base1, public Base2
{public:Son(){m_C = 300;m_D = 400;}int m_C, m_D;
};void test01()
{Son s;sizeof(s);cout << "size of Son= " << sizeof(Son) << endl; // 16// 当父类中出现同名的成员cout << "Base1::m_A= " << s.Base1::m_A << endl;cout << "Base2::m_A= " << s.Base2::m_A << endl;
}
int main()
{test01();// test02();system("pause");return 0;
}

6 菱形继承(虚继承)

菱形继承概念: 两个派生类继承同一个基类,又有某个类同时继承着两个派生类。

解决方式(利用虚继承):

  • 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费及毫无意义
  • 利用虚继承可以解决菱形继承问题
  • 继承方式之前加入关键字 virtual 变为虚继承 class Sheep : virtual public Animal {};
// 动物类
class Animal
{public:int m_Age;
};// Animal称为 虚基类(最大的类)
class Sheep : virtual public Animal
{};// 驼类
class Tuo : virtual public Animal
{};// 羊驼类
class SheepTuo : public Sheep, public Tuo
{};void test01()
{SheepTuo st;st.Sheep::m_Age = 18;st.Tuo::m_Age = 28;// 当菱形继承,两个父类拥有相同数据,需要加以作用域区分cout << "st.Sheep::m_Age= " << st.Sheep::m_Age << endl;cout << "st.Tuo::m_Age= " << st.Tuo::m_Age << endl;cout << "st.m_Age = " << st.m_Age << endl; // 28// 利用虚继承可以直接访问,不会有二义性
}
int main()
{test01();system("pause");return 0;
}
  • 没用虚函数(sheepTuo)

  • 用虚函数(sheepTuo)(不会出现二义性)
  • 从父类中拿到的只是一个虚基类指针而已

二、多态

  • C++开发提倡利用多态设计程序架构,因为多态优点很多
  • 开闭原则:对拓展进行开放,对修改进行关闭

多态分为两类

  • 静态多态:函数重载 和 运算符重载 属于静态多态,复用函数名
  • 动态多态:派生类和虚函数实现运行时多态

静态多态和动态多态区别

  • 静态多态的函数地址早绑定------编译阶段确认函数地址
  • 动态多态的函数地址晚绑定------运行阶段确认函数地址

多态满足条件

  • 有继承关系
  • 父类必须写虚函数,子类可写可不写
  • 子类重写父类中的虚函数
    • 重写:函数(返回值类型 函数名 参数列表)完全一致称为重写

多态使用条件

  • 父类指针或引用(指针必须开辟堆区)指向子类对象
class Animal
{public:// 虚函数virtual void speak(){cout << "动物在说话" << endl;}
};class Cat : public Animal
{public:// 重写 函数的返回值类型 函数名和参数列表要完全相同void speak(){cout << "小猫在说话" << endl;}
};class Dog : public Animal
{public:void speak(){cout << "小狗在说话" << endl;}
};// 父类的指针或引用 指向子类对象
void doSpeak(Animal &animal) // Animal &animal =cat;
{                            // 子类可以转父类,父类不能转子类animal.speak();
}
void test01()
{Cat cat;doSpeak(cat);Dog dog;doSpeak(dog);
}
int main()
{test01();system("pause");return 0;
}

1 多态的原理剖析(虚函数表指针)

  • 子类继承父类,同时也继承了虚函数表
  • 虚函数表指针指向虚函数表(记录虚函数的内容)
    • 通俗讲就是虚函数表指针指向虚函数表本身,有重写就替换

  • Animal(父类)
  • Cat(子类)

2 纯虚函数和抽象类

  • 通常父类中虚函数的实现是毫无意义的,主要都是调用子类中重写的内容。因此可以将虚函数改为纯虚函数
  • 纯虚函数语法: virtual 返回值类型 函数名 (参数列表)=0;
  • 当类中有了纯虚函数,这个类也成为抽象类

纯虚函数特点

  • 提供接口(父类虚函数表指针指向子类对象)
  • 不做任何实现

抽象类特点

  • 无法实例化对象
  • 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
// 纯虚函数和抽象类
class Base
{public:// 1、无法实例化对象// 2、抽象类的子类必须要重写父类中的纯虚函数,否则也属于抽象类virtual void func() = 0; // 纯虚函数
};class Son : public Base
{public:virtual void func(){cout << "func函数调用" << endl;};
};void test01()
{Base *base = new Son;base->func();
}int main()
{test01();system("pause");return 0;
}

3 虚析构和纯虚析构(解决父类指针管理堆区子类析构函数)

  • 如果子类中有属性开辟到堆区(栈区自动释放),那么父类指针在释放时无法调用到子类的析构函数

    • 父类的引用和开辟到栈区的指针可以调用子类析构和构造
  • 解决方式:将父类中的析构函数改为虚析构或纯虚析构
  • 如果子类中没有堆区数据,可以不写虚析构或纯虚析构(最好有继承关系就写虚析构!

虚析构和纯虚析构共性:(都需要函数实现

  • 可以解决父类指针释放子类对象
  • 都需要有具体的函数实现

虚析构和纯虚析构区别

  • 如果是纯虚析构,该类属于抽象类,也无法实例化对象
  • 虚析构语法:virtual ~类名(){};
  • 纯虚析构语法:virtual ~类名()=0;
  • 纯虚析构函数必须有实现。

如果类包含纯虚析构函数,则必须为纯虚析构函数提供函数体

  • 原因是因为析构函数(与其它函数不同)实际上并未被 “重写”,而是始终以派生类的相反顺序调用它们。这意味着派生类的析构函数将首先被调用,然后调用基类的析构函数。
  • 如果未提供纯虚析构函数的定义,那么在销毁对象期间将调用什么函数体?
// 虚析构和纯虚析构
class Animal
{public:Animal(){cout << "Animal的构造函数调用" << endl;}// 利用虚析构可以解决,父类指针释放子类对象时不干净的问题// virtual ~Animal()// {//     cout << "Animal的虚析构函数调用" << endl;// }// 纯虚析构 需要声明也需要实现// 有了纯虚析构之后,这个类也属于抽象类,无法实例化对象virtual ~Animal() = 0;// 纯虚函数virtual void speak() = 0;
};// 只能在类外实现纯虚析构
Animal::~Animal() //添加这段代码就能正常运行
{cout << "Animal的纯虚析构函数调用" << endl;
}class Cat : public Animal
{public:Cat(string name){cout << "Cat的构造函数调用" << endl;m_Name = new string(name);}~Cat(){if (m_Name != NULL){cout << "Cat的析构函数调用" << endl;delete m_Name;m_Name = NULL;}}virtual void speak(){cout << *m_Name << "小猫在说话" << endl;}string *m_Name;
};void test01()
{Animal *animal = new Cat("Tom");animal->speak();// 开辟到堆区的指针,手动释放delete animal;
}
int main()
{test01();system("pause");return 0;
}Animal的构造函数调用
Cat的构造函数调用
Tom小猫在说话
Cat的析构函数调用
Animal的纯虚析构函数调用

三、文件操作

  • 程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放
  • 通过文件可以将数据持久化
  • C++中对文件操作需要包含头文件 <fstream>(文件流)

文件类型分为两种

  • 文本文件(可以看到)

    • 文件以文本的ASCII码形式存储在计算机中
  • 二进制文件
    • 文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们

操作文件的三大类

  • ofstream:写操作(输出)
  • ifstream:读操作(输入)
  • fstream:读写操作(输入、输出)

1 文本文件

1.1 写文件(ofstream)

写文件步骤如下

  • 包含头文件 #include<fstream>
  • 创建流对象 ofstream ofs; (名字无所谓)
  • 打开文件 ofs.open("文件路径",打开方式);
  • 写数据 ofs<<"写入的数据";
  • 关闭文件 ofs.close();

文件打开方式

打开方式 解释
ios::in 为读文件而打开文件
ios::out 为写文件而打开文件
ios::ate 初始位置: 文件尾
ios::app 追加方式写文件
ios::trunc 如果文件存在先删除,再创建
ios::binary 二进制方式

注意:文件打开方式可以配合使用,使用 | 操作符

  • 例如:用二进制的方式写文件ios::binary | ios::out
#include <fstream>// 文本文件 写文件
void test01()
{// 1.包含头文件 fstream// 2.创建流对象ofstream ofs;// 3.指定打开方式// 没有指定路径和当前.cpp的目录一样// 路径可以写成绝对路径或者相对路径,或直接写文件名ofs.open("test1.txt", ios::out);// 4.写内容ofs << "姓名:张三" << endl;ofs << "性别:男" << endl;ofs << "年龄: 18" << endl;// 5.关闭文件ofs.close();
}int main()
{test01();system("pause");return 0;
}

1.2 读文件(ifstream)

  • 读文件与写文件步骤相似,但是读取方式相对于比较多

读文件步骤如下

  • 包含头文件 #include<fstream>
  • 创建流对象 ifstream ifs;
  • 打开文件并判断文件是否打开成功 ifs.open("文件路径",打开方式);
    • 判断文件是否打开成功 ifs.is_open()
  • 读数据 四种方式读取(程序中)
  • 关闭文件 ifs.close();

文件打开方式

打开方式 解释
ios::in 为读文件而打开文件
ios::out 为写文件而打开文件
ios::ate 初始位置: 文件尾
ios::app 追加方式写文件
ios::trunc 如果文件存在先删除,再创建
ios::binary 二进制方式
#include <fstream>
#include <string>// 文本文件 读文件
void test01()
{// 1.包含头文件// 2.创建流对象ifstream ifs;// 3.打开文件并判断是否打开成功ifs.open("test.txt", ios::in);// 判断文件是否打开成功if (!ifs.is_open()){cout << "文件打开失败了" << endl;return;}// 4.读数据// 第一种char buf[1024] = {0};while (ifs >> buf) // 读到头返回一个假的标志{cout << buf << endl;}// 第二种char buf[1024] = {0};// ifs对象内有成员函数// ifs.getline(char *(传入首地址),sizeof()(统计字节数))while (ifs.getline(buf, sizeof(buf))){ // getline()返回bool类型cout << buf << endl;}// 第三种(较好)string buf;// 全局函数getlinewhile (getline(ifs, buf)){cout << buf << endl;}// 第四种  不推荐char c;                        // EOF判断是否没有到达文件尾while ((c = ifs.get()) != EOF) // EOF end of file{                              // 中文每个占多个字节,用endl就分开了cout << c;}// 5.关闭文件ifs.close();
}
int main()
{test01();return 0;
}

2 二进制文件

文件打开方式

打开方式 解释
ios::in 为读文件而打开文件
ios::out 为写文件而打开文件
ios::ate 初始位置: 文件尾
ios::app 追加方式写文件
ios::trunc 如果文件存在先删除,再创建
ios::binary 二进制方式
  • 以二进制的方式对文件进行读写操作
  • 打开方式要指定为 ios::binary

2.1 写文件(ofstream)

  • 二进制方式写文件主要利用输出流ofstream调用成员函数 ofs.write()

    • wirte()函数原型:ofs.write(const char* buffer, sizeof(len);
#include <fstream>// 二进制文件 写文件
class Person
{public:// 对文件操作,写字符串最好不要用C++的string,会出现问题char m_Name[64]; // 姓名int m_Age;       // 年龄
};
void test01()
{// 1.包含头文件// 2.创建流对象// ofs对象有构造函数,直接初始化ofstream ofs("person.txt", ios::out | ios::binary);// 3.打开文件// ofs.open("person.txt", ios::out | ios::binary);// 4.写文件Person p = {"张三", 18};// 强制转换,原来为Person*ofs.write((const char *)&p, sizeof(Person));// 5.关闭文件ofs.close();
}
int main()
{test01();return 0;
}

2.2 读文件(ifstream)

  • 二进制方式读文件主要利用输入流ifstream调用成员函数 ifs.read()
  • read()函数原型:ifs.read(char *buffer,sizeof(len));
#include <fstream>class Person
{public:char m_Name[64]; // 姓名int m_Age;       // 年龄
};// 二进制文件读文件
void test01()
{// 1.包含头文件// 2.创建流对象ifstream ifs;// 3.打开文件 判断文件是否打开成功ifs.open("person.txt", ios::in | ios::binary);if (!ifs.is_open()){cout << "文件打开失败" << endl;return;}// 4.读文件Person p;ifs.read((char *)&p, sizeof(Person));cout << "姓名为:" << p.m_Name << endl;cout << "年龄为:" << p.m_Age << endl;// 5.关闭文件ifs.close();
}
int main()
{test01();return 0;
}

【C++】继承/多态/文件相关推荐

  1. -1-2 java 面向对象基本概念 封装继承多态 变量 this super static 静态变量 匿名对象 值传递 初始化过程 代码块 final关键字 抽象类 接口

    java是纯粹的面向对象的语言 也就是万事万物皆是对象 程序是对象的集合,他们通过发送消息来相互通信 每个对象都有自己的由其他的对象所构建的存储,也就是对象可以包含对象 每个对象都有它的类型  也就是 ...

  2. c语言编程 菲薄拉,C语言设计模式-封装-继承-多态

    快过年了,手头的工作慢慢也就少了,所以,研究技术的时间就多了很多时间,前些天在CSDN一博客看到有大牛在讨论C的设计模式,正好看到了,我也有兴趣转发,修改,研究一下. 记得读大学的时候,老师就告诉我们 ...

  3. Java继承_Hachi君浅聊Java三大特性之 封装 继承 多态

    Hello,大家好~我是你们的Hachi君,一个来自某学院的资深java小白.最近利用暑假的时间,修得满腔java语言学习心得.今天小宇宙终于要爆发了,决定在知乎上来一场根本停不下来的Hachi君个人 ...

  4. python多态的三种表现形式_python小结----面向对象的三大特征(封装,继承,多态)

    面向对象的三大特征: 封装,继承,多态 面向对象的编程思想核心:高类聚,低耦合–程序的设计模式范畴 封装 什么是封装: 在面向对象编程的思想中,对代码进行高度封装,封装又叫包装 封装就是指将数据或者函 ...

  5. linux c 多态原理,看了所谓的面向对象中靠继承多态实现的所谓重用 哥笑了

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 这种重用不过还是引用别的类的函数或其它成员元素 我老听有些不懂编程却爱喷的人说什么面向对象代码可重用性"强" C写的代码 完全不能重用 ...

  6. 深度探索C++ 对象模型(7)-Data member的布局(无继承、继承无多态、继承多态、多层继承)

    无继承 继承无多态 继承多态 虚表 : 用来存放基类的每一个虚函数,再加上首位的一个slots(支持RTTI). 每个class object导入一个vptr,提供执行期的链接,使得每一个class ...

  7. python 参数类型的多态_【Python】面向对象:类与对象\封装\继承\多态

    六.Python面向对象--类与对象\封装\继承\多态 1.什么是面向对象编程 1.1 程序设计的范式:程序可控,易于理解 1.2 抽象并建立对象模型 1.3 程序是不同对象相互调用的逻辑.每个对象在 ...

  8. Winform打砖块游戏制作step by step第5节---重构代码,利用继承多态

    一 引子 为了让更多的编程初学者,轻松愉快地掌握面向对象的思考方法,对象继承和多态的妙用,故推出此系列随笔,还望大家多多支持. 二 本节内容---重构代码,利用继承多态 1. 主界面截图如下: 2.  ...

  9. Java继承多态经典案例分享

    今天动力节点java培训机构小编为大家分享Java继承多态经典案例,希望通过此文能够帮助到大家,下面就随小编一起看看Java继承多态经典案例. public class A { public Stri ...

最新文章

  1. 设置IDEA最多同时打开的窗口数量为100
  2. python opencv 灰度图非局部平均去噪
  3. 荣耀有可能搭载鸿蒙系统吗,如果荣耀Magic3搭载了屏下镜头和鸿蒙系统,你会做第一批吗?...
  4. 贪心算法--多处最优服务次序问题
  5. 君正T20平台生成jffs2格式rootfs
  6. Not Equal on a Segment CodeForces - 622C
  7. 一个按钮触发多个a标签,只有一个可以下载,其他的window.open()被浏览器拦截...
  8. 大数据集群跨多版本升级、业务0中断,只因背后有TA
  9. Chrome开发者工具使用小技巧
  10. 吐槽Javascript系列三:数组的陷阱
  11. OpenCV-图像锐化
  12. podman 在 windows 安装
  13. Allegro给一个网络赋默认值,取消默认值
  14. winform c# chart控件添加边界值线条以及扩展性功能
  15. MySQL安装配置教程(超级详细、保姆级)
  16. 计算机无法加载操作系统,由于关键系统驱动程序丢失或包含错误,因此无法加载操作系统。解决方案...
  17. Matlab2019b中配置最小均方误差滤波器(dsp.LMSFilter)详细设置
  18. Excel怎么快速提取图片的主色调?
  19. 安装JDK8时错误1335的解决
  20. iwatch表盘壁纸图片_iwatch壁纸大全卡西欧表盘app

热门文章

  1. 从底层到算法 — 2020年最全的大厂面试题
  2. JS判断字符串长度的几种方法(区分中文和英文)
  3. 配置Nginx时一定要小心(1)——目录拼写问题。
  4. EXCEL如何快速【选中】或【跳到】当前列的最后一个数据?
  5. ZZULIOJ.1221: The Other Part of DNA
  6. 从零开始学习信号完整性--6-带宽
  7. 【Python】Python使用you-get库下载网址-公众号视频
  8. 三星手机没有信号网络连接到服务器,手机没信号的原因以及解决方法
  9. Win11更改磁盘驱动器号的方法
  10. Redsi通过geo计算距离