C++面向对象编程 -- 继承、多态和文件操作

继承、多态和文件操作

  • 继承
    • 继承方式
    • 继承中的对象模型
    • 继承中的构造和析构
    • 继承中同名成员的处理方式
    • 继承同名静态成员处理方式
      • 多继承语法
      • 菱形继承
  • 多态
    • 多态的基本概念
    • 动态多态满足的条件
    • 多态的优点
  • 纯虚函数和抽象类
    • 纯虚函数
    • 虚析构和纯虚析构
  • 文件操作
    • 文本文件
      • 写文件
      • 文件打开方式
      • 读文件
    • 二进制文件
      • 读文件


继承

  • 继承是面向对象的三大特性之一
  • 好处:减少重复代码
  • 语法:class 子类 : 继承方式 父类
  • 子类:派生类
  • 父类:基类

继承方式

  • 语法:class 子类 : 继承方式 父类
  • 继承方式:
    ○ 公共继承
    publicprotected 权限不变
    父类中私有成员不能访问
    ○ 保护继承
    publicprotected 都成为 protected 成员
    ■ 父类中私有成员不能访问
    ○ 私有继承
    ■ 父类中的所有成员都变成 private
    ■ 父类中私有成员不能访问

继承中的对象模型

● 父类中所有非静态成员属性都会被子类继承
● 父类中的私有成员只是被编译器隐藏,不能访问,但是实际被继承了


继承中的构造和析构

● 继承中的构造:先构造父类,再构造子类
● 继承中的析构:先析构子类,再析构父类


继承中同名成员的处理方式

  • 问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或者父类中同名的数据呢?
    ○ 访问子类同名成员:直接访问即可;
    ○ 访问父类同名成员:需要加作用域;
    ■ 如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类所有同名的成员函数;
    ■ 如果想访问,必须加作用域

继承同名静态成员处理方式

  • 问题:继承中同名的静态成员在子类对象上如何进行访问?
    ○ 静态成员和非静态成员出现同名,处理方式一致;
    ○ 访问子类:直接访问;
    ○ 访问父类:加作用域

多继承语法

  • C++允许一个类多继承;
    ○ 语法:class 子类 : 继承方式 父类, 继承方式 父类, ……
    ○ 多继承可能会引发父类中有同名成员出现,需要加作用域区分;
    ○ C++实际开发中不建议使用多继承;

菱形继承

  • 菱形继承概念:
    ○ 两个派生类继承同一个基类;
    ○ 又是某个类同时继承两个派生类;
    ○ 这种继承成为菱形继承或者钻石继承;
  • 关键字:virtual
    在继承的时候加上关键字后变成虚继承;
    ○ 虚继承中相同的数据是共享的;

多态

多态的基本概念

  • 多态是面向对象的三大特征之一
    ○ 多态的分为两类
    静态多态:函数重载和运算符重载属于静态多态,服用函数名;
    ■ 动态多态:派生类和虚函数实现运行时多态;

    ○ 静态多态和动态多态的区别
    ■ 静态多态的函数地址早绑定 - 编译阶段确定函数地址
    ■ 动态多态的函数地址晚绑定 - 运行阶段确定函数地址

动态多态满足的条件

  • 有继承关系;
  • 子类重写父类的虚函数;
    ○ 虚函数:函数前用virtual关键字说明;
    ○ 重写:函数返回值类型相同、函数名相同、函数参数列表完全相同;

多态的优点

  • 代码组织结构清晰;
  • 可读性强;
  • 利于前期和后期的扩展以及维护;
    ○ 开闭原则:对扩展进行开放,对修改进行关闭;

纯虚函数和抽象类

纯虚函数

  • 语法:virtual 返回值类型 函数名 (参数列表) = 0;
  • 当类中有了纯虚函数,这个类成为抽象类;
  • 抽象类特点:
    ○ 无法实例化对象;
    ○ 子类必须重写抽象类中的纯虚函数,否则也属于抽象类;

虚析构和纯虚析构

  • 问题:多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码;
    ○ 父类指针指向父类的对象,则在父类析构的时候,不会调用子类中的析构函数,导致子类如果有堆区属性,出现泄漏。

  • 解决方式:将父类中的析构函数改为虚析构或者纯虚析构;
    ○ 纯虚析构:既要声明也要实现,及需要在内外将其实现,不然不能通过编译;

  • 虚析构和纯虚析构的共性
    ○ 可以解决父类指针释放子类对象;
    ○ 都需要具体的函数实现;

  • 虚析构和纯虚析构的区别
    ○ 如果是纯虚析构,该类属于抽象类,无法实例化;

  • 虚析构语法:virtual ~类名(){}

  • 纯虚析构语法:virtual ~类名() = 0;


文件操作

  • 程序运行时产生的数据都属于零时数据,程序一旦运行结束都会被释放;

  • 通过文件可以将数据持久化;

  • C++中对文件操作需要包含头文件;

  • 文件类型分为两种:
    ○ 文本文件:文件以文本的ASCII码形式存储在计算机中,用户一般不能直接读懂它们;

  • 操作文件的三大类:
    ○ ofstream:写操作
    ○ ifstream:读操作
    ○ fstream:读写操作


文本文件

写文件

  • 步骤
    ○ 包含头文件:#include
    ○ 创建流对象: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 
#include 
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
    ○ 创建流对象:ifstream ifs;
    ○ 打开文件并判断文件是否打开成功:ifs.open("文件路径",打开方式);
    ○ 读数据:四种读方式
    ○ 关闭文件:ifs.close();
//
// Created by NJUST'er'wang's on 2022/1/18.
//文本文件 读文件
#include 
#include 
#include 
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 
#include 
#include 

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 
#include 
#include 

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

*/

你可能感兴趣的:(C++学习,c++,开发语言)