C++学习记录---1

根据B站学习:

1、new和delete

C++中利用new操作符在堆区开辟数据
堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符 delete
语法: new 数据类型
利用new创建的数据,会返回该数据对应的类型的指针

int* func1() 
{ 
	int* a = new int(10); 
	return a; 
}
void func2()
{
	int* arr = new int[10];
	for (int i = 0; i < 10; i++) 
	{ 
		arr[i] = i + 100; 
	}
	for (int i = 0; i < 10; i++) 
	{ 
		cout << arr[i] << endl; 
	}
	delete[] arr;
}

int main() 
{ 
	int* p = func1(); 
	cout << *p << endl; 
	delete p;
	func2();
	system("pause"); 
	return 0; 
}

2、引用

2.1、引用的基本使用

作用:给变量起一个别名
语法:数据类型 &别名 = 原名
注意事项:

  • 引用必须初始化
  • 引用在初始化后,不可以改变
#include <iostream>

using namespace std;
//作用: 给变量起别名
//语法: 数据类型& 别名 = 原名
//注意:
//引用必须初始化
//引用在初始化后,不可以改变

void quote(void)
{
	cout << "当前区域是“引用”部分-----start" << endl;
	int a = 100;
	int& b = a;
	b = 10000;
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
}

在这里插入图片描述

2.2、引用做函数参数

作用:函数传参时,可以利用引用的技术让形参修饰实参
优点:可以简化指针修改实参
小结:通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单

2.2.1、引用也可以作为函数返回值

作用:引用是可以作为函数的返回值存在的
注意:不要返回局部变量引用
用法:函数调用作为左值

2.2.2、引用的本质

本质:引用的本质在c++内部实现是一个指针常量.
结论:C++推荐用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了
栗子:

#include <iostream>
using namespace std;
/*值传递*/
void swap01(int a, int b)
{
	int temp;
	temp = a;
	a = b;
	b = temp;
}
/*地址传递*/
void swap02(int *a, int *b)
{
	int temp;
	temp = *a;
	*a = *b;
	*b = temp;
}
//引用传递
void swap03(int &a, int &b)
{
	int temp;
	temp = a;
	a = b;
	b = temp;
}
/*通过引用参数产生的效果同按地址传递是一样的、引用的语法更清楚简单*/
void quote_func(void)
{
	int a = 50;
	int b = 89;

	swap01(a, b);
	cout << "引用作为函数参数---值传递 " << "a = " << a << endl;
	cout << "引用作为函数参数---值传递 " << "b = " << b << endl;
	swap02(&a, &b);
	cout << "引用作为函数参数---地址传递 " << "a = " << a << endl;
	cout << "引用作为函数参数---地址传递 " << "b = " << b << endl;
	swap03(a, b);
	cout << "引用作为函数参数---引用传递 " << "a = " << a << endl;
	cout << "引用作为函数参数---引用传递 " << "b = " << b << endl;
}
//引用作为函数返回值
int& test01()
{
	int a = 100;//局部变量--注意不要返回局部变量
	return a;
}
int& test02()
{
	static int a = 45;
	return a;
}
void quote_return(void)
{
	int& a = test01();
	cout << "引用作为函数返回值---局部变量" << "a = " << a << endl;
	cout << "引用作为函数返回值---局部变量" << "a = " << a << endl;

	int &b = test02();
	cout << "引用作为函数返回值---静态变量" << "b = " << b << endl;
	cout << "引用作为函数返回值---静态变量" << "b = " << b << endl;

	test02() = 526;//实际上就是 b = 526
	cout << "引用作为函数返回值---静态变量" << "b = " << b << endl;
	cout << "引用作为函数返回值---静态变量" << "b = " << b << endl;
}
//C++推荐用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了
//int a = 10; 
//自动转换为 int* const ref = &a; 指针常量是指针指向不可改,也说明为什么引用不可更改
//int& ref = a;
//ref = 20; //内部发现ref是引用,自动帮我们转换为: *ref = 20;

在这里插入图片描述

2.3 常量引用

作用:常量引用主要用来修饰形参,防止误操作
在函数形参列表中,可以加const修饰形参,防止形参改变实参
栗子:

#include <iostream>
using namespace std;
void constpare(const int& ref)
{
	cout << "常量引用--函数参数引用参量" << "ref = " << ref << endl;
}
//常量引用
void quote_const(void)
{
	//int& a = 100;//错误的原因是没有进行添加const标识符
	const int& a = 100;

	//加入const之后就不能再次修改常量的值
	//a = 555;
	constpare(a);
}

在这里插入图片描述

3、函数提高

3.1、函数默认参数

在C++中,函数的形参列表中的形参是可以有默认值的。在使用的过程中,默认值也是可以根据使用函数的过程中传递的参数不同而不同
语法:返回值类型 函数名 (参数= 默认值){}

3.1.1、函数占位参数

C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置
语法:返回值类型 函数名 (数据类型){}

/*举例:创建这样的函数*/
void func9(int a, int);
//在引用的时候需要将函数的所有参数都带上:
比如这样func9(10,41);

栗子:

#include <iostream>

using namespace std;

/*
在C++中,函数的形参列表中的形参是可以有默认值的
语法: 返回值类型 函数名 (参数= 默认值){}
*/

//1. 如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值 
int func1(int a, int b = 2, int c = 56)
{
	return a + b + c;
}

//2. 如果函数声明有默认值,函数实现的时候就不能有默认参数
int func2(int a, int b, int c = 56, int d = 10);
int func2(int a, int b, int c, int d)
{
	return a + b + c + d;
}

//函数占位参数
//C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置
//语法: 返回值类型 函数名(数据类型) {}
void func3(int a, int) 
{ 
	cout << "函数占位功能栗子:" << "占位参数必须填补" << endl; 
}


void func_default_para(void)
{
	int a = 54;
	int b = 10;
	//func1和func2函数参数可以补齐、也可以只写变量部分

	//func1只写变量部分
	cout << "函数默认参数-----" << func1(a) << endl;
	//func2全部补全-默认参数部分也是可以进行更改的
	//声明是int func2(int a, int b, int c = 56, int d = 10)
	//默认参数C和D可以写成50和90
	cout << "函数默认参数-----" << func2(a,b,50,90) << endl;

	
	func3(10, 10); //占位参数必须填补
 }

在这里插入图片描述

3.2、函数重载

作用:函数名可以相同,提高复用性
函数重载满足条件:

  1. 同一个作用域下
  2. 函数名称相同
  3. 函数参数类型不同 或者 个数不同 或者 顺序不同

注意:函数的返回值不可以作为函数重载的条件
小结:函数名相同的情况下,参数的个数、类型、顺序可随意设置,如果有返回值,这些函数的返回值必须一样

3.2.1、引用作为重载时

栗子:

#include <iostream>
using namespace std;

//作用:函数名可以相同,提高复用性
//函数重载满足条件:
//1、同一个作用域下
//2、函数名称相同
//3、函数参数类型不同 或者 个数不同 或者 顺序不同
//注意 : 函数的返回值不可以作为函数重载的条件

//函数重载需要函数都在同一个作用域下 
void func() 
{ 
	cout << "函数重载---func 的调用!" << endl; 
}
void func(int a) 
{ 
	cout << "函数重载---func (int a) 的调用!" << endl; 
}
void func(double a) 
{ 
	cout << "函数重载---func (double a)的调用!" << endl; 
}
void func(int a ,double b)
{
	cout << "函数重载---func (int a ,double b) 的调用!" << endl;
}
void func(double a, int b) 
{ 
	cout << "函数重载---func (double a ,int b)的调用!" << endl; 
}

//函数重载时遇到----引用
void func1(int& a) 
{ 
	cout << "函数重载---引用---func1 (int &a) 调用 " << endl; 
}
void func1(const int& a)
{
	cout << "函数重载---引用---func1 (const int &a) 调用 " << endl;
}
//函数重载
void func_overload(void)
{

	func(); 
	func(10); 
	func(3.14); 
	func(10, 3.14); 
	func(3.14, 10);

	int a = 10;
	func1(a); //调用无const 
	func1(10);//调用有const
}

在这里插入图片描述
函数重载时遇到函数默认参数的情况下,应避免使用重载函数

4、类和对象

C++面向对象的三大特征为:封装、继承、多态
C++认为万事万物皆为对象,对象有其属性和行为
例如:
人可以作为对象,属性有姓名、年龄、身高、体重…,行为有走、跑、跳、吃饭、唱歌…
车也可以作为对象,属性有轮胎、方向盘、车灯…,行为有载人、放音乐、放空调…
具有相同性质的对象,我们可以抽象称为,人属于人类,车属于车类

4.1、封装

