继承、多态和文件操作
继承
- 继承是面向对象的三大特性之一
- 好处:减少重复代码
- 语法:
class 子类 : 继承方式 父类
- 子类:派生类
- 父类:基类
继承方式
- 语法:
class 子类 : 继承方式 父类
- 继承方式:
○ 公共继承
■public
和protected
权限不变
■ 父类中私有成员不能访问
○ 保护继承
■public
和protected
都成为protected
成员
■ 父类中私有成员不能访问
○ 私有继承
■ 父类中的所有成员都变成private
■ 父类中私有成员不能访问
继承中的对象模型
● 父类中所有非静态成员属性都会被子类继承
● 父类中的私有成员只是被编译器隐藏,不能访问,但是实际被继承了
继承中的构造和析构
● 继承中的构造:先构造父类,再构造子类
;
● 继承中的析构:先析构子类,再析构父类
;
继承中同名成员的处理方式
- 问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或者父类中同名的数据呢?
○ 访问子类同名成员:直接访问即可;
○ 访问父类同名成员:需要加作用域;
■ 如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类所有同名的成员函数;
■ 如果想访问,必须加作用域;
继承同名静态成员处理方式
- 问题:继承中同名的静态成员在子类对象上如何进行访问?
○ 静态成员和非静态成员出现同名,处理方式一致;
○ 访问子类:直接访问;
○ 访问父类:加作用域
;
多继承语法
- C++允许一个类多继承;
○ 语法:class 子类 : 继承方式 父类, 继承方式 父类, ……
;
○ 多继承可能会引发父类中有同名成员出现,需要加作用域区分;
○ C++实际开发中不建议使用多继承;
菱形继承
- 菱形继承概念:
○ 两个派生类继承同一个基类;
○ 又是某个类同时继承两个派生类;
○ 这种继承成为菱形继承或者钻石继承; - 关键字:
virtual
○ 在继承的时候加上关键字后变成虚继承;
○ 虚继承中相同的数据是共享的;
多态
多态的基本概念
- 多态是面向对象的三大特征之一
○ 多态的分为两类
■ 静态多态:函数重载和运算符重载属于静态多态,服用函数名;
■ 动态多态:派生类和虚函数实现运行时多态;
○ 静态多态和动态多态的区别
■ 静态多态的函数地址早绑定 - 编译阶段确定函数地址;
■ 动态多态的函数地址晚绑定 - 运行阶段确定函数地址;
动态多态满足的条件
- 有继承关系;
- 子类重写父类的虚函数;
○ 虚函数:函数前用virtual关键字说明;
○ 重写:函数返回值类型相同、函数名相同、函数参数列表完全相同;
多态的优点
- 代码组织结构清晰;
- 可读性强;
- 利于前期和后期的扩展以及维护;
○ 开闭原则:对扩展进行开放,对修改进行关闭;
纯虚函数和抽象类
纯虚函数
- 语法:
virtual 返回值类型 函数名 (参数列表) = 0;
- 当类中有了纯虚函数,这个类成为抽象类;
- 抽象类特点:
○ 无法实例化对象;
○ 子类必须重写抽象类中的纯虚函数,否则也属于抽象类;
虚析构和纯虚析构
-
问题:多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码;
○ 父类指针指向父类的对象,则在父类析构的时候,不会调用子类中的析构函数,导致子类如果有堆区属性,出现泄漏。 -
解决方式:将父类中的析构函数改为虚析构或者纯虚析构;
○ 纯虚析构:既要声明也要实现,及需要在内外将其实现,不然不能通过编译; -
虚析构和纯虚析构的共性
○ 可以解决父类指针释放子类对象;
○ 都需要具体的函数实现; -
虚析构和纯虚析构的区别
○ 如果是纯虚析构,该类属于抽象类,无法实例化; -
虚析构语法:
virtual ~类名(){}
-
纯虚析构语法:
virtual ~类名() = 0;
文件操作
-
程序运行时产生的数据都属于零时数据,程序一旦运行结束都会被释放;
-
通过文件可以将数据持久化;
-
C++中对文件操作需要包含头文件
<fstream>
; -
文件类型分为两种:
○ 文本文件:文件以文本的ASCII码形式存储在计算机中,用户一般不能直接读懂它们; -
操作文件的三大类:
○ ofstream:写操作
○ ifstream:读操作
○ fstream:读写操作
文本文件
写文件
- 步骤
○ 包含头文件:#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
//
// Created by NJUST'er'wang's on 2022/1/18.
//文本文件 写文件
#include <iostream>
#include <fstream>
using namespace std;
void test01(){
/* 1、包含头文件
* 2、创建流对象
* 3、指定打开方式
* 4、写内容
* 5、关闭文件
* */
ofstream ofs;
ofs.open("test.txt",ios::out);
ofs << "姓名:王东" <<endl;
ofs <<"性别:男"<<endl;
ofs.close();
}
int main(){
test01();
return 0;
}
读文件
- 读文件与写文件步骤相似,但是读取方式相对于比较多;
- 步骤
○ 包含头文件:#include <fstream>
○ 创建流对象:ifstream ifs
;
○ 打开文件并判断文件是否打开成功:ifs.open("文件路径",打开方式);
○ 读数据:四种读方式;
○ 关闭文件:ifs.close();
//
// Created by NJUST'er'wang's on 2022/1/18.
//文本文件 读文件
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
void test01(){
/* 1、包含头文件
* 2、创建流对象
* 3、打开文件 并且判断是否打开成功
* 4、读数据
* 5、关闭文件
* */
ifstream ifs;
ifs.open("test.txt",ios::in);
if(!ifs.is_open()) {
cout<<"打开文件失败!"<<endl;
return ;
}
- 读数据
//first method
char buf[1024] = {0};
while (ifs >> buf)
{
cout<<buf<<endl;
}
//second method
char buf2[1024] = {0};
while(ifs.getline(buf2,sizeof(buf2)))
{
cout<<buf2<<endl;
}
//third method
string str;
while (getline(ifs,str))
{
cout<<str<<endl;
}
//forth method
char ch;
while ((ch = ifs.get()) != EOF ) //EOF end of file
{
cout<<ch;
}
ifs.close();
}
int main()
{
test01();
return 0;
}
/*控制台输出:
姓名:王东
性别:男
*/
二进制文件
- 以二进制的方式对文件进行读写操作;
- 打开方式指定为:
ios::binary
写文件 - 二进制文件主要利用流对象调用成员函数
write
- 函数原型:
ostream& write(const char * buffer,int len);
- 参数解释:字符指针
buffer
指向内存中一段存储空间。len
是读写的字节数;
//
// Created by NJUST'er'wang's on 2022/1/18.
//二进制文件 写文件
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
class Person {
char name[10];
int age;
public:
Person(char name[10], int age) {
strcpy(this->name, name);
this->age = age;
}
char *getName() { return name; }
int getAge() { return age; }
};
void test01() {
/* 1、包含头文件
* 2、创建流对象
* 3、打开文件
* 4、写文件
* 5、关闭文件
* */
ofstream ofs;
ofs.open("Person.txt", ios::out | ios::binary);
Person p = {"唐潇", 24};
ofs.write((const char *) &p, sizeof(Person));
ofs.close();
}
int main() {
test01();
return 0;
}
读文件
- 二进制方式读文件主要利用流对象调用成员函数
read
; - 函数原型:
istream& read(char *buffer,int len);
- 参数解释:字符指针
buffer
指向内存中一段存储空间。len
是读写的字节数;
//
// Created by NJUST'er'wang's on 2022/1/18.
//二进制文件的读文件
#include <iostream>
#include <fstream>
#include <cstring>
using namespace std;
class Person {
char name[10];
int age;
public:
Person() {}
Person(char name[10], int age) {
strcpy(this->name, name);
this->age = age;
}
char *getName() { return name; }
int getAge() { return age; }
};
void test01() {
/* 1、包含头文件
* 2、创建流对象
* 3、判断是否打开成功
* 4、读文件
* 5、关闭文件
* */
ifstream ifs;
ifs.open("Person.txt", ios::in | ios::binary);
if (!ifs.is_open()) {
cout << "打开文件失败!" << endl;
return;
}
Person p;
ifs.read((char *) &p, sizeof(Person));
cout << "name: " << p.getName() << ' ' << "age: " << p.getAge() << endl;
ifs.close();
}
int main() {
test01();
return 0;
}
/*
name: 唐潇 age: 24
*/