C++基础知识重点

const

#include <iostream>
using namespace std;

const修饰指针--常量指针,const int *p;p=&a;*p=1/错误,指针指向固定的值;
const修饰常量--指针常量,int * const p;p=&a/错误;*p=1,指针指向固定的地址;
const既修饰指针也修饰常量,const * int *p;

内存分区模型:

  • 代码区:放二进制代码
  • 全局区:存放全局变量和静态变量(static)以及常量等;
  • 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等;
  • 堆区:由程序员分配和释放,若程序员不释放,操作系统回收。

利用new在堆区开辟数据

int * p=new int(10)创建一个数据10new返回该数据类型的指针
int * arr=new int[10]创建10个数据

引用

int a=10;int c=10;
int &b=a;//给变量起别名,a,b的数据类型一致,a,b操作同一块内存

注意:1.引用必须要初始化{int &b;错误}{int &b=a;正确};2.引用一旦初始化,不能改为其他变量的别名,{b=c;错误}

引用做函数参数

//交换函数
void Swap01(int a,int b){//值传递,不改变主函数实参
int temp=b;
b=a;
a=temp;
}
void Swap02(int* a,int* b){//地址传递,改变主函数实参
int temp=*b;
*b=*a;
*a=temp;
}
void Swap03(int &a,int &b){//引用传递,改变主函数实参
int temp=b;
b=a;
a=temp;
}
Swap01(a,b);
Swap02(&a,&b);
Swap03(a,b);

引用作为返回值类型

int& test01(){
		int a=10;
return a;}//a在栈区
int& b=test01();//正常输出10,编译器保留一次
int& b=test01();//数据被回收了,出错
int& test02(){
		static int a=10;//a放在全局区,程序结束后系统释放
return a;}//
int& c=test02();//输出c的值是10
test02()=1000;//如果函数的返回值是引用,这个函数可以作为左值,输出c的值是1000

引用的本质是指针常量int * const p=&a;

常量引用

修饰形参,防止误操作

int a=10;
int &p=a;正确
int &p=10;错误
Const int &p=10;正确//编译器自动优化,int temp=10;const int& p=temp;
const int &p=a;p=10;错误,加入const变为只读,不可修改
Void show(const int &a){
a=100;//错误,常量引用不能被修改
}

函数的默认参数

int fun(int a=10,int b=20);//声明有默认参数时
int fun(int a,int b){//实现不要默认参数
return a+b;
}

声明和实现只能有一个有默认参数

函数占位参数

void fun(int a,int){//只写数据类型
cout<<a<<endl;
}
fun(10,10);//必须传2个值进去
void fun(int a,int=10){//占位参数可以默认
cout<<a<<endl;
}
fun(10);//可以只传1个值进去

函数重载

函数名称相同,参数类型不同或个数不同或顺序不同,提高复用性.必须在同一个作用域下

void func()
void func(int a)
void func(int a, double b) 
int func(int a, double b) //错误,无法发生重载
void func(int &a);
void func(const int &a);
func(10)//进入void func(const int &a);而不是void func(int &a);const int &a=10合法,int &a=10;不合法
void func(int a,int b=10);
void func(int a);
func(10);//函数重载碰到默认参数,以上两个函数都可以进入,错误,因此不要加默认值

class student{
public:
string s_name;
int number;
void showStudent(){
	Cout<<”姓名:<<name<<”学号:<<number<<endl;
}
void setName(string name){
	s_name=name;
}
void setId(int id){
	number=id;}
}

访问权限

  • public公共权限 类内外都可以访问
  • protected保护权限 类内可以访问类外不可以访问 子类可以访问父类的保护内容
  • private私有权限 类内可以访问类外不可以访问 子类不可以访问父类的保护内容
class person{
public:
	string name;
protected:
	string car;
private:
	int password;
}
int main(){
	person P1;
	P1.name=”张三”;
	P1.car=”奔驰”;//错误,类外访问不到
	P1.password=123456;//错误,类外访问不到
}

class和struct区别