封装是C++面向对象三大特性之一
封装的意义:

  • 将属性和行为作为一个整体,表现生活中的事物
  • 将属性和行为加以权限控制

语法:class 类名{ 访问权限: 属性 / 行为 }

4.1.1、三种访问权限
  • public 公共权限
  • protected 保护权限
  • private 私有权限

解释:

  • 公共权限 public 类内可以访问 类外可以访问
  • 保护权限 protected 类内可以访问 类外不可以访问
  • 私有权限 private 类内可以访问 类外不可以访问
4.1.2、struct和class区别

在C++中,struct和class区别就在于默认的访问权限不同

  • struct 默认权限为公共
  • class 默认权限为私有
4.1.3、将成员属性设置为私有
  • 优点1:将所有成员属性设置为私有,可以自己控制读写权限
  • 优点2:对于写权限,我们可以检测数据的有效性

栗子:

#include <iostream>

using namespace std;

class student
{
public:
	void setname(string name)
	{
		m_name = name;
	}
	void setnum(int num)
	{
		m_num = num;
	}
	string getname()
	{
		return m_name;
	}
	int getnum()
	{
		return m_num;
	}
	void showmassage()
	{
		cout << "封装----姓名:" << m_name << endl;
		cout << "封装----学号:" << m_num << endl;
	}
public:
	string m_name;
	int m_num;
};

//三种保护权限
//1、public		公共权限 类内外都可访问
//2、private	私有权限 类内可以访问,类外不可访问
//3、protected	保护权限 类内可以访问,类外不可访问

//成员属性设置为私有
class person
{
public:
	//m_name---可读可写
	void setname(string name)
	{
		m_name = name;
	}
	string getname(void)
	{
		return m_name;
	}
	//年龄---只读
	int getage(void)
	{
		return m_age;
	}
	void setage(int age)
	{
		if (age < 0 || age > 150)
		{
			cout << "你个老怪物!" << endl;
			return;
		}
		m_age = age;
	}
	//情人设置为只写
	void setlover(string lover)
	{
		m_lover = lover;
	}
private:
	string m_name;
	int m_age;
	string m_lover;
};
//封装
void capsulation()
{
	student stu;
	stu.setname("王二毛");
	stu.setnum(4545);
	stu.showmassage();
	person per;
	per.setname("王大毛");
	cout << "封装--可读可写--姓名:" << per.getname() << endl;
	per.setage(12);
	cout << "封装--只读--年龄:" << per.getage() << endl;
	per.setlover("王大妈");
}

在这里插入图片描述

4.1.4、小项目

要求:
设计立方体类(Cube)
求出立方体的面积和体积
分别用全局函数和成员函数判断两个立方体是否相等。

#include <iostream>

using namespace std;

class CUBE
{
public:
	void set_l(int l)
	{
		m_l = l;
	}
	void set_w(int w)
	{
		m_w = w;
	}
	void set_h(int h)
	{
		m_h = h;
	}


	int S()
	{
		return (2 * (m_l * m_w + m_l * m_h + m_w * m_h));
	}
	int V()
	{
		return m_l * m_h * m_w;
	}

	bool equality(CUBE c1)
	{
		if (c1.S() == S() && c1.V() == V())
			return true;
		return false;
	}


public:
	int m_l, m_w, m_h;
};

bool equalityout(CUBE c1, CUBE c2)
{
	if (c1.S() == c2.S() && c1.V() == c2.V())
		return true;
	return false;
}

void cube_project(void)
{
	CUBE c1,c2;
	c1.set_l(100);
	c1.set_w(100);
	c1.set_h(100);

	c2.set_l(100);
	c2.set_w(100);
	c2.set_h(100);

	cout << "小项目--c1-长方体的面积是:" << c1.S() << endl;
	cout << "小项目--c1-长方体的体积是:" << c1.V() << endl;
	if(c1.equality(c2))
		cout << "小项目--类内判断--C1和C2是相等的" << endl;
	else
		cout << "小项目--类内判断--C1和C2是不相等的" << endl;
	if (equalityout(c1,c2))
		cout << "小项目--类外判断--C1和C2是相等的" << endl;
	else
		cout << "小项目--类外判断--C1和C2是不相等的" << endl;
}

在这里插入图片描述

4.2、对象的初始化和清理

4.2.1、构造函数和析构函数

C++要想完成对象的初始化和清理操作需要使用两个函数:构造函数和析构函数

  • 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用
  • 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作

构造函数语法: 类名(){}

  1. 构造函数,没有返回值也不写void
  2. 函数名称与类名相同
  3. 构造函数可以有参数,因此可以发生重载
  4. 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次

析构函数语法: ~类名(){}

  1. 析构函数,没有返回值也不写void
  2. 函数名称与类名相同,在名称前加上符号 ~
  3. 析构函数不可以有参数,因此不可以发生重载
  4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
4.2.2、构造函数的分类及调用

两种分类方式

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

三种调用方式

  • 括号法
  • 显示法
  • 隐式转换法
#include <iostream>
using namespace std;
class person1
{
public:
	person1()
	{
		cout << "person1的构造函数" << endl;
	}
	~person1()
	{
		cout << "person1的析构函数" << endl;
	}
};
void func1()
{
	person1 per1;
}
/*
构造函数的分类及调用
两种分类方式:
1、按参数分为: 有参构造和无参构造
2、按类型分为: 普通构造和拷贝构造
三种调用方式:
1、括号法
2、显示法
3、隐式转换法*/
class person2
{
public:
	//无惨构造
	person2()
	{
		cout << "person2无参构造函数" << endl;
	}
	person2(int age)
	{
		cout << "括号法----person2有参构造函数" << endl;
		m_age = age;
	}
	person2(const person2 &p)
	{
		cout << "person2拷贝构造函数" << endl;
		m_age = p.m_age;
	}
	~person2()
	{
		cout << "person2析构函数" << endl;
	}
public:
	int m_age;
};
void func2()
{

	person2 p1;//调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明
	person2 p2(40); //2.1 括号法,常用
	person2 p3(p2);

	person2 p4 = person2(80);
	person2 p5 = person2(p4);

	//注意:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明 
	//Person p5(p4);
}
void construc_destruc(void)
{
	func1();
	func2();
}

在这里插入图片描述

4.2.3、拷贝构造函数调用时机

C++中拷贝构造函数调用时机通常有三种情况

  • 使用一个已经创建完毕的对象来初始化一个新对象
  • 值传递的方式给函数参数传值
  • 以值方式返回局部对象
#include <iostream>
using namespace std;
/*
C++中拷贝构造函数调用时机通常有三种情况
1、使用一个已经创建完毕的对象来初始化一个新对象
2、值传递的方式给函数参数传值
3、以值方式返回局部对象
*/

class person3
{
public:
	//无惨构造
	person3()
	{
		cout << "person3无参构造函数" << endl;
	}
	person3(int age)
	{
		cout << "括号法----person3有参构造函数    ";
		m_age = age;
		cout << "年龄 = " << m_age << endl;
	}
	person3(const person3& p)
	{
		cout << "person3拷贝构造函数  ";
		m_age = p.m_age;
		cout << "年龄 = " << m_age << endl;
	}
	~person3()
	{
		cout << "person3析构函数" << endl;
	}
public:
	int m_age;
};

//1、使用一个已经创建完毕的对象来初始化一个新对象
void func4()
{
	person3 man(100);
	person3 newman(man);	//调用拷贝构造函数
	person3 newman2 = man;	//调用拷贝构造函数
	cout << "1、使用一个已经创建完毕的对象来初始化一个新对象" << endl;
}

//2、值传递的方式给函数参数传值
void dowork(person3 p1)
{
	cout << "2、值传递的方式给函数参数传值" << endl;
}
void func5()
{
	person3 man(100);
	dowork(man);	
}

//3、以值方式返回局部对象
person3 dowork1()
{
	person3 p1(56);
	cout << "局部变量p1的地址是:" << (int*)&p1 << endl;
	return p1;
}
void func6()
{
	person3 man = dowork1();
	cout << "返回值---局部变量p1的地址是:" << (int*)&man << endl;
	cout << "3、以值方式返回局部对象--age = " << man.m_age << endl;
}
void copytime(void)
{
	func4();
	func5();
	func6(); 
}

在这里插入图片描述

构造函数调用规则:
默认情况下,c++编译器至少给一个类添加3个函数

  • 默认构造函数(无参,函数体为空)
  • 默认析构函数(无参,函数体为空)
  • 默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则如下:

  • 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
  • 如果用户定义拷贝构造函数,c++不会再提供其他构造函数
4.2.4、深拷贝和浅拷贝
  • 浅拷贝:简单的赋值拷贝操作
  • 深拷贝:在堆区重新申请空间,进行拷贝操作
