本阶段主要针对C++面向对象编写,以及文件操作
目录
第三部分 C++核心编程二
1,面向对象
1.1封装
1.1.1struct和class区别
1.1.2成员属性设置为私有
1.2对象的初始化和清理
1.2.1构造函数和析构函数
1.2.2构造函数的分类及调用
1.2.3拷贝构造函数调用时机
1.2.4构造函数调用规则
1.2.5深拷贝与浅拷贝
1.2.6初始化列表
1.2.7类对象作为类成员
1.2.8静态成员
1.3C++对象模型和this指针
1.3.1成员变量和成员函数分开存储
1.3.2this指针概念
1.3.3空指针访问成员函数
1.3.4const修饰成员函数
1.4友元函数
1.5运算符重载
1.5.1加号运算符重载
1.5.2运算符重载
1.5.3递增运算符重载
1.5.4赋值运算符重载
1.5.5关系运算符重载
1.5.6函数调用运算符重载
1.6继承
1.6.1 继承方式
1.6.2继承中的对象模型
1.6.3继承中构造和析构顺序
1.6.4继承同名成员处理方式
1.6.5继承同名静态成员处理方式
1.6.6多继承语法
1.6.7菱形继承
1.7多态
1.7.1多态的基本概念
1.7.2 纯虚函数和抽象类
1.7.3虚析构和纯虚析构
2,文件操作
2.1文本文件
2.1.1写文件
2.1.2读文件
2.2二进制文件
2.2.1写文件
2.2.2读文件
C++基础知识完结散花
C++面向对象的三大特性为:封装,继承,多态
关于对象的说法:万事万物皆为对象,对象有其属性和行为
eg:
人可以作为对象,属性有:姓名,身高,体重,年龄
车也可以作为对象,属性有:轮胎,方向盘,车灯
具有相同性质的对象,可以抽象称为类。
封装意义:
语法:class 类名{ 访问权限: 属性/行为 };
如下:创建一个圆类
#include
#include
using namespace std;
#define PI 3.14
class Circle
{
public:
int R; //属性
double Rlong() //行为
{
return 2 * PI*R;
}
};
int main()
{
Circle c1; //实例化,创建一个类
c1.R = 2;
cout << "圆的周长为" << c1.Rlong() << endl;
system("pause");
return 0;
}
类在设计时,可以把属性和行为放在不同的权限下,加以控制
三种访问权限:
两者的区别在于默认的访问权限不同
区别:
class Test1
{
int a; //默认为私有,不可访问
};struct Test2
{
int b; //默认为公有,可以访问
};
优点:
#include
#include
using namespace std;
class Test1
{
public:
void set(int n) //通过函数给私有变量赋值
{
a = n;
}
int get() //通过函数获得私有变量的值
{
return a;
}
private:
int a;
};
int main()
{
Test1 s1;
s1.set(10);
cout << s1.get() << endl;
system("pause");
return 0;
}
对象的初始化和清理十分重要
如果我们不提供构造和析构函数,则编译器会提供编译的构造函数和析构函数说空实现
构造函数语法:类名(){}
Test1()
{
cout << "Test1的构造函数调用" << endl;
}
析构函数语法:~类名(){}
~Test1()
{
cout << "Test1的析构函数调用" << endl;
}
两种分类方式
1,按参数分类:
2,按类型分类
Test1() //无参构造
{
cout << "Test1的构造函数调用" << endl;
}
Test1(int a) //有参构造
{
cout << "Test1的有参构造函数" << endl;
}
Test1(const Test1 &p) //拷贝构造函数
{
cout << "Test1的拷贝构造函数" << endl;
}
三种调用方法
Test1 p; //默认构造函数调用
Test1 p_1(10); //有参构造函数
Test1 p_2(p); //拷贝构造函数
Test1 pp_1 = Test1(10);//有参构造
Test1 pp_2 = Test1(pp_1);//拷贝构造
Test1 ppp_1 = 10; //相当于 Test1 p_4=Test1(10)
Test1 ppp_2 = ppp_1;
匿名对象:显示法中的 Test1(10)
这一行执行完,匿名对象就会立即会收掉匿名对象,但是仍然调用了构造函数和析构函数
注意事项:
Test1(p); //编译器会认为是Test1(p)=Test1 p;所以此时对象p就被创建出来了
三种情况:
void test_1()
{
Test1 p(10);
Test1 p1(p);
}
void do1(Test1 p)
{}
void test_2()
{
Test1 p;
}这个地方可能由于编译器优化,结果会不一样,
推荐一篇我搜了几个小时找到的贺老师的文章:https://helijian.blog.csdn.net/article/details/50977946
Test1 do2()
{
Test1 p(10);
return p;
}
void test_3()
{
Test1 p = do2();
}
默认情况下,C++编译器至少给一个编译器添加3个函数
调用规则:
主要是当类的属性有指针时,需要用到深拷贝
(仅仅写了一个拷贝构造函数,用于解释深拷贝,其它函数没写)
class Test1{
public:
Test1(const Test1 &p)
{
a = p.a;
//b = p.b; 编译器浅拷贝的做法,次数运用到指针是错误的
b = new int(*p.b);
}
private:
int a;
int *b;
};
作用:C++提供了初始化列表语法,用于初始化列表
语法:构造函数():属性1(值1),属性2(值2).....{}
class Test1{
public:
Test1(int aa, int bb) :a(a), b(&bb)
{}
private:
int a;
int *b;
};
C++类中的成员可以是另一个类的对象,我们称该成员为 对象成员
class A{};
class B
{
A a;
};
当创建B对象时,先构造类对象,再构造本身;析构的时候,先析构自己,再析构类对象
静态成员就是在成员变量和成员函数之前加上ststic关键字,称为静态成员
静态成员分为:
1,静态成员变量
static void show()
{
a = 20;
cout << "访问静态成员函数" << endl;
}
2,静态成员函数
static int a;
访问方式:
A b;
b.show(); //通过对象A::show(); //通过类名
#include
using namespace std;
class A
{
public:
static void show()
{
a = 20;
cout << "访问静态成员函数" << endl;
}
private:
static void showP()
{
a = 20;
cout << "私有静态成员函数" << endl;
}
int b;
static int a;
};
int A::a = 10;
int main()
{
A b;
b.show(); //通过对象
A::show(); //通过类名
//A::showP(); //错误,因为不能访问私有静态成员函数
system("pause");
return 0;
}
在C++中,类的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上
this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一种指针
this指针不需要定义,直接使用即可
this指针的用途:
this指针的本质是指针常量,指针的指向是不可以修改的
A *const this
#include
using namespace std;
class A
{
public:
A(int a)
{
this->a = a;
}
A& add(A &p) //如果此处不是用引用返回,则每次返回都会创建一个新的对象
{
this->a += p.a;
return *this;
}
int a;
};
int main()
{
A m(10);
A n(10);
n.add(m).add(m).add(m);
cout << n.a << endl;
system("pause");
return 0;
}
C++中空指针也是可以使用成员函数的,但是也要注意有没有用到this指针
如果用到this指针,需要加以判断代码的健壮性
即:调用的成员函数不能包含属性等东西,因为this为空,无法显示
#include
using namespace std;
class A
{
public:
void show()
{
cout << "打印***" << endl;
}
void put()
{
//cout << a << endl; //错误;此处a相当于this->a,this为空,所以无法输出;
}
private:
int a;
};
void test()
{
A *a = NULL;
a->show();
a->put();
}
int main()
{
test();
system("pause");
return 0;
}
常函数:
常对象:
#include
using namespace std;
class A
{
public:
void change_1()
{
a = 100;
b = 100;
}
//在成员函数后面加const,修饰的是this指针,让指针指向的值也不可以修改
void change_2() const //相当于 const A* const this
{
//a = 100; //不可以修改
b = 100; //特殊变量,可以修改
}
int a;
mutable int b; //特殊变量,即使在常函数中也可以修改
};
void test()
{
A p;
p.a = 100;
p.b = 100;
p.change_1();
p.change_2();
}
void test2()
{
const A q;
//q.a = 100; //常对象不能修改普通值
q.b = 100;
//q.change_1(); //错误,常对象只能调用常函数,因为普通成员函数可以修改属性
q.change_2();
}
int main()
{
test();
system("pause");
return 0;
}
友元的关键字:friend
三种实现形式:
即:当全局函数中的对象想要访问类中的私有属性时,需要将该全区函数在类中加以声明为friend类型
class Student
{
//此时全局函数show是类Student的好朋友,可以访问类的私有成员
friend void show(Student &a); //此时全局函数中的对象都可以访问类中的私有属性
........
#include
using namespace std;
#include
class Student
{
friend void show(Student &a);
public:
Student()
{
age = 18;
name = "小明";
}
string name;
private:
int age;
};
void show(Student &a)
{
cout << a.name << endl;
cout << a.age << endl;
}
int main()
{
Student m;
show(m);
system("pause");
return 0;
}
class Class
{
..........
};class Student
{
friend class Class;
.........
#include
using namespace std;
#include
class Student;
class Class
{
public:
Class();
void show();
private:
Student *s;
};
class Student
{
friend class Class;
public:
Student()
{
age = 18;
name = "小明";
}
string name;
private:
int age;
};
Class::Class()
{
s = new Student;
}
void Class::show()
{
cout << s->age << endl;
cout << s->name << endl;
}
int main()
{
Class a;
a.show();
system("pause");
return 0;
class Class
{
..........
};class Student
{
friend void Class::show(); //和类做友元类似,此处把类变成了函数
.........
#include
using namespace std;
#include
class Student;
class Class
{
public:
Class();
void show();
void show1();
private:
Student *s;
};
class Student
{
friend void Class::show();
public:
Student()
{
age = 18;
name = "小明";
}
string name;
private:
int age;
};
Class::Class()
{
s = new Student;
}
void Class::show()
{
cout << s->age << endl; //类的友元函数,可以访问私有属性
cout << s->name << endl;
}
void Class::show1()
{
// cout << s->age << endl; //不能访问私有属性
cout << s->name << endl;
}
int main()
{
Class a;
a.show();
a.show1();
system("pause");
return 0;
}
概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
实现两个自定义数据类型相加的运算
和正常的相加思路类似,只不过这里需要引用其他的对象作为参数;
//成员函数实现“+”运算符重载 p1=p2+p3;类似于:p1=p2.operator+(p3);
Class operator+(const Class &p){
Class q(0, 0);
q.a = this->a + p.a;
q.b = this->b + p.b;
return q;
}
//全局函数实现“+”运算符重载 p1=p2+p3;类似于:p1=operator+(p2,p3);
Class operator+(const Class &p1, const Class &p2){
Class q(0,0);
q.a = p1.a + p2.a;
q.b = p1.b + p2.b;
return q;
}
#include
using namespace std;
#include
class Class
{
public:
Class(int a, int b) :a(a), b(b){}
void show()
{
cout<<"a="<< a << " b=" << b << endl;
}
//成员函数实现“+”运算符重载
Class operator+(const Class &p){
Class q(0, 0);
q.a = this->a + p.a;
q.b = this->b + p.b;
return q;
}
//private:
int a;
int b;
};
////全局函数实现“+”运算符重载
//Class operator+(const Class &p1, const Class &p2){
// Class q(0,0);
// q.a = p1.a + p2.a;
// q.b = p1.b + p2.b;
// return q;
//}
int main()
{
Class a(10,10);
Class b(20, 30);
Class c(0, 0);
c= a + b;
c.show();
system("pause");
return 0;
}
作用:可以输出自定义数据类型
//只能用全局函数实现“<<”运算符重载
ostream & operator<<(ostream &cout, const Class &p) //本质operator<<(cout,p),简化cout<{
cout << p.a << " " << p.b << endl;
return cout; //此处返回cout的为了能够实现链式编程,可以多次返回输出
}
#include
using namespace std;
#include
class Class
{
public:
Class(int a, int b) :a(a), b(b){}
int a;
int b;
};
//如果利用成员函数重载 左移运算符 p.operator<<(cout) 简化版本 p<
作用:通过重载递增运算符,实现自己的整型数据
//重载前置++运算符
Class &operator++() //返回引用是为了一直对一个数据进行增值操作
{
a++;
return *this;
}//重载后置++运算符 //此时不能再使用链式操作了
Class operator++(int) //此处int作为占位符,用来区分前置还是后置
{
Class p = *this; //记录当前本身的值,然后让本身的值加1,但是返回的值还是以前的值
a++;
return p;
}前置返回的是一个引用,而后置返回的是结果是一个临时对象。因为后置的时候原来的对象已经被++改变了,所以需要一个新对象来保存改变之前的值。而前置用引用则是为了不产生临时变量,减少内存的消耗而已。
所以前置引用可以使用链式的方法,多次前置++,而后置不可以多次++;
#include
using namespace std;
#include
class Class
{
friend ostream & operator<<(ostream &cout, const Class &p);
public:
Class(int a) :a(a){}
//重载前置++运算符
Class &operator++() //返回引用是为了一直对一个数据进行增值操作
{
a++;
return *this;
}
//重载后置++运算符
Class operator++(int) //此处int作为占位符,用来区分前置还是后置
{
Class p = *this; //记录当前本身的值,然后让本身的值加1,但是返回的值还是以前的值
a++;
return p;
}
private:
int a;
};
ostream & operator<<(ostream &cout, const Class &p)
{
cout << p.a << endl;
return cout;
}
int main()
{
Class a(10);
cout << ++(++a)<
赋值运算符需要注意的地方就是当数据在堆区存储时,注意开辟新的空间以及及时释放空间
void operator = (Class &p)
{
if (a != NULL) //如果已经有值,需要先释放,再进行赋值
{
delete a;
a = NULL;
}
//深拷贝
a = new int(*p.a);
}
#include
using namespace std;
#include
class Class
{
friend ostream & operator<<(ostream &cout, const Class &p);
public:
Class(int a){
this->a = new int(a);
}
void operator = (Class &p)
{
if (a != NULL)
{
delete a;
a = NULL;
}
//深拷贝
a = new int(*p.a);
}
private:
int *a;
};
ostream & operator<<(ostream &cout, const Class &p)
{
cout << *p.a << endl;
return cout;
}
int main()
{
Class a(10);
Class b(20);
a = b;
cout << a << endl;
system("pause");
return 0;
}
作用:可以让两个自定义类型对象进行对比操作
bool operator == (Class &p) //相对比较简单,就简单传值,然后对比一下
{
if (this->a == a) {
return true;
}
else {
return false;
} }
#include
using namespace std;
class Class
{
public:
Class(int a){
this->a = a;
}
bool operator == (Class &p)
{
if (this->a == a)
{
return true;
}
else
{
return false;
}
}
private:
int a;
};
int main()
{
Class a(10);
Class b(20);
if (a == b)
{
cout << "两个数相等" << endl;
}
else
{
cout << "两个数不相等" << endl;
}
system("pause");
return 0;
}
void operator()(string world)
{
cout << world << endl;
}
#include
using namespace std;
#include
class Class
{
public:
void operator()(string world)
{
cout << world << endl;
}
};
int main()
{
Class a;
a("hells world"); //由于使用起来类似于函数调用,因此称为仿函数;非常灵活,没有固定写法
//此处为匿名函数对象
Class()("hello world"); //Class()代替了类似于a
system("pause");
return 0;
}
继承的好处:减少重复代码
继承语法:class A(子类) : 继承方式 B(父类)
子类也称为派生类;父类也称为基类
派生类中的成员,包含两大部分:
一共有三种:
下面这段代码,详细注释了各种情况下的访问和继承
#include
using namespace std;
#include
class Class
{
public:
int a;
protected:
int b;
private:
int c;
};
class sun1:public Class
{
void fun()
{
a = 10; //公有 不变
b = 10; //保护 不变
//c = 10; 错误,子类不可访问父类的私有成员
}
};
class sun2 :protected Class
{
void fun()
{
a = 10; //保护 变为
b = 10; //保护 不变
//c = 10; 错误,子类不可访问父类的私有成员
}
};
class sun3 :private Class
{
void fun()
{
a = 10; //私有 变为
b = 10; //私有 变为
//c = 10; 错误,子类不可访问父类的私有成员
}
};
void test()
{
sun1 p1;
p1.a = 10;
//p1.b = 20; //保护b不可访问
//p1.c = 30; //私有c不可访问
sun2 p2;
//p2.a = 10; //保护a不可访问
//p2.b = 20; //保护b不可访问
//p2.c = 30; //私有c不可访问
sun3 p3;
//p3.a = 10; //私有a不可访问
//p3.b = 20; //私有b不可访问
//p3.c = 30; //私有c不可访问
}
int main()
{
test();
system("pause");
return 0;
}
继承规则:
class Class{
public: int a;
protected: int b;
private: int c;
};
class sun1 :public Class{
int a;
}; //此时sun1定义出的对象大小就为16,继承的三个加上新增加的一个
先构造父类,再构造子类,析构的顺序与构造的顺序相反(可以自己写代码实验)
sun p;
cout << p.a << endl;
cout << p.Base::a << endl;
p.change(); //子类中的成员函数
//p.change(a); //此处虽然是重载函数,但是只要子类父类发生重名,子类就会隐藏父类所有同名函数
p.Base::change(9); //父类中的带参成员函数
p.Base::change(); //父类中的不带参成员函数
#include
using namespace std;
#include
class Base{
public:
Base()
{
a = 10;
}
void change()
{
a = 100;
}
void change(int x)
{
a = x;
}
int a;
};
class sun:public Base{
public:
sun()
{
a = 20;
}
void change()
{
a = 200;
}
int a;
};
void test()
{
sun p;
//成员
cout << p.a << endl;
cout << p.Base::a << endl;
//成员函数
p.change(); //子类中的成员函数
//p.change(a); //此处虽然是重载函数,但是只要子类父类发生重名,子类就会隐藏父类所有同名函数
p.Base::change(9); //父类中的带参成员函数
p.Base::change(); //父类中的不带参成员函数
}
int main()
{
test();
system("pause");
return 0;
}
静态成员和非静态成员出现同名,处理方式一致,只不过有两种访问方式
cout << p.a << endl;
cout << p.Base::a << endl;cout << sun::a << endl;
cout << sun::Base::a << endl;
C++中允许一个类继承多个类
语法: class 子类 :继承方式 父类1,继承方式 父类2........
(实际开发中不建议)
class Base :public Base1, public Base2{}
当父类中出现同名成员,需要加作用域进行区分
cout << p.a << endl;
cout << p.Base1::a << endl;
cout << p.Base2::a << endl;
#include
using namespace std;
#include
class Base1{
public:
Base1()
{
a = 10;
}
int a;
};
class Base2{
public:
Base2()
{
a = 20;
}
int a;
};
class Base :public Base1, public Base2
{
public:
Base()
{
a = 30;
}
int a;
};
void test()
{
Base p;
cout << p.a << endl;
cout << p.Base1::a << endl;
cout << p.Base2::a << endl;
}
int main()
{
test();
system("pause");
return 0;
}
概念:
如图所示:B1,B2继承A,C又继承B1,B2。
当出现这种零星继承时,如果两个父类拥有相同数据,需要加以作用域区分
这份数据我们知道,只需要一份就足够了,而菱形继承导致数据有两份,资源浪费。
解决方法:
class B1 :virtual public A{};
class B2 :virtual public A{};
多态分为两类:
静态多态和动态多态的区别:
地址早绑定的话,在编译阶段就确定了函数地址,因此函数就不会更改了,一直显示某一个
若要按要求执行,使得函数不提前绑定,就需要用到动态多态,即加上关键字"virtual",变成虚函数
动态多态满足的条件:
动态多态的使用:
#include
using namespace std;
#include
class Base1{
public:
//虚函数
virtual void show()
{
cout << "Base1" << endl;
}
};
class Base2:public Base1
{
public:
void show()
{
cout << "Base2" << endl;
}
};
class Base3 :public Base1
{
public:
void show()
{
cout << "Base3" << endl;
}
};
void test(Base1 &p)
{
p.show();
}
int main()
{
Base1 p1;
Base2 p2;
Base3 p3;
test(p1);
test(p2);
test(p3);
system("pause");
return 0;
}
因为在多态中,通常父类的虚函数实现毫无意义,通常都是调用子类重写的内容,因此可以将虚函数改为纯虚函数
语法:virtual 返回值类型 函数名(参数列表)=0
当类中有了纯虚函数,这个类也称为抽象类
抽象类特点:
class Base1{
public:
//纯虚函数,所以此类也被称为抽象类
virtual void show() = 0;
};则不能Base1 p; 因为Base1是抽象类,抽象类不能实例化对象。
#include
using namespace std;
#include
class Base1{
public:
//纯虚函数,所以此类也被称为抽象类
virtual void show() = 0;
};
class Base2:public Base1
{
public:
//重写父类Base1中的show函数
void show(){ cout << "Base2"; }
};
class Base3 :public Base1
{
};
void test()
{
//Base1 p; //抽象类不能实例化对象
//Base3 p; //如果不重写抽象类中的纯虚函数,则子类也变成抽象类
Base2 p;
p.show();
}
int main()
{
test();
system("pause");
return 0;
}
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方法:将父类中的析构函数改为虚析类或者纯虚析构
虚析类和纯虚析构共性:
虚析类和纯虚析构区别:
虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名()=0
//利用虚析构可以解决父类释放子类对象时堆区数据泄露的问题
virtual ~Base1()
{
cout << "Base1的析构函数" << endl;
}//纯虚析构 需要声明也需要实现
//有了纯虚析构,这个类也属于抽象类,无法实例化对象
virtual ~Base1() = 0;
#include
using namespace std;
#include
class Base1{
public:
Base1()
{
cout << "Base1的构造函数" << endl;
}
////利用虚析构可以解决父类释放子类对象时堆区数据泄露的问题
//virtual ~Base1()
//{
// cout << "Base1的析构函数" << endl;
//}
//纯虚析构 需要声明也需要实现
//有了纯虚析构,这个类也属于抽象类,无法实例化对象
virtual ~Base1() = 0;
};
Base1::~Base1()
{
cout << "Base1纯虚析构函数" << endl;
}
class Base2:public Base1
{
public:
Base2()
{
cout << "Base2的构造函数" << endl;
}
~Base2()
{
cout << "Base2的析构函数" << endl;
}
};
void test()
{
Base1 *p = new Base2;
//父类指针在析构时候,不会调用子类中析构函数,导致子类如果有堆区数据,会出现内存泄露
delete p;
}
int main()
{
test();
system("pause");
return 0;
}
程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放
通过文件可以将数据持久化,需要包含的头文件
文件类型分为两种:
操作文件的三大类
步骤:
- 包含头文件:#include
- 创建流对象:ofstream test;
- 打开文件:test.open("文件路径",打开方式);
- 写数据:test<<"写入的数据";
- 关闭文件:test.close();
//头文件
ofstream test;
test.open("test.txt", ios::out);
test << "姓名:小明" << endl;
test.close();
文件打开方式
打开方式 | 解释 |
ios::in | 为读文件而打开文件 |
ios::out | 为写文件而打开文件 |
ios::ate | 初始位置:文件尾 |
ios::app | 追加方式写文件 |
ios::trunc | 如果文件存在先删除,再创建 |
ios::binary | 二进制方式 |
文件打开方式可以配合使用,利用” | ”操作符。
读文件与写文件步骤相似,但是读取方式相对较多
读取文件步骤:
- 包含头文件:#include
- 创建流对象:ifstream test;
- 打开文件:test.open("文件路径",打开方式);
- 读数据:四种方式读取
- 关闭文件:test.close();
#include
using namespace std;
#include
#include
int main()
{
ifstream test;
test.open("test.txt", ios::in);
////第一种
//char buf_1[1024] = { 0 };
//while (test >> buf_1)
//{
// cout << buf_1 << endl;
//}
////第二种
//char buf_2[1024] = { 0 };
//while (test.getline(buf_2,sizeof(buf_2)))
//{
// cout << buf_2 << endl;
//}
////第三种
//string buf_3;
//while (getline(test,buf_3))
//{
// cout << buf_3 << endl;
//}
////第四种
//char buf_4;
//while ((buf_4 = test.get()) != EOF)
//{
// cout << buf_4;
//}
test.close();
system("pause");
return 0;
}
以二进制的方式对文件进行读写操作
打开方式指定为 ios::binary
二进制方式写文件主要是利用流对象调用成员函数write
函数原型:ostream& write(const char *buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间,len是读写的字节数。
二进制方式写文件主要是利用流对象调用成员函数read
函数原型:istream& read(const char *buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间,len是读写的字节数。
关于文件操作链接:https://blog.csdn.net/qq_46423166/article/details/106963861