class C1{
	int m_A;//默认权限:私有
}
struct C2{
	int m_A;//默认权限:公共
}
int main(){
	C1 c1;
	c1.m_A=100;//报错,类外不可以访问
	C2 c2;
	c2.m_A=100;//成功
}

成员属性设置为私有

class Person
{
private:
	string m_name;//要求可读可写
	int age;//要求只读
public:
	void setName(string name){//写
		m_name=name;
}
	void getName(){//读
		return m_name;
}
	void getAge(){//读
		return m_age;
}
}

构造函数和析构函数

构造函数用于初始化对象,析构函数用于清理对象

class Person
{
	Person()
	{
		cout<<"构造函数"<<endl;
	}
	~Person()
	{
		cout<<"析构函数"<<endl;
    }
}
void test(){
	Person p;
}
int main(){
	test();//几乎同时调用构造函数和析构函数
	Person p;//调用构造函数,函数main结束时调用析构函数
}

构造函数的分类和调用

两种构造方式:

  • 按参数分类:有参构造,无参构造
  • 按类型分类:普通构造和拷贝构造

三种调用方式:

  • 括号法
  • 显示法
  • 隐式转换法
class Person{
public:
	int age;
	Person(int a){
		age=a;
		cout<<"有参构造函数"<<endl;
}
	
	Person(const Person &p){//拷贝构造
		age=p.age;
}	
}
void test(){
//括号法
	Person p1;//默认构造函数调用,不加()
	Person p2(10);//有参构造函数调用
	Person p3(p2);//拷贝构造函数调用
//显示法
	Person p1;
	Person p2=Person(10);//有参构造
	Person p3=Person(p2);//拷贝构造
	Person(10);//匿名对象,系统运行到这会立即收回匿名对象
//隐式转换法
	Person p4=10;//相当于Person p4=Person(10);有参构造
	Person p5=p4;//相当于Person p5=Person(p4);拷贝构造
}

拷贝构造函数调用时机

1.使用一个已经创建完毕的对象来初始化一个新对象

void test(){
Person p1;
Person p2(p1);
}

2.值传递的方式给函数参数传值

void doWork(Person p){}
void test(){
Person p;
doWork(p);
}

3.值方式返回局部对象

Person doWork2(){
	Person p;
	return p;//输出p
}
void test(){
	Person p=doWork2();//接收到的p地址与上面输出p的地址不同,深拷贝
}

构造函数调用原则

默认情况下,C++编译器至少给一个类添加3个函数
1.默认构造函数(默认函数体为空)
2.默认析构函数(默认函数体为空)
3.默认拷贝构造函数,对所有属性进行值拷贝

class Person{
public:
	int age;
	Person(){
		cout<<"默认构造函数"<<endl;
}
	~Person(){
		cout<<"默认析构函数"<<endl;
}
	Person(int a){//提供有参构造函数,编译器不再提供默认无参构造,依然提供拷贝构造
		age=a;
		cout<<"有参构造函数"<<endl;
}
	
	Person(const Person &p){//拷贝构造//提供拷贝构造函数,编译器不再提供其他构造函数
		age=p.age;
		cout<<"拷贝构造函数"<<endl;
}	
}

深拷贝与浅拷贝

  • 浅拷贝:简单赋值操作
  • 深拷贝:在堆区重新申请空间进行拷贝操作
class Person{
public:
   int age;
   int *m_Height;
   Person(){
   	cout<<"默认构造函数"<<endl;
}
   ~Person(){
   	if(m_Height!=NULL){
   			delete m_Height;
   			m_Height=NULL;
}
   	cout<<"默认析构函数"<<endl;
}
   Person(int a,int Height){//提供有参构造函数,编译器不再提供默认无参构造,依然提供拷贝构造
   	age=a;
   	m_Height=new int(Height);
   	cout<<"有参构造函数"<<endl;
}
   
   Person(const Person &p){//拷贝构造//提供拷贝构造函数,编译器不再提供其他构造函数
   	age=p.age;
   	//m_Height=p.m_Height;编译器默认,浅拷贝,报错
   	m_Height=new int(*p.m_Height);//深拷贝,自动返回地址
   	cout<<"拷贝构造函数"<<endl;
}	
}