#include <iostream>
using namespace std;

//浅拷贝:简单的赋值拷贝操作
//深拷贝:在堆区重新申请空间,进行拷贝操作

class person4
{
public:
	person4()
	{
		cout << "person4---无参构造函数!" << endl;
	}
	person4(int age, int height)
	{

		cout << "person4---有参构造函数!" << endl;
		m_age = age;
		m_height = new int(height);
	}
	person4(const person4 &p)
	{
		cout << "person4---拷贝构造函数!" << endl;
		m_age = p.m_age;
		m_height = new int(*p.m_height);
	}
	//构造函数
	~person4()
	{
		cout << "person4---析构函数" << endl;
		if (m_height != NULL)
		{
			delete m_height;
		}
	}
public:
	int m_age;
	int* m_height;
};
void Deep_and_shallow_copy(void)
{
	person4 p1(50, 96);
	person4 p2(p1);
	cout << "p1的年龄: " << p1.m_age << " 身高: " << *p1.m_height << endl;
	cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl;
}
//总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

在这里插入图片描述

4.2.5、初始化列表
  • 作用:C++提供了初始化列表语法,用来初始化属性
  • 语法: 构造函数():属性1(值1),属性2(值2)… {}
#include <iostream>

using namespace std;

class person5
{
public:
	person5(int i, float f, string name) :m_i(i), m_f(f), m_name(name)
	{
		cout << "person5---有参构造函数---初始化列表" << endl;
		cout << "person5---m_i = " << m_i << endl;
		cout << "person5---m_f = " << m_f << endl;
		cout << "person5---m_name = " << m_name << endl;
	}
	~person5()
	{
		cout << "person5---有参析构函数---初始化列表" << endl;
	}

private:
	int m_i;
	float m_f;
	string m_name;
};

void configlist(void)
{
	person5 p(1, 56.65, "王二毛");
}

在这里插入图片描述

4.2.6、类对象作为类成员

C++类中的成员可以是另一个类的对象,我们称该成员为 对象成员
举例:

class A {} 
class B 
{ 
	A a; 
}
//B类对象中有对象A作为成员,A为对象成员

那么当创建B对象时,A与B的构造和析构的顺序是谁先谁后?

  • 当类中成员是其他类对象时,我们称该成员为 对象成员
  • 构造的顺序是 :先调用对象成员的构造,再调用本类构造
  • 析构顺序与构造相反
#include <iostream>
using namespace std;

class phone
{
public:
	phone(string pname)
	{
		cout << "phone---有参构造函数" << endl;
		m_phone = pname;
	}
	~phone()
	{
		cout << "phone---有参析构函数" << endl;
	}
public:
	string m_phone;
};

class person6
{
public:
	person6(int age, string name, string pname) :m_age(age), m_name(name), m_pname(pname)
	{
		cout << "person6---有参构造函数" << endl;
		cout << m_age << "岁的" << m_name << "使用的是" << m_pname.m_phone << "牌手机" << endl;

	}
	~person6()
	{
		cout << "person6---有参析构函数" << endl;
	}
public:
	string m_name;
	int m_age;
	phone m_pname;
};
void object_member(void)
{
	person6 p(56, "王二毛", "诺基亚");
}

在这里插入图片描述

4.2.7、静态成员

静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员
静态成员分为:静态成员变量静态成员函数

静态成员变量

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

静态成员函数

  • 所有对象共享同一个函数
  • 静态成员函数只能访问静态成员变量
#include <iostream>
using namespace std;

//所有对象共享同一份数据
//在编译阶段分配内存
//类内声明,类外初始化

//静态变量
class staticmemvar
{
public:
	//类内声明,类外初始化
	//staticmemvar(int a) :m_a(a){}

	staticmemvar()
	{
		cout << "静态成员变量---staticmemvar---有参构造函数" << endl;
		cout << "静态成员变量---所有对象共享同一份数据" << endl;
		cout << "静态成员变量---也有权限之分" << endl;
	}
	~staticmemvar()
	{
		cout << "staticmemvar---有参析构函数" << endl;
	}

public:
	static int m_a;
private:
	static int m_b;
};
int staticmemvar::m_a = 100;
int staticmemvar::m_b = 189;

//1 程序共享一个函数
//2 静态成员函数只能访问静态成员变量
//静态函数
class staticmemfun
{
public:

	static void func()
	{
		m_a = 45;
//		m_b = 5355;
//		m_c = 10;
		cout << "公共权限----func调用" << endl;
		cout << "静态成员函数---所有对象共享一个函数" << endl;
		cout << "静态成员函数---也有权限之分" << endl;
		cout << "静态成员函数---只能访问静态成员变量" << endl;
	}
	~staticmemfun()
	{
		cout << "staticmemvar---有参析构函数" << endl;
	}

public:
	static int m_a;
	int m_c;
private:
	//静态成员函数也是有访问权限的 
	static void func2() 
	{ 
		cout << "私有权限----func2调用" << endl;
	}
	static int m_b;
};
int staticmemfun::m_a = 1089;
int staticmemfun::m_b = 199;
void staticfunc(void)
{
	staticmemvar p1;
	p1.m_a = 5632;
	cout << "p1.m_a = " << p1.m_a << endl;
//	cout << "p1.m_b = " << p1.m_b << endl;//无权限
	staticmemvar p2;
	p2.m_a = 896454;
	cout << "p1.m_a = " << p1.m_a << endl;//共享同一份数据
//	cout << "p1.m_b = " << p1.m_b << endl; //无权限 
	cout << "p2.m_a = " << p2.m_a << endl;
//	cout << "p2.m_b = " << p2.m_b << endl;//无权限

	//2、通过类名 
	cout << "m_a = " << staticmemvar::m_a << endl;
//	cout << "m_b = " << staticmemvar::m_b << endl;//无权限

	staticmemfun f1;
	f1.func();
	//通过类名
	staticmemfun::func();
//	staticmemfun::func2();//无权限
}

在这里插入图片描述

4.3、C++对象模型和this指针

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

在C++中,类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上

class Person 
{
	public: 
	Person() 
	{
	    mA = 0; 
	}
	//非静态成员变量占对象空间 
	int mA; 
	//静态成员变量不占对象空间 
	static int mB; 
	//函数也不占对象空间,所有函数共享一个函数实例 
	void func() 
	{ 
		cout << "mA:" << this->mA << endl; 
	}
	//静态成员函数也不占对象空间 
	static void sfunc() 
	{
	} 
};
int main() 
{ 
	cout << sizeof(Person) << endl; 
	system("pause"); 
	return 0; 
}

在这里插入图片描述

4.3.2、this指针概念

每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
那么问题是:这一块代码是如何区分那个对象调用自己的呢?

  • c++通过提供特殊的对象指针,this指针,解决上述问题。this指针指向被调用的成员函数所属的对象
  • this指针是隐含每一个非静态成员函数内的一种指针
  • this指针不需要定义,直接使用即可

this指针的用途:

  • 当形参和成员变量同名时,可用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可使用return *this
class person7
{
public:
	person7(int age)
	{
		cout << "this--person7--有参构造函数" << endl;
		this->age = age;
	}
	person7 &personaddperson(person7 p)
	{
		this->age += p.age;
		//返回自身地址
		return *this;
	}
public:
	int age;
};

void objeck_this(void)
{
	person7 p1(45);
	cout << "p1.age = " << p1.age << endl;
	person7 p2(78);
	p2.personaddperson(p1).personaddperson(p1).personaddperson(p1);
	cout << "p2.age = " << p2.age << endl;
}

在这里插入图片描述

4.3.3、空指针访问成员函数
class person8
{
public:
	int age;

public:
	void show1()
	{
		cout << "show1" << endl;
	}
	void show2()
	{
		if (this == NULL)//如果没有添加判断,就会报错
			return;
		cout << "age = " << age << endl;
	}
};

void objeck_this(void)
{
	person8* p3 = NULL;
	p3->show1();
	p3->show2();
}

在这里插入图片描述

4.3.4、const修饰成员函数

常函数:

  • 成员函数后加const后我们称为这个函数为常函数
  • 常函数内不可以修改成员属性
  • 成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象:

  • 声明对象前加const称该对象为常对象
  • 常对象只能调用常函数
class person9
{
public:
	int ma;
	mutable int mb;
public:
	person9()
	{
		cout << "person9--无参构造函数" << endl;
		ma = 0;
		mb = 0;
	}
	void show1() const {
		//ma = 100;//ma在定义时没有使用mutable关键字,常函数不能修改成员的属性
		//this = NULL;//
		mb = 345;//mb在定义时使用了mutable关键字,在常函数中是可以进行修改的
		cout << "show1常函数 的 ma = " << this->ma << endl;
		cout << "show1常函数 的 mb = " << this->mb << endl;
	}
	void show2(){
		ma = 5547;
		mb = 67679;
		cout << "show2函数 的 ma = " << this->ma << endl;
		cout << "show2函数 的 mb = " << this->mb << endl;
	}
};

void objeck_this(void)
{
	const person9 p4;//常对象
	person9 p5;//常对象
	cout << "person9.ma = " << p4.ma << endl;
	cout << "person9.ma = " << p4.mb << endl;
	//p4.ma = 2245;//常对象不能修改成员变量的值,但是可以访问
	p4.mb = 5657;但是常对象可以修改mutable修饰成员变量
	//常对象访问成员函数
	
	//p4.show2();//常对象只能调用常函数
	p5.show2();
	p4.show1();
}

在这里插入图片描述

4.4、友元

生活中你的家有客厅(Public),有你的卧室(Private)
客厅所有来的客人都可以进去,但是你的卧室是私有的,也就是说只有你能进去
但是呢,你也可以允许你的好闺蜜好基友进去。
在程序里,有些私有属性也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术
友元的目的就是让一个函数或者类 访问另一个类中私有成员

友元的关键字为 friend

友元的三种实现

  • 全局函数做友元
  • 类做友元
  • 成员函数做友元
#include <iostream>
using namespace std;

/*
友元的三种实现
	1、全局函数做友元
	2、类做友元
	3、成员函数做友元
*/
class mybuilding;
class goodfriend1
{
public:
	goodfriend1(mybuilding *bulid, string name);
	void visit();

private:
	mybuilding *build;
	string m_name;
};

class goodfriend2
{
public:
	goodfriend2(mybuilding* bulid, string name);
	void visit1();
	void visit2();

private:
	mybuilding* build;
	string m_name;
};

class mybuilding
{
	//使用friend关键字,说明myfriend全局函数是mybuilding的好朋友
	friend void myfriend(mybuilding& building);	//1、全局函数-做友元
	friend class goodfriend1;					//2、类-做友元
	friend void goodfriend2::visit1();			//3、成员函数做友元--visit1可以访问,visit2不可以访问
public:
	mybuilding(string sittingroom,string bedroom)
	{
		cout << "mybuilding--有参构造函数" << endl;
		cout << "1、全局函数-做友元" << endl;
		cout << "2、类-做友元" << endl;
		cout << "3、成员函数做友元--visit1可以访问,visit2不可以访问" << endl;
		this->m_bedroom = bedroom;
		this->m_sittingroom = sittingroom;
	}
public:
	string m_sittingroom;//客厅
private:
	string m_bedroom;//卧室
};

//1
void myfriend(mybuilding& building)
{
	cout << "**********************************************************" << endl;
	cout << "myfriend--全局函数--做友元" << endl;
	cout << "myfriend--全局函数--正在访问:" << building.m_bedroom << endl;
	cout << "myfriend--全局函数--正在访问:" << building.m_sittingroom << endl;
}

//2
goodfriend1::goodfriend1(mybuilding *bulid, string name)
{
	this->build = bulid;
	cout << "**********************************************************" << endl;
	cout << "goodfriend1--" << name << "--有参构造函数" << endl;
	cout << "goodfriend1--" << name << "--类--做友元" << endl;
	this->m_name = name;
}
void goodfriend1::visit()
{
	cout << "goodfriend1--visit--" << this->m_name << "--类--正在访问:" << build->m_bedroom << endl;
	cout << "goodfriend1--visit--" << this->m_name << "--类--正在访问:" << build->m_sittingroom << endl;
}

//3
goodfriend2::goodfriend2(mybuilding* bulid, string name)
{
	this->build = bulid;
	cout << "**********************************************************" << endl;
	cout << "goodfriend2--" << name << "--有参构造函数" << endl;
	cout << "goodfriend2--" << name << "--成员函数--做友元" << endl;
	this->m_name = name;

}
void goodfriend2::visit1()
{
	cout << "goodfriend2--visit1--" << this->m_name << "--成员函数--正在访问:" << build->m_bedroom << endl;
	cout << "goodfriend2--visit1--" << this->m_name << "--成员函数--正在访问:" << build->m_sittingroom << endl;
}
void goodfriend2::visit2()
{
//	cout << "goodfriend2--visit2--" << this->m_name << "--成员函数--正在访问:" << build->m_bedroom << endl;
	cout << "goodfriend2--visit2--" << this->m_name << "--成员函数--正在访问:" << build->m_sittingroom << endl;
}

void friendtest(void)
{
	mybuilding b1("客厅","卧室");//定义

	//1
	myfriend(b1);//全局函数做友元

	//2
	goodfriend1 g1(&b1, "王二毛");//类做友元
	g1.visit();

	//3
	goodfriend2 g2(&b1, "王三毛");//成员函数做友元
	g2.visit1();
	g2.visit2();
}

在这里插入图片描述

4.5、运算符重载

  • 运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
4.5.1、加号运算符重载

注意:

  • 对于内置的数据类型的表达式的的运算符是不可能改变的
  • 不要滥用运算符重载
#include <iostream>
using namespace std;

class person10
{
public:
	person10()
	{
		cout << "perosn10--加号运算符重载--无参构造函数" << endl;
	}

	person10(int a, int b)
	{
		cout << "perosn10--加号运算符重载--有参构造函数" << endl;
		this->m_a = a;
		this->m_b = b;
	}


	/*person10 operator+(person10& p)
	{
		person10 temp;
		temp.m_a = this->m_a + p.m_a;
		temp.m_b = this->m_b + p.m_b;
		return temp;
	}*/

public:
	int m_a;
	int m_b;
};

person10 operator+(person10 &p1, person10 &p2)
{
	person10 temp;
	temp.m_a = p1.m_a + p2.m_a;
	temp.m_b = p1.m_b + p2.m_b;
	return temp;
}
person10 operator+(person10& p1, int num)
{
	person10 temp;
	temp.m_a = p1.m_a + num;
	temp.m_b = p1.m_b + num;
	return temp;
}


void add_operator_overloading(void)
{
	person10 p1(10, 56);
	person10 p2(20, 86);
	person10 p3 = p1 + p2;
	//成员函数加号运算符重载调用本质
	//person10 p3 = p1.operator+(p2);

	//全局函数加号运算符重载调用本质
	//person10 p4 = operator+(p1, p2);

	//运算符重载、也可以发生函数重载
	//person10 p4 = operator+(p1, 100);
	person10 p4 = p1 + 100;
	
	
	cout << "加号运算符重载--p3.m_a = " << p3.m_a << endl;
	cout << "加号运算符重载--p3.m_b = " << p3.m_b << endl;
	cout << "加号运算符重载--p4.m_a = " << p4.m_a << endl;
	cout << "加号运算符重载--p4.m_b = " << p4.m_b << endl;
}

在这里插入图片描述

4.5.2、左移运算符重载

作用:可以输出自定义数据类型
注意:

  • 重载左移运算符配合友元可以实现输出自定义数据类型

一般情况下cout可以输出整形、浮点型、等等等等

class person
{
public:
int m_a;
int m_b;
}
person p;
cout << p;//就会报错

#include <iostream>
using namespace std;


class person11
{
	friend ostream& operator<<(ostream& out, person11& p);

public:
	person11(int a, int b)
	{
		cout << "person11--左移运算符--有参构造函数" << endl;
		m_a = a;
		m_b = b;
	}
	//成员函数实现不了 p << cout 不是我们想要的效果 
	//void operator<<(Person& p){ 
	
	//}
private:
	int m_a;
	int m_b;
};
  
ostream& operator<<(ostream &out, person11 &p)
{
	out << "a = " << p.m_a << endl;
	out << "b = " << p.m_b;
	return out;
}

void leftshift_operator_overloading(void)
{
	person11 p(45,798);
	cout << p << endl << "hello" << endl;
}

在这里插入图片描述

4.5.3、递增、递减运算符重载

作用: 通过重载递增运算符,实现自己的整型数据
注意:

  • 前置递增返回引用,后置递增返回值
#include <iostream>
using namespace std;

class person12
{
	friend ostream& operator<<(ostream& out, person12 p);

public:
	person12()
	{
		cout << "person12--递增运算符重载--无参构造函数" << endl;
		m_num = 5;
	}

	//前缀递增++
	person12& operator++()
	{
		m_num++;
		return *this;
	}
	//后缀递增++
	person12 operator++(int)
	{
		//记录当前本身的值
		person12 temp = *this;
		//然后让本身的值加1
		m_num++;
		//但是返回的是以前的值,达到先返回后++;
		return temp;
	}
	