初始化列表

class Person{
public:
	int m_A;
	int m_B;
	int m_C;
	Person(int a,int b,int c){
		m_A=a;
		m_B=b;
		m_C=c;
		cout<<"传统初始化操作"<<endl;
}
	Person(int a,int b,int c):m_A(a),m_B(b),m_C(c){
		cout<<"初始化列表初始化属性"<<endl;
}

}

类对象作为类成员

class A();
class B{
	A a;
}

静态成员

静态成员变量

  • 所有对象共享同一份数据
  • 在编译阶段分配内存
  • 类内声明,类外初始化
class Person{
public:
	static int m_A;//类内声明
private:
	static int m_B;//类外无法访问
}
static int Person::m_A=10;//类外初始化
void test(){
	Person p1;
	p1.m_A=100;//通过对象访问
	Person::m_A=200;//通过类名访问
}

静态成员函数

  • 所有对象共享同一个函数
  • 静态成员函数只能访问静态成员变量
class Person{
public:
	static int m_A;//类内声明
	int m_B;
	static void func()
	{
		m_A=100;//静态成员函数可以访问
		m_B=100;//非静态成员函数不可以访问,报错
	}
private:
	static void func2()
	{
	}
}
static int Person::m_A=10;//类外初始化
Person p;
p.func();//通过对象访问
p.func2();//类外访问不到私有函数
Person::func();//通过类名访问

C++对象模型

成员函数和成员变量分开存储

class Person{}
void test(){
	Person p;
	cout<<"size of class: "<<sizeof(p)<<endl;//大小为1,编译器给每个空对象分配一个字节空间,用于区别空对象占内存的位置
}
class Person2{
	int m_A;
	static int m_B;
	void func(){}
	static void func2(){}//静态不属于类对象,非静态属于类对象,分开存储
}

指针

this指针

同一份代码会被多个同类型的对象调用,利用this指针区分不同对象,this指针指向被调用的成员函数所属的对象。

class Person
{
public:
	int age;
	Person(int age){
		this->age=age;}
	Person& addP(Person &p){//用引用的方式,不加&是值的方式,返回一个新的对象,引用返回的还是原p
		this->age+=age;//this指针指向被调用成员的对象
		return *this;//*this就是一个Person对象
}
}
void test(){
	Person p1(10);
	Person p2(20);
	p2.addP(p1).addP(p1).addP(p1).addP(p1);//链式编程
	cout<<"p2的年龄是:"<<p2.age<<endl;
}

空指针访问成员函数

class Person
{
public:
	int age;
	void showname(){
		cout<<"class name is Person"<<endl;;}
	void showage(){
		if(this==NULL){
			return;
}
		cout<<this->age<<endl;}
}
void test(){
	Person*p=NULL;//空指针,age没有值,无法访问age
	p->showname();//正确
	p->showage();//错误
}

const 修饰成员函数

常函数:
常函数内不可以修改成员属性;
想要修改,在属性声明时加关键字mutable;

常对象:
常对象只能调用常函数;

class Person
{
public:
//this指针的本质是指针常量,指针的指向不可修改,但是指向的值可以改
//Person * const this
	void showPerson() const//this 既是指针常量也是常量指针
	{
		this->A=100;//错误,
		this->B=100;//正确
	}
	void showPerson() 
	{
		this->A=100;//正确,
	}
	void func(){}
	int A;
	mutable int B;
}
void test(){
	const Person p;//常对象
	p.A=100;//错误,不可修改普通属性
	p.B=100;//正确
	p.func();//错误,常对象不可调用普通函数
	p.showPerson();//正确
}

友元

友元的三种实现:

  • 全局函数做友元
class Building{
	friend void goodFriend(Building* building);//全局函数做友元,goodFriend是Building的好朋友,可以访问它的私有成员
public:
	Building(){
	sittingroom="客厅";
	bedroom="卧室";
}
	string sittingroom;
private:
	string bedroom;
}
void goodFriend(Building* building){
	cout<<"好朋友全局函数正在访问:"<<
	building->sittingroom<<endl;
	cout<<"好朋友全局函数正在访问:"<<
	building->bedroom<<endl;//正确
}
int main(){
	Building building;
	goodFriend(&building);
}
  • 类做友元