	//前缀递减--
	person12& operator--()
	{
		m_num--;
		return *this;
	}
	//后缀递增++
	person12 operator--(int)
	{
		//记录当前本身的值
		person12 temp = *this;
		//然后让本身的值减1
		m_num--;
		//但是返回的是以前的值,达到先返回后++
		return temp;
	}

private:
	int m_num;
};

//左移运算符
ostream& operator<<(ostream& out, person12 p)
{
	out << "p.m_num = " << p.m_num;
	return out;
}


void increase_operator_overloading(void)
{
	person12 p1;
	person12 p2;
	person12 p3;
	person12 p4;
	cout << "p1.m_num = " << ++p1 << endl;
	cout << "p1.m_num = " << p1 << endl;

	cout << "p2.m_num = " << p2++ << endl;
	cout << "p2.m_num = " << p2 << endl;

	cout << "p3.m_num = " << --p3 << endl;
	cout << "p3.m_num = " << p3 << endl;

	cout << "p4.m_num = " << p4-- << endl;
	cout << "p4.m_num = " << p4 << endl;
}

在这里插入图片描述

4.5.4、赋值运算符重载

c++编译器至少给一个类添加4个函数

  • 默认构造函数(无参,函数体为空)
  • 默认析构函数(无参,函数体为空)
  • 默认拷贝构造函数,对属性进行值拷贝
  • 赋值运算符operator,对属性进行值拷贝

如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题

#include <iostream>
using namespace std;

class person13
{
public:
	person13(int age)
	{
		cout << "person13--赋值运算符重载--有参构造函数" << endl;
		
		m_age = new int(age);
	}
	~person13()
	{
		if (m_age != NULL)
		{
			delete m_age;
			m_age = NULL;
		}
		cout << "person13--赋值运算符重载--有参析构函数" << endl;
	}
	//赋值操作-返回的是引用
	person13& operator=(person13 &p)
	{
		//先判断堆区的地址中是不是空
		if (m_age != NULL)
		{
			delete m_age;
			m_age = NULL;
		}
		//编译器提供的代码是浅拷贝--会出现地址重复delete,错误
		//m_age = p.m_age;

		//提供深拷贝 解决浅拷贝的问题
		m_age = new int(*p.m_age);
		return *this;
	}

public:
	int* m_age;
};
void assigning_operator(void)
{
	person13 p1(89);
	person13 p2(45);
	person13 p3(989);

	p3 = p2 = p1;
	cout << "p1的年龄为:" << *p1.m_age << endl;
	cout << "p2的年龄为:" << *p2.m_age << endl;
	cout << "p3的年龄为:" << *p3.m_age << endl;
}

在这里插入图片描述

4.5.5、关系运算符重载

作用:重载关系运算符,可以让两个自定义类型对象进行对比操作

#include <iostream>
using namespace std;

class person14
{
public:
	person14(string name, int age)
	{
		cout << "person14--关系运算符重载--有参构造函数" << endl;
		m_name = name;
		m_age = age;
	}
	~person14()
	{
		cout << "person14--关系运算符重载--析构函数" << endl;
	}
	bool operator==(person14 &p)
	{
		if (this->m_age == p.m_age && this->m_name == p.m_name)
			return true;
		else
			return false;
	}
	bool operator!=(person14& p)
	{
		if (this->m_age == p.m_age && this->m_name == p.m_name)
			return false;
		else
			return true;
	}

public:
	string m_name;
	int m_age;
};

void relational_operator(void)
{
	person14 p1("王二毛", 5986);
	person14 p2("王二毛", 5986);
	person14 p3("王二毛", 34);
	person14 p4("王二毛", 596);
	if (p1 == p2)
		cout << "p1和p2相等" << endl;
	else
		cout << "p1和p2不相等" << endl;
	if (p3 == p4)
		cout << "p3和p4相等" << endl;
	else
		cout << "p3和p4不相等" << endl;
}

在这里插入图片描述

4.5.6、函数调用运算符重载
  • 函数调用运算符 () 也可以重载
  • 由于重载后使用的方式非常像函数的调用,因此称为仿函数
  • 仿函数没有固定写法,非常灵活
#include <iostream>
using namespace std;

class function1
{
public:
	void operator()(string massage)//也可以发生重载--参数不一样即可
	{
		cout << massage << endl;
	}
	int operator()(int a,int b)
	{
		return (a + b);
	}
};

void Func(string massage)
{
	cout << massage << endl;
}

void func_operator(void)
{
	function1 f1;
	f1("函数调用运算符重载--大家好!我叫王二毛");
	Func("函数调用--大家好!我叫王三毛");
	cout << f1(34, 56) << endl;

	//匿名对象调用
	cout << function1()(69, 31) << endl;
}

在这里插入图片描述

4.6、继承

继承是面向对象三大特性之一
有些类与类之间存在特殊的关系,例如下图中:
在这里插入图片描述
我们发现,定义这些类时,下级别的成员除了拥有上一级的共性,还有自己的特性。
这个时候我们就可以考虑利用继承的技术,减少重复代码

4.6.1、继承的基本语法

例如我们看到很多网站中,都有公共的头部,公共的底部,甚至公共的左侧列表,只有中心内容不同
接下来我们分别利用普通写法和继承的写法来实现网页中的内容,看一下继承存在的意义以及好处
普通实现:

#include <iostream>
using namespace std;

//Java页面 
class Java 
{
public: 
	void header() 
	{ 
		cout << "首页、公开课、登录、注册...(公共头部)" << endl; 
	}
	void footer() 
	{ 
		cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
	}
	void left() 
	{ 
		cout << "Java,Python,C++...(公共分类列表)" << endl; 
	}
	void content() 
	{ 
		cout << "JAVA学科视频" << endl; 
	} 
};

//Python页面 
class Python 
{
public: 
	void header() 
	{ 
		cout << "首页、公开课、登录、注册...(公共头部)" << endl; 
	}
	void footer() 
	{ 
		cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
	}void left() 
	{ 
		cout << "Java,Python,C++...(公共分类列表)" << endl; 
	}
	void content() 
	{ cout << "Python学科视频" << endl; 
	} 
};
//C++页面
class CPP 
{
public: 
	void header() 
	{ 
		cout << "首页、公开课、登录、注册...(公共头部)" << endl; 
	}
	void footer() 
	{ 
		cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl; 
	}
	void left() 
	{ 
		cout << "Java,Python,C++...(公共分类列表)" << endl; 
	}
	void content() 
	{ 
		cout << "C++学科视频" << endl; 
	}
};

void inherit_basic(void)
{ 
	//Java页面 
	cout << "Java下载视频页面如下: " << endl; 
	Java ja;
	ja.header(); 
	ja.footer(); 
	ja.left();
	ja.content(); 
	cout << "--------------------" << endl; 
	//Python页面 
	cout << "Python下载视频页面如下: " << endl; 
	Python py;
	py.header();
	py.footer(); 
	py.left(); 
	py.content(); 
	cout << "--------------------" << endl; 
	//C++页面 
	cout << "C++下载视频页面如下: " << endl;
	CPP cp;
	cp.header();
	cp.footer();
	cp.left();
	cp.content();
}

继承实现:

//公共页面
 class BasePage
 {
 public: 
	 void header() 
	 { 
		 cout << "首页、公开课、登录、注册...(公共头部)" << endl; 
	 }
	 void footer() 
	 { 
		 cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl; 
	 }
	 void left() 
	 {
		 cout << "Java,Python,C++...(公共分类列表)" << endl; 
	 }
 };
 //Java页面 
 class java : public BasePage 
 {
 public: 
	 void content() 
	 { 
		 cout << "JAVA学科视频" << endl;
	 } 
 };

 //python界面
 class python : public BasePage
 {
 public:
	 void content()
	 {
		 cout << "Python科学视频" << endl;
	 }
 };

 //C++界面
 class cpp : public BasePage
 {
 public:
	 void content()
	 {
		 cout << "C++科学视频" << endl;
	 }
 };


 void inherit_basic(void)
 {
	 //Java页面 
	 cout << "Java下载视频页面如下: " << endl;
	 java ja;
	 ja.header();
	 ja.footer();
	 ja.left();
	 ja.content();
	 cout << "--------------------" << endl;
	 //Python页面 
	 cout << "Python下载视频页面如下: " << endl;
	 python py;
	 py.header();
	 py.footer();
	 py.left();
	 py.content();
	 cout << "--------------------" << endl;
	 //C++页面 
	 cout << "C++下载视频页面如下: " << endl;
	 cpp cp;
	 cp.header();
	 cp.footer();
	 cp.left();
	 cp.content();
 }

在这里插入图片描述
总结:

  • 继承的好处:可以减少重复的代码

class A : public B;
A 类称为子类 或 派生类
B 类称为父类 或 基类

派生类中的成员,包含两大部分:

  • 一类是从基类继承过来的,
  • 一类是自己增加的成员。

从基类继承过过来的表现其共性,而新增的成员体现了其个性。

4.6.2、继承方式

继承的语法: class 子类 : 继承方式 父类

继承方式一共有三种:

  • 公共继承
  • 保护继承
  • 私有继承

在这里插入图片描述

  • 由图可知,不管是什么继承,子类都不能访问父类的私有权限
  • 公共继承方式下,子类中除了不能访问父类的私有权限,子类中的其他权限不会发送改变,父类是public,子类也是public,父类是protected,子类也是protected
  • 保护继承方式下,子类中除了不能访问父类的私有权限,子类中的其他权限都会变成保护权限:父类是public,子类变成protected,父类是protected,子类也是protected
  • 私有继承方式下,子类中除了不能访问父类的私有权限,子类中的其他权限都会变成私有权限:父类是public,子类变成private,父类是protected,子类变成private

具体例子:

#include <iostream>
using namespace std;

class base
{
public:
	int m_a;
protected:
	int m_b;
private:
	int m_c;
};

//公共继承
class sun1 :public base
{
public:
	void func()
	{
		m_a = 100;//m_a在父类中是公共权限,子类中是公共权限
		m_b = 200;//m_b在父类中是保护权限,子类中是保护权限
		//m_c = 300;//m_c在父类中是私有权限,子类不可访问
	}
};


//保护继承
class sun2 :protected base
{
public:
	void func()
	{
		m_a = 100;//m_a在父类中是公共权限,子类中变成保护权限
		m_b = 200;//m_b在父类中是保护权限,子类中变成保护权限
		//m_c = 300;//m_c在父类中是私有权限,子类不可访问
	}
};

//私有继承
class sun3 :private base
{
public:
	void func()
	{
		m_a = 100;//m_a在父类中是公共权限,子类中变成私有权限
		m_b = 200;//m_b在父类中是保护权限,子类中变成私有权限
		//m_c = 300;//m_c在父类中是私有权限,子类不可访问
	}
};
//子类grandsun3 公共继承 父类sun3
class grandsun3 :public sun3
{
public:
	void func()
	{
		//m_a = 21000;//m_a在sun3中是私有权限,所有子类grandsun3是没有权限访问的
		//m_b = 324424;//m_b在sun3中是私有权限,所有子类grandsun3是没有权限访问的
	}
};



void inherit_method(void)
{
	//公共继承
	sun1 s1;
	s1.m_a = 10000;//sun1中的m_a是公共权限,类外可以访问
	//s1.m_b = 10000;//sun1中的m_b是保护权限,类外不可以访问
	//s1.m_c = 10000;//sun1中的m_c是私有权限,类外不可以访问

	//保护继承
	sun2 s2;
	//s2.m_a = 10000;//sun2中的m_a是保护权限,类外不能访问
	//s2.m_b = 10000;//sun2中的m_b是保护权限,类外不能访问
	//s2.m_c = 10000;//sun2中的m_c是私有权限,类外不能访问


	//私有继承
	sun3 s3;
	//s3.m_a = 10000;//sun3中的m_a是私有权限,类外不能访问
	//s3.m_b = 10000;//sun3中的m_b是私有权限,类外不能访问
	//s3.m_c = 10000;//sun3中的m_c是私有权限,类外不能访问
}

4.6.3、继承中的对象模型

问题:从父类继承过来的成员,哪些属于子类对象中?

#include <iostream>
using namespace std;


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 << "sizeof Son = " << sizeof(Son) << endl; 
}
int main() 
{ 
	test01(); 
	system("pause"); 
	return 0; 
}

利用工具查看:
在这里插入图片描述
结论: 父类中私有成员也是被子类继承下去了,只是由编译器给隐藏后访问不到

4.6.4、继承中构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类的构造函数
问题:父类和子类的构造和析构顺序是谁先谁后?
栗子:

#include <iostream>
using namespace std;

class base1
{
public:
	base1()
	{
		cout << "父类base1---无参构造函数" << endl;
	}
	~base1()
	{
		cout << "父类base1---无参析构函数" << endl;
	}
};

class son4 :public base1
{
public:
	son4()
	{
		cout << "子类son4---无参构造函数" << endl;
	}
	~son4()
	{
		cout << "子类son4---无参析构函数" << endl;
	}
	
};

void inherit_order(void)
{
	son4 s1;
}

在这里插入图片描述
总结:继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反

4.6.5、继承同名成员处理方式

问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域
#include <iostream>

using namespace std;


class base3
{
public:
	base3()
	{
		cout << "父类base3---无参构造函数" << endl;
		m_a = 438975754390;
		m_b = 0;
	}
	void func()
	{
		cout << "父类base3---func()函数调用" << endl;
	}
	void func(int a)
	{
		cout << "父类base3---func(int a)函数调用" << endl;
		
	}
public:
	int m_a;
	int m_b;
};

class son6: public base3
{
public:
	son6()
	{
		cout << "子类son6---无参构造函数" << endl;
		m_a = 100;
		m_b = 46456l;
	}

	//当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数 
	//如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域
	void func()
	{
		cout << "子类son6---func()函数调用" << endl;
	}
public:
	int m_a;
	int m_b;
};

void inherit_samename(void)
{
	son6 s;
	cout << "子类son6的m_a:" << s.m_a << endl;
	cout << "父类base3的m_a:" << s.base3::m_a << endl;

	s.func();
	s.base3::func();
	s.base3::func(4545);
}

在这里插入图片描述
总结:

  1. 子类对象可以直接访问到子类中同名成员
  2. 子类对象加作用域可以访问到父类同名成员
  3. 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中
    同名函数
4.6.6、继承同名静态成员处理方式

问题:继承中同名的静态成员在子类对象上如何进行访问?
静态成员和非静态成员出现同名,处理方式一致

  • 访问子类同名成员 直接访问即可
  • 访问父类同名成员 需要加作用域
#include <iostream>

using namespace std;


class base4
{
public:
	base4()
	{
		cout << "父类base4---无参构造函数" << endl;
		/*m_a = 454390;
		m_b = 0;*/
	}
	static void func()
	{
		cout << "父类base4---func()函数调用" << endl;
	}
	static void func(int a)
	{
		cout << "父类base4---func(int a)函数调用" << endl;
	}
public:
	static int m_a;
	static int m_b;
};
int base4::m_a = 34;
int base4::m_b = 89;

class son7 : public base4
{
public:
	son7()
	{
		cout << "子类son7---无参构造函数" << endl;
		/*m_a = 100;
		m_b = 46456l;*/
	}

	//当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数 
	//如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域
	static void func()
	{
		cout << "子类son7---func()函数调用" << endl;
	}
public:
	static int m_a;
	static int m_b;
};

int son7::m_a = 5465;
int son7::m_b = 4354;

void inherit_samename_static(void)
{
	cout << "通过对象访问" << endl;
	son7 s;
	cout << "子类son7的m_a:" << s.m_a << endl;
	cout << "父类base4的m_a:" << s.base4::m_a << endl;
	s.func();
	s.base4::func();
	s.base4::func(4545);

	//出现同名,子类会隐藏掉父类中所有同名成员函数,需要加作作用域访问
	cout << "通过类名访问" << endl;
	cout << "子类son7的m_a:" << son7::m_a << endl;
	cout << "父类base4的m_a:" << son7::base4::m_a << endl;
	son7::func();
	son7::base4::func();
	son7::base4::func(676);
}

在这里插入图片描述
总结:同名静态成员处理方式和非静态处理方式一样,只不过有两种访问的方式(通过对象 和 通
过类名)

4.6.7、多继承语法

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

#include <iostream
using namespace std;
class base5
{
public:
	base5()
	{
		cout << "父类base5---无参构造函数" << endl;
		m_a = 43897;
		m_b = 890;
	}
	~base5()
	{
		cout << "父类base5---无参析构函数" << endl;
	}
	void func()
	{
		cout << "父类base5---func()函数调用" << endl;
	}
	void func(int a)
	{
		cout << "父类base5---func(int a)函数调用" << endl;

	}
public:
	int m_a;
	int m_b;
};
class base6
{
public:
	base6()
	{
		cout << "父类base6---无参构造函数" << endl;
		m_a = 234;
		m_b = 56;
	}
	~base6()
	{
		cout << "父类base6---无参析构函数" << endl;
	}
	void func()
	{
		cout << "父类base6---func()函数调用" << endl;
	}
	void func(int a)
	{
		cout << "父类base6---func(int a)函数调用" << endl;

	}
public:
	int m_a;
	int m_b;
};
//多继承的构造函数的顺序是,先base5->base6->son8
//析构函数相反
class son8 : public base5, public base6
{
public:
	son8()
	{
		cout << "子类son8---无参构造函数" << endl;
		m_a = 100;
		m_b = 56l;
	}
	~son8()
	{
		cout << "子类son8---无参析构函数" << endl;
	}
	//当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数 
	//如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域
	void func()
	{
		cout << "子类son8---func()函数调用" << endl;
	}
public:
	int m_a;
	int m_b;
};