class Building{
	friend class goodFriend;//goodFriend是Building的好朋友,可以访问它的私有成员
public:
	Building(){}
	string sittingroom;
private:
	string bedroom;
}
//类外也能实现成员函数
Building::Building(){
	sittingroom="客厅";
	bedroom="卧室";
}
class goodFriend(){
public:
	void visit();
	Building * building;
}
goodFriend::goodFriend(){
	building=new Building;//创建对象
}
goodFriend::visit(){
	cout<<"好朋友正在访问:"<<building->sittingroom<<endl;
	cout<<"好朋友正在访问:"<<building->bedroom<<endl;
}
int main(){
	goodFriend gf;
	gf.visit();
}
  • 成员函数做友元
class Building{
	friend void goodFriend::visit();//成员函数做友元,goodFriend是Building的好朋友,可以访问它的私有成员
public:
	Building(){
	sittingroom="客厅";
	bedroom="卧室";
}
	string sittingroom;
private:
	string bedroom;
}
class goodFriend(){
public:
    goodFriend(){
	building=new Building;
}
	void visit(){
	cout<<"visit函数正在访问"<<building.sittingroom<<endl;
	cout<<"visit函数正在访问"<<building.bedroom<<endl;
}//让visit可以访问building中的私有成员

	void visit2(){
	cout<<"visit2函数正在访问"<<building.sittingroom<<endl;}//让visit2不可以访问building中的私有成员

	Building * building;
}
int main(){
	goodFriend gf;
	gf.visit();
	gf.visit2();
}

运算符重载

加法运算符重载

class Person
{
public:
	Person& operator+(Person&p){//成员函数重载
		Person temp;
		temp.A=this->A+p.A;
		temp.B=this->B+p.B;
		return temp
}
	int A;
	int B;
}
//全局函数重载,功能同成员函数重载一样
Person& operator+(Person&p1,Person&p2){
		Person temp;
		temp.A=p1->A+p2.A;
		temp.B=p1->B+p2.B;
		return temp
}
Person& operator+(Person&p1,int num){//Person+int
		Person temp;
		temp.A=p1->A+num;
		temp.B=p1->B+num;
		return temp
}
void test(){
	Person p1;
	p1.A=10;
	p1.B=10;
	Person p2;
	p2.A=10;
	p2.B=10;
	Person p3=p1+p2;//等价于p3=p1.operator+(p2);
	Person p4=p1+100;
}

左移运算符重载

class Person
{
friend ostream& operator<<(ostream& cout,Person &p);
private://私有
	int A;
	int B;
}
public:
	Person(int a, int b){
		A=a;
		B=b;
}
//只能利用全局函数重载
ostream& operator<<(ostream& cout,Person &p){
		cout<<"p.A="<<p.A<<", p.B="<<p.B<<endl;
		return cout;//cout只是别名,可以修改
}
void test(){
	Person p(10,10);
	cout<<p<<endl;
}

递增运算符重载

class MyInteger{
friend ostream& operator<<(ostream& cout,MyInteger& myint);
public:
	MyInteger(){
		num=0;
}
//前置递增
MyInteger& operator++(){
	num++;
	return *this;
}
//后置递增
MyInteger& operator++(int){//int 代表占位参数,可以用于区分前置递增和后置递增
	MyInteger temp=*this;
	num++;
	return temp;
}
private:
	int num;
}
ostream& operator<<(ostream& cout,MyInteger& myint){
	cout<<myint<<endl;
	return cout;
}
void test(){
  MyInteger myint;
  cout<<myint++<<endl;
  cout<<++myint<<endl;
}

赋值运算符重载

C++编译器至少给一个类添加4个函数
1.默认构造函数,函数体为空
2.默认析构函数,函数体为空
3.默认拷贝函数,对属性进行值拷贝
4.赋值运算符operater=,对属性进行值拷贝