void inherit_samename_more(void)
{
	son8 s;
	cout << "子类son8的m_a:" << s.m_a << endl;
	cout << "父类base5的m_a:" << s.base5::m_a << endl;
	cout << "父类base6的m_a:" << s.base6::m_a << endl;
	s.func();
	s.base5::func();
	s.base5::func(4545);
	s.base6::func();
	s.base6::func(78);
}

在这里插入图片描述
总结: 多继承中如果父类中出现了同名情况,子类使用时候要加作用域

4.6.8、菱形继承

菱形继承概念:

  • 两个派生类继承同一个基类
  • 又有某个类同时继承者两个派生类
  • 这种继承被称为菱形继承,或者钻石继承
    在这里插入图片描述
    菱形继承问题:
  1. 羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
  2. 草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。

本例程使用了:virtual关键字

#include <iostream>

using namespace std;

//动物
class animal
{
public:
	animal()
	{
		cout << "父类animal---无参构造函数" << endl;
		m_age = 345;
	}
	~animal()
	{
		cout << "父类animal---无参析构函数" << endl;
	}
	void func()
	{
		cout << "父类animal---func()函数调用" << endl;
	}
	void func(int a)
	{
		cout << "父类animal---func(int a)函数调用" << endl;

	}
public:
	int m_age;
};
//继承前加virtual关键字后,变为虚继承 
//此时公共的父类Animal称为虚基类
class tuo : virtual public animal
{
public:
	tuo()
	{
		cout << "父类tuo---无参构造函数" << endl;
		m_age = 234;
	}
	~tuo()
	{
		cout << "父类tuo---无参析构函数" << endl;
	}
	void func()
	{
		cout << "父类tuo---func()函数调用" << endl;
	}
	void func(int a)
	{
		cout << "父类tuo---func(int a)函数调用" << endl;

	}
public:
	int m_age;
};
//继承前加virtual关键字后,变为虚继承 
//此时公共的父类Animal称为虚基类
class sheep : virtual public animal
{
public:
	sheep()
	{
		cout << "父类sheep---无参构造函数" << endl;
		m_age = 234;
	}
	~sheep()
	{
		cout << "父类sheep---无参析构函数" << endl;
	}
	void func()
	{
		cout << "父类sheep---func()函数调用" << endl;
	}
	void func(int a)
	{
		cout << "父类sheep---func(int a)函数调用" << endl;

	}
public:
	int m_age;
};
class sheeptuo : public sheep, public tuo
{
public:
	sheeptuo()
	{
		cout << "子类sheeptuo---无参构造函数" << endl;
		m_age = 100;
	}
	~sheeptuo()
	{
		cout << "子类sheeptuo---无参析构函数" << endl;
	}
	//当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数 
	//如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域
	void func()
	{
		cout << "子类sheeptuo---func()函数调用" << endl;
	}
public:
	int m_age;
};
void diamond_inheritance(void)
{
	sheeptuo s;
	s.m_age = 85;
	s.sheep::m_age = 13;
	s.tuo::m_age = 38;
	s.sheep::animal::m_age = 67;
	s.tuo::animal::m_age = 47;

	cout << "子类---s.m_age:" << s.m_age << endl;
	cout << "小父类---s.sheep::m_age:" << s.sheep::m_age << endl;
	cout << "小父类---s.tuo::m_age:" << s.tuo::m_age << endl;
	
	cout << "总父类--动物--s.sheep::animal::m_age:" << s.sheep::animal::m_age << endl;
	cout << "总父类--动物--s.tuo::animal::m_age:" << s.tuo::animal::m_age << endl;
	s.func();
	s.sheep::func();
	s.sheep::func(56);
	s.tuo::func();
	s.tuo::func(78);

	s.sheep::animal::func();
	s.sheep::animal::func(497);
	s.tuo::animal::func();
	s.tuo::animal::func(245);

}

在这里插入图片描述

没有使用:virtual关键字的结果是
在这里插入图片描述
总结:

  • 菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义
  • 利用虚继承virtual可以解决菱形继承问题

4.7、多态

4.7.1、多态的基本语法

多态是C++面向对象三大特性之一

多态分为两类

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

静态多态和动态多态区别:

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

下面通过案例进行讲解多态

#include <iostream>
using namespace std;
class animal1
{
public:
	//Speak函数就是虚函数 
	//函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。

	//如果没有使用virtual关键字,本节内容打印的就是:
	//动物在说话
	//动物在说话
	virtual void speak()
	{
		cout << "动物正在说话" << endl;
	}
};
class cat : public animal1
{
public: 
	void speak()
	{
		cout << "猫正在说话" << endl;
	}
};
class dog : public animal1
{
public:
	void speak()
	{
		cout << "狗正在说话" << endl;
	}
};
//我们希望传入什么对象,那么就调用什么对象的函数 
//如果函数地址在编译阶段就能确定,那么静态联编:父类中--speak--没有添加--virtual--就是静态的
//如果函数地址在运行阶段才能确定,就是动态联编:父类中--speak--添加--virtual--就是动态的
void dospeak(animal1 &a)
{
	a.speak();
}
//多态满足条件: 
//1、有继承关系 
//2、子类重写父类中的虚函数 
//多态使用: 
//父类指针或引用指向子类对象
void polymorphic_base(void)
{
	cat c;
	dospeak(c);

	dog d;
	dospeak(d);
}

在这里插入图片描述

总结:
多态满足条件

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

多态使用条件

  • 父类指针或引用指向子类对象

重写:函数返回值类型 函数名 参数列表 完全一致称为重写

4.7.2、计算机小项目

案例描述:分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类

多态的优点:

  • 代码组织结构清晰
  • 可读性强
  • 利于前期和后期的扩展以及维护
#include <iostream>
using namespace std;

//父类

//多态实现 
//抽象计算器类
//多态优点:代码组织结构清晰,可读性强,利于前期和后期的扩展以及维护
class calculator
{
public:
	virtual int result()
	{
		return 0;
	}
public:
	int num1;
	int num2;
};

class addcalculator : public calculator
{
public:
	int result()
	{
		return num1 + num2;
	}
};
class subcalculator : public calculator
{
public:
	int result()
	{
		return num1 - num2;
	}
};
class mulcalculator : public calculator
{
public:
	int result()
	{
		return num1 * num2;
	}
};

class divcalculator : public calculator
{
public:
	int result()
	{
		return num1 / num2;
	}
};

void polymorphic_project1(void)
{
	//加法
	calculator* abc = new addcalculator;
	abc->num1 = 34;
	abc->num2 = 78;
	cout << abc->num1 << "+" << abc->num2 << "=" << abc->result() << endl;
	delete abc;

	//减法
	abc = new subcalculator;
	abc->num1 = 34;
	abc->num2 = 78;
	cout << abc->num1 << "-" << abc->num2 << "=" << abc->result() << endl;
	delete abc;

	//乘法
	abc = new mulcalculator;
	abc->num1 = 24;
	abc->num2 = 5;
	cout << abc->num1 << "*" << abc->num2 << "=" << abc->result() << endl;
	delete abc;

	//除法
	abc = new divcalculator;
	abc->num1 = 100;
	abc->num2 = 23;
	cout << abc->num1 << "/" << abc->num2 << "=" << abc->result() << endl;
	delete abc;
}

在这里插入图片描述
总结:C++开发提倡利用多态设计程序架构,因为多态优点很多

4.7.3、纯虚函数和抽象类

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

抽象类特点:

  • 无法实例化对象
  • 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
#include <iostream>
using namespace std;


/*纯虚函数的写法:virtual 数据类型 函数名 = 0;*/
class base7
{
public:
	//纯虚函数 
	//类中只要有一个纯虚函数就称为抽象类 
	//抽象类无法实例化对象 
	//子类必须重写父类中的纯虚函数,否则也属于抽象类
	virtual void fun() = 0;
public:
	int num;
};

class son9 :public base7
{
public:
	void fun()
	{
		cout << "纯虚函数--抽象类--bsae7--son9--func函数调用" << endl;
	}
};
void Purevirtual_func(void)
{
	base7 *s = NULL;
	//base = new Base; // 错误,抽象类无法实例化对象
	s = new son9;
	s->fun();
	delete s;
}

在这里插入图片描述

4.7.4、多态案例二-制作饮品