class Person
{
public:
	int * Age;
	Person(int age){
		Age=new int(age);	
}
	~Person(){
		if(Age!=NULL){
			delete Age;
			Age=NULL;
}
	Person& operator=(Person&p){//深拷贝
		//先判断是否有属性在堆区,如果有先释放干净
		if(Age!=NULL){
			delete Age;
			Age=NULL;
}
		this->Age=new int(*p.Age);
		return *this;
}
}
}
void test01(){
	Person p1(18);
	Person p2(28);
	Person p3(38);
	p2=p1;//浅拷贝,p1,p2的Age都指向同一块内存,两者都释放一次,第二次释放时会报错,堆区内存重复释放
	cout<<"p2的年龄是:"<<*p2.Age<<endl;
	p3=p2=p1;
}

关系运算符重载

class Person
{
public:
	string Name;
	int Age;
	Person(int age,string name){
		Age=age;
		Name=name;}
	bool operator==(Person&p){
		if(p.Name==this->Name && p.Age==this->Age){
			return true;
}
		return false;
}
	bool operator!=(Person&p){
		if(p.Name==this->Name && p.Age==this->Age){
			return false;
}
		return true;
}
}

void test(){
	Person p1=(18,"Tom");
	Person p2=(18,"Tom");
	Person p3=(28,"Tom");
	if(p1==p2){
		cout<<"p1和p2相同"<<endl;
}
	if(p1!=p3){
		cout<<"p1和p2不同"<<endl;
}
}

函数调用运算符()重载

class MyPrint{
public:
	void operator()(string test){
		cout<<test<<endl;
}
	
}
class MyAdd{
public:
	int operator()(int num1,int num2){
		return num1+num2;
}
}

void test(){
	MyPrint myprint;
	myprint("Hello World");//由于重载后使用的方式非常像函数的调用,因此称为仿函数。
	MyAdd myadd;
	cout<<myadd(2,3)<<endl;
	cout<<MyAdd()(2,3)<<endl;//匿名函数对象
}

继承

class BasePage{
public:
	void header(){
		cout<<"首页、公开课、登录..."<<endl;}
}
class Java:public BasePage{//class 子类:继承方式 父类,子类也叫派生类,父类也叫基类
	void conter(){
		cout<<"Java学科视频"<<endl;}
}
class Python:public BasePage{
	void conter(){
		cout<<"python学科视频"<<endl;}
}

继承方式

公共继承,保护继承,私有继承
继承方式
A类是父类,私有的属性c在子类中无法访问,保护继承下父类共有属性和保护属性在子类中都是保护属性,私有继承下父类共有属性和保护属性在子类中都是私有属性。

利用开发人员命令提示工具查看对象模型.。

继承中的构造和析构顺序

父类构造---->子类构造---->子类析构---->父类析构

继承同名成员处理方式

  • 子类对象可以直接访问子类中的同名成员
  • 子类对象加作用域可以访问到父类同名成员
  • 当子类和父类拥有同名成员函数,子类会隐藏父类中的同名成员函数,加作用域可以访问父类中同名函数。
class Base{
public:
	int A;
	Base(){A=100;}
	void func(){cout<<"base-func"<<endl;}
	void func(int a){cout<<"base-func a"<<endl;}
}
class Son:public Base{
	int A;
	Base(){A=200;}
	void func(){cout<<"son-func"<<endl;}
}
void test(){
	Son s;
	cout<<s.A<<endl;//输出200
	cout<<s.Base::A<<endl;//输出100
	s.func()//输出son-func
	s.Base::func()//输出base-func
	s.func(10);//报错,无法访问,父类同名函数被子类的同名函数隐藏,无论有无参
	s.Base::func(10);//输出base-func a
}

同名静态成员处理方式

同非静态处理方式一样

  • 访问子类同名成员,直接访问即可
  • 访问父类同名成员,需要加作用域
class Base{
public:
	static int A;
	Base(){A=100;}
	static void func(){cout<<"base-func"<<endl;}
	static void func(int a){cout<<"base-func a"<<endl;}
}
int Base::A=100;
class Son:public Base{
	static int A;
	Base(){A=200;}
	static void func(){cout<<"son-func"<<endl;}
}
int Son::A=100;
void test(){
	Son s;
	//通过对象访问
	cout<<s.A<<endl;
	cout<<s.Base::A<<endl;
	s.func()//输出son-func
	s.Base::func()//输出base-func
	s.Base::func(10);//输出base-func a
	//通过类名访问
	cout<<"son下A="<<Son::A<<endl;
	cout<<"base下A="<<Son::Base::A<<endl;
	Son::func()//输出son-func
	Son::Base::func()//输出base-func
	Son::Base::func(10);//输出base-func a
}

多继承语法

C++允许一个类继承多个类,但不推荐
多继承可能会引发父类中由同名成员出现,需要加作用域

class Base1{
public:
	int A;
	Base1(){A=10;}
	
}
class Base2{
public:
	int A;
	Base1(){A=20;}
}
class Son:public Base1,public Base2{
}
void test(){
	Son s;
	cout<<"Base1:"<<s.Base1::A<<endl;
	cout<<"Base2:"<<s.Base2::A<<endl;
}

菱形继承

概念:两个派生类继承同一个基类,又有某个类继承同时继承这两个派生类。
问题:容易引发二义性,子类继承两份相同的数据,使用虚继承解决。
菱形继承

class Animal{
public:
	int Age;};
class Sheep:virtual public Animal{};//加上关键字virtual,虚继承
class Camel:virtual public Animal{};//Animal是虚基类
class Sheepcamel:public Sheep,public Camel{};
void test(){
	Sheepcamel sc;
	sc.Sheep::Age=18;
	sc.Camel::Age=28;//覆盖了
	cout<<"羊的年龄:"<<sc.Sheep::Age<<endl;
	cout<<"驼的年龄:"<<sc.Camel::Age<<endl;//需要加作用域区分
	//这份数据只需要有一份就可以,菱形继承数据有两份,造成资源浪费,使用virtual进行虚继承,都输出28
	cout<<"驼的年龄:"<<sc.Age<<endl;//输出28
}
//vbptr虚v基类b指针ptr,指向vbtable虚基类表

多态

好处:

  • 对修改封闭,对扩展开放,维护性高
  • 组织结构清晰
  • 可读性强

两种多态:

  • 静态多态:包含函数重载和运算符重载等,函数地址早绑定,编译阶段确定函数地址
  • 动态多态:派生类和虚函数实现运行时多态,函数地址晚绑定,运行阶段确定函数地址(这里讲动态多态)
class Animal{
public:
	void Speak1(){cout<<"动物在说话"<<endl;}
	virtual void Speak2(){cout<<"动物在说话"<<endl;}//加virtual虚函数,实现地址晚绑定
};
class Cat:public Animal{
public:
	void Speak1(){cout<<"小猫在说话"<<endl;}
	void Speak2(){cout<<"小猫在说话"<<endl;}
};
void doSpeak1(Animal& animal){
	animal.speak1();
}
void doSpeak2(Animal& animal){//父类引用
	animal.speak2();
}
void test(){
	Cat cat;
	doSpeak1(cat);//输出动物在说话,地址早绑定,在编译阶段确定函数地址
	doSpeak2(cat);//输出小猫在说话,地址晚绑定,在运行阶段确定函数地址
}

动态多态满足条件:

  • 有继承关系
  • 子类重写父类虚函数

动态多态使用:

  • 父类的指针或者引用,执行子类对象

多态的底层原理

多态的底层原理

纯虚函数和抽象类

父类中的虚函数的实现是毫无意义的,主要是调用子类重写的内容,因此可以将虚函数改为纯虚函数。
当类中有纯虚函数,这个类也称为抽象类。

抽象类的特点:无法实例化对象;子类必须重写抽象类中的纯虚函数,否则也属于抽象类。

class Base{
public:
	virtual void func()=0;//纯虚函数,无法实例化对象
}
class Son:public Base{
public:
	void func(){cout<<"子类"<<endl;}
}
void test(){
	Base b;//报错
	Son s;//可以实例化
	Base* b=new Son;//开辟在堆区
	b->func();
	delete b;//手动删除
}