案例描述:制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料
利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶

#include <iostream>

using namespace std;

//制作流程
class makingprocess
{
public:
	//烧水
	virtual void boil() = 0;
	//冲泡咖啡
	virtual void brew() = 0;
	//倒入杯中
	virtual void pour() = 0;
	//加入辅料
	virtual void addaccessories() = 0;
	
	void allprocess()
	{
		boil();
		brew();
		pour();
		addaccessories();
	}	
};
class makecoffee : public makingprocess
{
public:
	void boil()
	{
		cout << "烧水" << endl;
	}
	void brew()
	{
		cout << "冲泡" << endl;
	}
	void pour()
	{
		cout << "倒入" << endl;
	}
	void addaccessories()
	{
		cout << "加入牛奶" << endl;
	}
};
class maketea : public makingprocess
{
public:
	void boil()
	{
		cout << "烧水" << endl;
	}
	void brew()
	{
		cout << "冲泡" << endl;
	}
	void pour()
	{
		cout << "倒入" << endl;
	}
	void addaccessories()
	{
		cout << "加入柠檬" << endl;
	}
};

void makingdrink(void)
{
	makingprocess* drink = new makecoffee;
	drink->allprocess();
	delete drink;
	cout << "-----------------------------" << endl;
	drink = new maketea;
	drink->allprocess();
	delete drink;
}

在这里插入图片描述

4.7.5、虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或者纯虚析构

虚析构和纯虚析构共性:

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

虚析构和纯虚析构区别:

  • 如果是纯虚析构,该类属于抽象类,无法实例化对象

虚析构语法:virtual ~类名(){}
纯虚析构语法:virtual ~类名() = 0;
初始化:类名::~类名(){}

#include <iostream>
using namespace std;


class animal2
{
public:
	animal2()
	{
		cout << "animal2--无参构造函数调用" << endl;
	}
	virtual ~animal2() = 0;

	virtual void speak() = 0;
	//析构函数加上virtual关键字,变成虚析构函数
	/*virtual ~animal2() 
	{ 
		cout << "animal2虚析构函数调用!" << endl; 
	}*/

};

animal2::~animal2()
{
	cout << "animal2--纯虚析构函数调用" << endl;
}
//和包含普通纯虚函数的类一样,包含了纯虚析构函数的类也是一个抽象类。不能够被实例化。
class cow :public animal2
{
public:
	cow(string name)
	{
		cout << "cow--有参构造函数" << endl;
		this->name = new string(name);
	}
	~cow()
	{
		cout << "cow--无参析构函数" << endl;
		if (name != NULL)
		{
			delete name;
			name = NULL;
		}
	}
	
	void speak()
	{
		cout << *name << "正在说话" << endl;
	}

public:
	string* name;
};

void purevirtual_destructor(void)
{
	animal2 *a = NULL;
	a = new cow("王二毛");
	a->speak();

	//通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏 
	//怎么解决?给基类增加一个虚析构函数 
	//虚析构函数就是用来解决通过父类指针释放子类对象
	delete a;
}

在这里插入图片描述
总结:

  1. 虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
  2. 如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
  3. 拥有纯虚析构函数的类也属于抽象类
4.7.6、小项目-电脑组装

案例描述:
电脑主要组成部件为 CPU(用于计算),显卡(用于显示),内存条(用于存储)
将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商
创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口
测试时组装三台不同的电脑进行工作

#include <iostream>
using namespace std;

//cpu 显卡 内存
class cpu
{
public:
	virtual void calculator() = 0;
};
class card
{
public:
	virtual void display() = 0;
};
class memory
{
public:
	virtual void storage() = 0;
};

//继承--多态--实现
class intercpu :public cpu
{
public:
	void calculator()
	{
		cout << "inter的CPU正在计算" << endl;
	}
};
class intercard :public card
{
public:
	void display()
	{
		cout << "inter的显卡正在显示图像" << endl;
	}
};
class intermemory :public memory
{
public:
	void storage()
	{
		cout << "inter的内存正在存储文件" << endl;
	}
};

class amdcpu :public cpu
{
public:
	void calculator()
	{
		cout << "amd的CPU正在计算" << endl;
	}
};
class amdcard :public card
{
public:
	void display()
	{
		cout << "amd的显卡正在显示图像" << endl;
	}
};
class amdmemory :public memory
{
public:
	void storage()
	{
		cout << "amd的内存正在存储文件" << endl;
	}
};

//电脑主体
class computer
{
public:
	computer(cpu *cpuname, card *cardname, memory *memoryname)
	{
		cout << "computer--有参构造函数" << endl;
		this->cpuname = cpuname;
		this->cardname = cardname;
		this->memoryname = memoryname;
	}
	~computer()
	{
		cout << "computer--析构函数" << endl;
		if (cpuname != NULL)
		{
			delete cpuname;
			cpuname = NULL;
		}
		if (cardname != NULL)
		{
			delete cardname;
			cardname = NULL;
		}
		if (memoryname != NULL)
		{
			delete memoryname;
			memoryname = NULL;
		}
	}
	void computerwork()
	{
		cpuname->calculator();
		cardname->display();
		memoryname->storage();
	}

private:
	cpu *cpuname;
	card* cardname;
	memory *memoryname;
};


void computer_package(void)
{
	computer *com = NULL;
	//第一台电脑
	cpu *cpu1 = new intercpu;
	card *card1 = new intercard;
	memory *memory1 = new intermemory;
	com = new computer(cpu1, card1, memory1);
	com->computerwork();
	delete com;

	cout << "----------------------------------------" << endl;
	//第二台电脑
	cpu* cpu2 = new intercpu;
	card* card2 = new amdcard;
	memory* memory2 = new amdmemory;
	com = new computer(cpu2, card2, memory2);
	com->computerwork();
	delete com;
}

5、文件

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

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

操作文件的三大类:

  1. ofstream:写操作
  2. ifstream: 读操作
  3. fstream : 读写操作

5.1、文本文件

写文件步骤如下:

  1. 包含头文件
    #include <fstream>
  2. 创建流对象
    ofstream ofs;
  3. 打开文件
    ofs.open(“文件路径”,打开方式);
  4. 写数据
    ofs << “写入的数据”;
  5. 关闭文件
    ofs.close();

在这里插入图片描述
读文件与写文件步骤相似,但是读取方式相对于比较多
读文件步骤如下:

  1. 包含头文件
    #include <fstream>
  2. 创建流对象
    ifstream ifs;
  3. 打开文件并判断文件是否打开成功
    ifs.open(“文件路径”,打开方式);
  4. 读数据
    四种方式读取
  5. 关闭文件
    ifs.close();
#include<iostream>
#include<fstream>
#include <string>
using namespace std;

void files_write(void)
{
	//创建流对象
	ofstream ofs;
	ofs.open("filestest01.txt", ios::out);
	ofs << "大家好!我是王二毛!"<<endl;
	ofs.close();
}

void files_read(void)
{
	//创建流对象
	ifstream ifs;
	ifs.open("filestest01.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)
	{
		cout << c;
	}
	ifs.close();
}

在这里插入图片描述

在这里插入图片描述

5.2、二进制文件

以二进制的方式对文件进行读写操作
打开方式要指定为 ios::binary

写文件

二进制方式写文件主要利用流对象调用成员函数write
函数原型 : ostream& write(const char * buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数

读文件

二进制方式读文件主要利用流对象调用成员函数read
函数原型: istream& read(char *buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数

#include<iostream>
#include<fstream>


using namespace std;

class person15
{
public:
	char m_name[64];
	int m_age;
};
void write_binary(void)
{
	//1、包含头文件

	//2、创建输出流对象
	ofstream ofs("person.txt", ios::out | ios::binary);
	//3、打开文件
	//ofs.open("person.txt", ios::out | ios::binary);

	person15 p = { "王二毛",456 };

	//4、写文件
	ofs.write((const char *)&p,sizeof(p));

	//5、关闭文件 
	ofs.close();
}

void read_binary(void)
{
	//1、包含头文件

	//2、创建输出流对象
	ifstream ifs("person.txt", ios::in | ios::binary);

	//3、打开文件
	//ofs.open("person.txt", ios::out | ios::binary);
	if (!ifs.is_open()) 
	{ 
		cout << "文件打开失败" << endl; 
	}

	person15 p1;

	//4、写文件
	ifs.read((char*)&p1, sizeof(p1));

	cout << "姓名: " << p1.m_name << " 年龄: " << p1.m_age << endl;
	//5、关闭文件 
	ifs.close();
}

在这里插入图片描述

总结:

  • 文件输出流对象 可以通过write函数,以二进制方式写数据
  • 文件输入流对象 可以通过read函数,以二进制方式读数据
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值