虚析构和纯虚析构

问题:如果子类中有属性开辟到堆区,父类指针在释放时无法调用到子类的析构代码。
解决办法:将父类中的析构函数改为虚析构或者纯虚析构(也是抽象类)

class Animal{
public:
	virtual void Speak()=0;
	Animal(){cout<<"animal构造"<<endl;}
	virtual ~Animal(){cout<<"animal析构"<<endl;}//利用虚析构解决父类指针释放不干净的问题。
	//或者纯虚析构,既需要声明也需要实现,抽象类
	virtual ~Animal()=0;
};
Animal::~Animal(){cout<<"animal纯虚析构"<<endl;}
class Cat:public Animal{
public:
	
	string *Name;
	Cat(string name){
		cout<<"cat 构造"<<endl;
		Name=new string(name);//堆区
		}
	~Cat(){
	if(Name!=NULL){
		cout<<"cat 析构"<<endl;
		delete Name;
		Name=NULL;}
}
	void Speak(){cout<<*Name<<"小猫在说话"<<endl;}
};
void test(){
	Animal* animal=new Cat("Tom");
	animal->Speak();
	delete animal;//父类指针在析构时不会调用子类中的析构函数,导致子类如果有堆区数据会导致内存泄漏,添加virtual到 ~Animal()
}

文件操作

需要添加头文件#include
文件类型分类:

  • 文本文件,以ASCII码的形式存储在计算机中
  • 二进制文件,以文本二进制的形式存储在计算机中

操作:

  • 写:ofstream(o代表output)
  • 读:ifstream,(i代表input)
  • 读写:fstream

写文本文件

ofstream ofs;
ofs.open(“文件路径”,文件打开方式);
ofs<<“写入数据”<<endl;
ofs.close();
文件打开方式:打开方式

void test(){
	ofstream ofs;
	ofs.open("test.txt",ios::out);//打开文件
	ofs<<"姓名:张三"<<endl;//写入数据
}

读文本文件

ifstream ifs;
ifs.open(“文件路径”,文件打开方式);
四种方式读取文件
ifs.close();

void test(){
	ifstream ifs;
	ifs.open("test.txt",ios::in);//打开文件
	if(!ifs.is_open()){
		cout<<"文件打开失败"<<endl;
		return;}
	//读数据,四种
	//第一种,一行一行的读
	char buf[1024]={0};//字符数组
	while(ifs>>buf){
		cout<<buf<<endl;}
	//第二种
	char buf[1024]={0};
	while(ifs.getline(buf,sizeof(buf))){
		cout<<buf<<endl;
	}
	//第三种
	string buf;
	while(getline(ifs,buf)){
		cout<<buf<<endl;
	}
	//第四种,一个字节一个字节地读,效率低,不推荐
	char c;
	while((c=ifs.get())!=EOF){//end of file文件尾
		cout<<c;
		}
	ifs.close();
}

写二进制文件

以二进制方式对文件进行读写,打开方式指定ios::binary
函数原型:ostream& write(const char* buffer,int len)
字符指针buffer指向内存中一段存储空间,len是读写的字节数

#include <iostream>
class Person{
public:
	char Name[64];
	int Age;
}
void test(){
	ofstream ofs;
	ofs.open("person.txt",ios::out | ios::binary);
	//或两行合到一起ofstream ofs.open("person.txt",ios::out | ios::binary);
	Person p={"张三",18};
	ofs.write((const char *)&p,sizeof(Person));}
	ofs.close();

读二进制文件

函数原型:ostream& read(const char* buffer,int len)
字符指针buffer指向内存中一段存储空间,len是读写的字节数

#include <iostream>
class Person{
public:
	char Name[64];
	int Age;
}
void test(){
	ifstream ifs.open("person.txt",ios::in | ios::binary);
	if(!ifs.is_open()){
		cout<<"文件打开失败!"<<endl;
		return;
}
	Person p;
	ifs.read((char*)&p,sizeof(Person));
	cout<<"姓名:"<<p.Name<<" 年龄:"<<p.Age<<endl;
	ifs.close();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值