C++进阶(B站黑马程序员)

文章目录

  • 类和对象
  • 访问权限
  • struct和class的区别
  • 成员属性设置为私有
  • 立方体类
  • 点和圆的关系
  • 构造函数和析构函数
    • 构造函数的分类以及调用
    • 拷贝构造函数的调用时机
    • 构造函数调用规则
  • 深拷贝 浅拷贝
  • 初始化列表
  • 类成员做类对象
  • 静态成员
  • this指针
  • 加号 运算符重载
  • 二、左移<< 运算符重载
  • 递增运算符重载
  • 重载赋值运算符
  • 重载关系运算符
  • 重载关系函数调用运算符
  • 继承
  • 继承中构造和析构顺序
  • 继承同名成员处理方式
  • 多继承语法
  • 菱形继承
  • 多态
  • 纯虚函数和抽象类
  • 制作饮品
  • 虚析构和纯虚析构
  • 组装电脑
  • 文件操作
  • 写文件
  • 读文件
  • 二进制文件
  • 写文件
  • 二进制读文件


类和对象

#include
using namespace std;
//C++用类求阶乘
class fact
{
private:
	int n;//n是要求的阶乘 控制循环次数
	int ret;//ret存放结果
public:
	void assign(int x,int y)
	{
		n=x;
		ret=y;
	}
	int fac()
	{
		int i;
		for(i=1;i<=n;i++)
		{
			ret*=i;
		}
		return ret;
	}
};
int main()
{
	 fact f;
	 f.assign(3,1);
	 //int k=f.fac();
	 //cout<
	 cout<<f.fac()<<endl;
	 return 0;
}

#include
using namespace std;
//设计一个圆类,求圆的周长
//圆求周长的公式:2*pi*半径
class fact
{
private:
	int n;//n是要求的阶乘 控制循环次数
	int ret;//ret存放结果
public:
	void assign(int x,int y);

	int fac();
	
};
void fact::assign(int x,int y)
{
	n=x;
	ret=y;
}
int fact::fac()
{
	int i;
	for(i=1;i<=n;i++)
	{
		ret*=i;
	}
	return ret;
}
int main()
{
	 fact f;
	 f.assign(3,1);
	 //int k=f.fac();
	 //cout<
	 cout<<f.fac()<<endl;
	 return 0;
}

#include
using namespace std;
#define PI 3.14
//设计一个圆类,求圆的周长
//圆求周长的公式:2*pi*半径
class Circle//类名
{
	//访问权限
private:
	int m_r;//成员变量 半径
	
public:
	//行为 给圆的半径赋值
	void assign(int r);
	//获取圆的半径
	int get();
	//获取圆的周长
	double calculateZC();
	
};
void Circle::assign(int r)
{
	m_r=r;
}
int Circle::get()
{
	return m_r;
}
double Circle::calculateZC()
{
	return 2*PI*m_r;
}
int main()
{
	//通过圆类 创建具体的圆的对象 
	 Circle c;
	 //调用成员函数assign给圆的半径赋值
	 c.assign(10);
	 //调用成员函数get获取圆的半径
	 cout<<"圆的半径是"<<c.get()<<endl;
	 //调用成员函数calculateZC获取圆的周长
	  cout<<"圆的周长是"<<c.calculateZC()<<endl;
	 return 0;
}
//设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号
#include
#include
using namespace std;
class Student//类名
{
	//访问权限
private:
	string m_name;//成员变量
	int m_id;

	
public:
	//行为 给姓名和学号赋值
	void assign(string name,int id);
	//显示学生的姓名和学号
	void show();
	
	
};
void Student::assign(string name,int id)
{
	m_name=name;
	m_id=id;
}
void Student::show()
{
	cout<<"学生的姓名是"<<m_name<<"学号是"<<m_id<<endl;
}

int main()
{
	//通过学生类 创建具体的学生的对象 
	 Student s;
	 //调用成员函数assign给学生的姓名和学号赋值
	 s.assign("aa",10);
	 //调用成员函数show显示学生的姓名和学号
	 
	 s.show();
	 return 0;
}
//设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号
#include
#include
using namespace std;
class Student//类名
{
	//访问权限
private:
	string m_name;//成员变量
	int m_id;

	
public:
	//行为 给姓名和学号赋值
	void assign(string name,int id);
	//显示学生的姓名和学号
	void show();
	
	
};
void Student::assign(string name,int id)
{
	m_name=name;
	m_id=id;
}
void Student::show()
{
	cout<<"学生的姓名是"<<m_name<<"学号是"<<m_id<<endl;
}
int main()
{
	//通过学生类 创建具体的学生的对象 
	 Student s;
	 string s1;
	 int i;
	 cin>>s1>>i;
	 //调用成员函数assign给学生的姓名和学号赋值
	 s.assign(s1,i);
	 //调用成员函数show显示学生的姓名和学号
	 
	 s.show();
	 return 0;
}

访问权限

#include
#include
using namespace std;
class Person 
{
public:
	//公共权限
	string m_name;
protected:
	//保护权限
	string m_car;
private:
	//私有权限
	int m_password;
public:
	//在类内不管何种访问权限都是可以直接访问的
	void fun()
	{
		m_name="小王";
		m_car="benchi";
		m_password=123;
	}
};		
int main()
{
	//实例化具体对象
	Person p;
	p.m_name="小李";
	//p.m_car="奔驰";//保护权限内容 在类外访问不到
	//p.m_password=123456;//私有权限内容 在类外访问不到
	return 0;
}

struct和class的区别

struct默认权限是公共 public
class默认权限是公共 private

成员属性设置为私有

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

#include
#include
using namespace std;
class Person 
{
private:
	//姓名 可读可写
	string m_name;
	//年龄 只读
	int m_age;
	//学号 只写
	int m_id;
public:
	//写姓名
	void setName(string name)
	{
		m_name=name;
	}
	//读姓名
	string getName()
	{
		return m_name;
	}
	//写年龄
	void setAge(int age)
	{
		if(age<0||age>150)
		{
			m_age=0;
			cout<<"年龄有误"<<endl;
			return ;
		}
		m_age=age;
	}
	//读年龄
	int getAge()
	{
		return m_age;
	}
	//写学号
	void setId(int id)
	{
		m_id=id;
	}
};		

int main()
{
	//实例化具体对象
	Person p;
	p.setName("小李");
	cout<<"姓名:"<<p.getName()<<endl;
	p.setAge(18);
	cout<<"年龄:"<<p.getAge()<<endl;
	//设置id 但不能得到id
	p.setId(20);
	return 0;
}

立方体类

1.创建立方体类
2.设计属性
3.设计行为 获取立方体面积和体积
4.分别利用全局函数和成员函数是否相等

#include
using namespace std;
class Cube
{
private:
	double m_L;
	double m_H;
	double m_W;
public:
	void Set(double l, double h, double w)
	{
		m_L = l;
		m_H = h;
		m_W = w;
	}
	double getL()
	{
		return m_L;
	}
	double getH()
	{
		return m_H;
	}
	double getW()
	{
		return m_W;
	}
	double Volume()
	{
		return m_L * m_H * m_W;
	}
	double Area()
	{
		return 2 * m_L * m_H + 2 * m_L * m_W + 2 * m_W * m_H;
	}
	//成员函数判断是否相等
	bool isSameByClass(Cube& c)
	{
		if (m_L == c.getL() && m_H == c.getH() && m_W == c.getW())
		{
			return 1;
		}
		else
			return 0;
	}
};
//全局函数判断是否相等
bool isSame(Cube& c1, Cube& c2)
{
	if (c1.getL() == c2.getL() && c1.getH() == c2.getH() && c1.getW() == c2.getW())
	{
		return 1;
	}
	else
		return 0;
}
int main()
{
	Cube c1;
	c1.Set(10, 10, 10);
	cout << "c1的面积" << c1.Area() << endl;
	cout << "c1的体积" << c1.Volume() << endl;
	Cube c2;
	c2.Set(10, 10, 11);
	bool ret = isSame(c1, c2);
	if (ret)
	{
		cout << "全局函数判断相等" << endl;
	}
	else
		cout << "全局函数判断不相等" << endl;
	bool ret2 = c1.isSameByClass(c2);
	if (ret2)
	{
		cout << "成员函数判断相等" << endl;
	}
	else
		cout << "成员函数判断不相等" << endl;
	return 0;
}

点和圆的关系

#include
using namespace std;
//练习案例
//设计一个圆形类,和一个点类,计算点和圆的关系
class Point
{
private:
	int m_X;
	int m_Y;
public:
	void setX(int x)
	{
		m_X = x;
	}
	void setY( int y)
	{
		m_Y = y;
	}
	int getX()
	{
		return m_X;
	}
	int getY()
	{
		return m_Y;
	}

};

class Circle
{
private:
	int m_R;
	Point m_Center;
public:
	void setR(int r)
	{
		m_R = r;
	}
	int getR()
	{
		return m_R;
	}
	void setCenter(Point Center)
	{
		m_Center = Center;
	}
	Point getCenter()
	{
		return m_Center;
	}

};
//判断点和圆的关系
void isInCircle(Circle& c, Point& p)
{
	int distance = (c.getCenter().getX() - p.getX()) * (c.getCenter().getX() - p.getX()) +
		(c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY());
	int rDistance = c.getR() * c.getR();
	if (distance == rDistance)
	{
		cout << "点在圆上" << endl;
	}
	else if (distance > rDistance)
	{
		cout << "点在圆外" << endl;
	}
	else
	{
		cout << "点在圆内" << endl;
	}
}
int main()
{
	//创建圆
	Circle c;
	c.setR(10);
	Point center;
	center.setX(10);
	center.setY(0);
	c.setCenter(center);
	//创建点
	Point p;
	p.setX(10);
	p.setY(9);
	isInCircle(c, p);
	return 0;
}

构造函数和析构函数

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

#include
using namespace std;
class Person
{
public:
	Person();//构造函数
	~Person();//析构函数

private:

};

Person::Person()
{
	cout << "Person的构造函数调用" << endl;
}

Person::~Person()
{
	cout << "Person的析构函数调用" << endl;
}
int main()
{
	Person p;
	return 0;
}

构造函数的分类以及调用

两种分类方式:
按参数分类:有参构造和无参构造(默认构造)
按类型分类:普通构造和拷贝构造
三种调用方式
括号法
显示法
隐式转换法

#include
using namespace std;
class Person
{
public:
	Person();//无参构造函数
	Person(int a);//有参构造函数
	Person(const Person &p);//拷贝构造函数
	~Person();//析构函数
	

	int age;
};

Person::Person()
{
	cout << "Person的无参构造函数调用" << endl;
}
Person::Person(int a)
{
	age=a;
	cout << "Person的有参构造函数调用" << endl;
}
//拷贝构造函数
Person::Person(const Person &p)
{
	age=p.age;
	cout << "Person的拷贝构造函数调用" << endl;
}
Person::~Person()
{
	cout << "Person的析构函数调用" << endl;
}
int main()
{
	//调用构造函数的方法
	//括号法
	Person p1;//无参构造函数调用
	Person p2(10);//有参构造函数调用
	Person p3(p2);//拷贝构造函数调用

	cout<<"p2的年龄为"<<p2.age<<endl;
	cout<<"p3的年龄为"<<p3.age<<endl;
	//显示法
	Person p1;//无参构造函数调用
	Person p2 =	Person(10);//有参构造函数调用
	Person p3 =	Person(p2);//拷贝构造函数调用
	//隐式转换法
	Person p4=10;//==Person p4=	Person(10);
	Person p5=P4;//拷贝构造函数
	return 0;
}

拷贝构造函数的调用时机

三种情况:
使用一个已经1创建完毕的对象来初始化一个新对象
值传递的方式给函数参数赋值
值方式返回局部对象

#include
using namespace std;
class Person
{
public:
	Person();//构造函数
	~Person();//析构函数
	Person(int age);//有参构造函数
	Person(const Person &p);//拷贝构造函数
private:
	int m_age;
};

Person::Person()
{
	cout << "Person的构造函数调用" << endl;
}

Person::Person(int age)
{
	cout << "Person的有参构造函数调用" << endl;
	m_age=age;
}

Person::Person(const Person &p)
{
	cout << "Person的拷贝构造函数调用" << endl;
	m_age=p.m_age;
}
Person::~Person()
{
	cout << "Person的析构函数调用" << endl;
}
void dowork(Person p)
{
	
}
Person dowork2()
{
	Person p1;
	return p1;
}
int main()
{
	Person p;
	Person p1(10);
	Person p2(p1);


	//值传递的方式给函数参数赋值
	Person p3;
	dowork(p3);
	//值方式返回局部对象
	Person p4=dowork2();
	return 0;
}


构造函数调用规则

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

构造函数调用规则如下:
如果用户定义有参构造函数,c++不再提供默认无参构造,但是会提供默认拷贝构造
如果用户定义拷贝构造函数,c++不会在提供其他构造函数

深拷贝 浅拷贝

浅拷贝:编译器提供的默认拷贝构造函数
深拷贝:在堆区重新申请空间,进行拷贝工作

#include
using namespace std;
class Person
{
public:
	Person();//构造函数
	~Person();//析构函数

	Person(int age,int height);//有参构造函数
	Person(const Person &p);//拷贝构造函数


	int m_age;
	int* m_height;
};	


Person::Person(int age,int height)
{
	cout << "Person的有参构造函数调用" << endl;
	m_age=age;
	m_height=new int(height);
}
//自己实现拷贝构造函数,解决浅拷贝带来的问题
Person::Person(const Person &p)
{
	cout << "Person的拷贝构造函数调用" << endl;
	m_age=p.m_age;
	//height=p.m_height这是编译器实现的默认拷贝构造函数
	//深拷贝操作
	m_height=new int(*p.m_height);
}

Person::~Person()
{
	//析构代码,将堆区开辟数据做释放操作
	if(m_height!=NULL)
	{
		delete m_height;
	}
	cout << "Person的析构函数调用" << endl;
}

int main()
{
	Person p1(10,160); 
	cout<<"p1 "<<p1.m_age<<" "<<*p1.m_height<<endl;
	Person p2(p1);
	//浅拷贝都带来的问题就是对取得内存重复释放
	//浅拷贝的问题要利用深拷贝来解决
	//如果利用编译器提供的拷贝构造函数,会做浅拷贝操作
	cout<<"p2 "<<p2.m_age<<" "<<*p2.m_height<<endl;
	return 0;
}



初始化列表

初始化列表用于给类中的私有成员变量初始化
语法:构造函数():属性1(值1),属性2(值2)…{}

#include
#include
using namespace std;
class Person
{
public:

	//传统初始化工作
	//Person(int age,int height,string name);//有参构造函数
	//初始化列表初始化属性
	Person(int age,int height,string name);
	~Person();//析构函数
	int m_age;
	int m_height;
	string m_name;
};	

//1.赋值形式
/*Person::Person(int age,int height,string name)
{
	m_age=age;
	m_height=height;
	m_name=name;
}*/
//2.初始化列表初始化属性
Person::Person(int age,int height,string name):m_age(age),m_height(height), m_name(name)
{
}
Person::~Person()
{
}

int main()
{
	Person p1(10,160,"wk"); 

	cout<<p1.m_age<<" "<<p1.m_height<<" "<<p1.m_name<<endl;
	return 0;
}

类成员做类对象

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

class A {};
class B
{
	A a;
};

B类中有对象A作为成员,A为对象成员

#include
#include
using namespace std;
class Phone
{
public:
	Phone(string pName)
	{
		m_pName=pName;
		cout<<"Phone构造函数"<<endl;
	}
	string m_pName;
};
class Person
{
public:
	Person(string name,string phone):m_name(name),m_phone(phone)
	{
		cout<<"Person构造函数"<<endl;
	}
	string m_name;
	Phone m_phone;
};	

//当其他类对象最为本类成员,构造时先构造类对象
//在构造自身。析构与构造相反
int main()
{
	//Phone p1();
	Person p("wk","苹果"); 
	
	cout<<p.m_name<<"拿着 "<<p.m_phone.m_pName<<endl;
	return 0;
}

静态成员

静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员。
静态成员分为:
静态成员变量
所有对象共享同一份数据
在编译阶段分配内存
类内声明,类外初始化
静态成员函数
所有对象共享同一个函数
静态成员函数只能访问静态成员变量

#include
using namespace std;
class Person
{
public:
	//1.所有对象都共享同一份数据
	//2.编译阶段就分配内存
	//3.类内声明,类外初始化操作
	static int m_a;
	
	//静态成员变量也是有访问权限的
private:
	static int m_b;

};
int Person::m_a=100;
int Person::m_b=200;
int main()
{
	Person p;
	cout<<p.m_a<<endl;
	Person p2;
	p2.m_a=200;
	cout<<p.m_a<<endl;
	//静态成员变量 不属于某个对象上,所有对象共享同一份数据
	//因此静态成员变量有两种访问方式
	
	//1.通过对象进行访问
	Person p3;
	cout<<p3.m_a<<endl;
	//2.通过类名进行访问
	cout<<Person::m_a<<endl;
	//cout<
	return 0;
}
#include
using namespace std;
class Person
{
public:
	//1.所有对象都共享同一个函数
	//2.静态成员函数只能访问静态成员变量
	static void func()
	{
		cout<<"static void func调用"<<endl;
	} 
	static int m_a;//静态成员变量
private:
	static void func2()
	{
		cout<<"static void func2调用"<<endl;
	} 
};

int main()
{
	//1.通过对象进行访问
	Person p;
	p.func();
	//2.通过类名进行访问
	Person::func();
	//Person::func2();私有权限访问不到
	return 0;
}

this指针

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

#include
using namespace std;
class Person
{
public:
	int m_a;//非静态的成员变量 属于类的对象上
	static int m_b;//非静态成员变量 不属于类对象上
	void func(){}//非静态成员函数 不属于类的对象上
	static void func2(){}//静态成员函数 不属于类的对象上
};
int Person::m_b=0;
int main()
{
	Person p;
	//空对象占用内存空间为1个字节
	cout<<"sizeof p="<<sizeof(p)<<endl;
	return 0;
}

构造函数的目的是给类的私有成员变量赋值
涉及到this指针 this相当于是一个隐式的指针
指向这个类的成员变量 此时成员函数的形参可以和类的私有成员变量的名称相同
每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
这一块代码是如何区分哪个对象调用自己的呢?
c++提供特殊的对象指针:this指针,this指针指向被调用的成员函数所属的对象。
this指针的用于:1.解决名称冲突2.返回对象本身用*this

#include
using namespace std;
class Person
{
public:
	int age;
//1.解决名称冲突
	Person(int age)
	{
		//this指针指向被调用的成员函数所属的对象
		this->age=age;
	}
	Person& PersonAddAge(Person &p)
	{
		this->age+=p.age;
		return *this;
	}
};

//2.返回对象本身用*this
int main()
{
	Person p1(18);
	Person p2(18);

	cout<<p1.age<<endl;
	p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
	cout<<p2.age<<endl;
	return 0;
}

加号 运算符重载

运算符重载概念:对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。
括号法
转换法
通过成员函数重载+号

#include
using namespace std;
class Person
{
public:
	int m_a;
	int m_b;
	Person()
	{}
	Person(int a,int b)
	{
		this->m_a=a;
		this->m_b=b;
	}
	Person operator+(Person& p)
	{
		Person temp;
		temp.m_a=this->m_a+p.m_a;
		temp.m_b=this->m_b+p.m_b;
		return temp;
	}
};
int main()
{
	Person p1(10,20);
	Person p2(10,20);
	//正常调用情况
	Person p3=p1.operator +(p2);
	//简化形式
	//Person p3=p1+(p2);
	cout<<p3.m_a<<" "<<p3.m_b<<endl;
	return 0;

}

通过全局函数重载+号

#include
using namespace std;
class Person
{
public:
	int m_a;
	int m_b;
	Person()
	{}
	Person(int a,int b)
	{
		this->m_a=a;
		this->m_b=b;
	}
	
};
Person operator+(Person& p1,Person& p2)
{
	Person temp;
	temp.m_a=p1.m_a+p2.m_a;
	temp.m_b=p1.m_b+p2.m_b;
	return temp;
}
int main()
{
	Person p1(10,20);
	Person p2(10,20);
	//正常调用情况
	//Person p3=operator+(p1,p2);
	//简化形式
	Person p3=p1+(p2);
	cout<<p3.m_a<<" "<<p3.m_b<<endl;
	return 0;

}

运算符重载,也可以发生函数重载

#include
using namespace std;
class Person
{
public:
	int m_a;
	int m_b;
	Person()
	{}
	Person(int a,int b)
	{
		this->m_a=a;
		this->m_b=b;
	}

	
};
Person operator+(Person& p1,int num)
{
	Person temp;
	temp.m_a=p1.m_a+num;
	temp.m_b=p1.m_b+num;
	return temp;
}
int main()
{
	Person p1(10,20);
	Person p2(10,20);
	//正常调用情况
	//Person p3=operator+(p1,100);
	//简化形式
	Person p3=p1+100;//Person+int 
	cout<<p3.m_a<<" "<<p3.m_b<<endl;
	return 0;

}
#include
using namespace std;
class Person
{
public:
	int m_a;
	int m_b;
	Person()
	{}
	Person(int a,int b)
	{
		this->m_a=a;
		this->m_b=b;
	}
	Person operator+(int num)
	{
		Person temp;
		temp.m_a=m_a+num;
		temp.m_b=m_b+num;
		return temp;
	}

	
};

int main()
{
	Person p1(10,20);
	Person p2(10,20);
	//正常调用情况
	//Person p3=p1.operator+(100);
	//简化形式
	Person p3=p1+100;//Person+int 
	cout<<p3.m_a<<" "<<p3.m_b<<endl;
	return 0;

}

#include
using namespace std;
class Person
{
public:
    /*Person operator+(Person &p);*/
    int m_a;
    int m_b;
private:

};
//全局函数重载+号
//Person operator+(Person& p1, Person& p2)
//{
//    Person temp;
//    temp.m_a = p1.m_a + p2.m_a;
//    temp.m_b = p1.m_b + p2.m_b;
//    return temp;
//}
Person operator+(Person& p1, int num)
{
    Person temp;
    temp.m_a = p1.m_a + num;
    temp.m_b = p1.m_b + num;
    return temp;
}


//通过成员函数重载
//Person Person::operator+(Person& p)
//{
//    Person temp;
//    temp.m_a = this->m_a + p.m_a;
//    temp.m_b = this->m_b + p.m_b;
//    return temp;
//}


int main()
{
    Person p1;
    p1.m_a = 10;
    p1.m_b = 20;
    Person p2;
    p2.m_a = 10;
    p2.m_b = 20;
    //Person p3 = p1 + p2;//Person p3=p1.operator+(p2)
    //cout << p3.m_a << endl;
    //cout << p3.m_b << endl;
    //通过成员函数重载+号本质
    //Person p3=p1.operator+(p2)==Person p3 = p1 + p2;


    //通过成员函数重载+号本质
    //Person p3=p1.operator+(p2)
    //Person p3=operator+(p1,p2)==Person p3 = p1 + p2;

    //运算符重载 也可以发生函数重载
    Person p3 = p1 + 10;
    cout << p3.m_a << endl;
    cout << p3.m_b << endl;
    return 0;
}

二、左移<< 运算符重载

可以输出自定义数据类型

#include
using namespace std;
class Person
{
public:
	friend ostream& operator<<(ostream& cout, Person& p);
	Person()
	{}
	Person(int a, int b)
	{
		this->m_a = a;
		this->m_b = b;
	}
private:
	int m_a;
	int m_b;

	//不能利用成员函数重载<<运算符,因为无法实现 cout在左侧



};
//全局函数重载<<运算符
ostream& operator<<(ostream& cout, Person& p)
{
	cout << p.m_a << " " << p.m_b;
	return cout;
}
int main()
{
	Person p1(10, 20);
	Person p2(10, 20);
	//标准调用
	operator<<(cout, p1) << endl;
	//简化版本
	//cout<
	return 0;

}

递增运算符重载

前置递增运算符重载

#include
using namespace std;
class MyInteger
{
	friend ostream& operator<<(ostream& cout, MyInteger& myint);
public:

	MyInteger()
	{
		m_num = 0;
	}
	//重载前置++运算符
	//MyInteger& operator++()
	//{
	//	//先进行++运算
	//	m_num++;
	//	//再返回自身
	//	return *this;
	//}
	//重载后置++运算符
	MyInteger operator++(int)//int 代表占位参数,可以用于区分前置和后置递增
	{
		//先记录当时的结果
		MyInteger temp = *this;
		//后递增
		m_num++;
		return temp;
	}
private:
	int m_num;
};

//重载左移运算符
ostream& operator<<(ostream & cout, MyInteger& myint)
{
	cout << myint.m_num;
	return cout;
}

int main()
{
	MyInteger myint;
	//cout << myint++ << endl;
	cout << myint << endl;
	//cout << ++(++myint) << endl;
	return 0;
}

重载赋值运算符

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

#include
using namespace std;
class Person
{
public:
	Person(int age)
	{
		m_age = new int(age);
	}
	~Person()
	{
		
	}
	Person& operator+(Person &p)
	{
		if (m_age != NULL)
		{
			delete m_age;
		}
		m_age = new int(*p.m_age);
		return *this;
	}
	int* m_age;
private:
	
};
int main()
{
	Person p1(18);
	Person p2(20);
	Person p3(30);
	p3 = p2 = p1;
	cout << *p1.m_age << endl;
	cout << *p2.m_age << endl;
	cout << *p3.m_age << endl;
	return 0;
}

重载关系运算符

#include
using namespace std;
class Person
{
public:
	Person(string name,int age)
	{
		m_name = name;
		m_age = age;
	}
	
	bool operator==(Person &p)
	{
		if (this->m_name==p.m_name&&this->m_age==p.m_age)
		{
			return true;
		}
		return false;
	}
	bool operator!=(Person &p)
	{
		if (this->m_name==p.m_name&&this->m_age==p.m_age)
		{
			return false;
		}
		return true;
	}
	string m_name;
	int m_age;
private:
	
};
int main()
{
	Person p1("hi",18);
	Person p2("hi",18);

	if( p1 ==p2)
	{
		cout<<"相等"<<endl;
	}

	return 0;
}

重载关系函数调用运算符

由于重载后使用的方式非常像函数的调用,因此被称为仿函数

#include
#include
using namespace std;
class Person
{
public:
	
	//重载()运算符
	void operator()(string test)
	{
		cout<<test<<endl;
	}	
private:
	
};
void myprint(string test)
{
	cout<<test<<endl;
}
int main()
{
	Person p1;
//	p1.operator ()("hello");
	p1("hello");
	myprint("hello");
	return 0;
}

继承

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

#include
using namespace std;

class Base1
{
public:
	int m_a;
protected:
	int m_b;
private:
	int m_c;
};
//公共继承
class Son1:public Base1
{
public:
	void func()
	{
		m_a=10;//父类中的公共权限成员 到子类中依然是公共权限
		m_b=10;//父类中的保护权限成员 到子类中依然是保护权限
		//m_c=10;//父类中的私有权限成员 子类访问不到
	}
};
//保护继承
class Son2:protected Base1
{
public:
	void func()
	{
		m_a=10;//父类中的公共权限成员 到子类中变为保护权限
		m_b=10;//父类中的保护权限成员 到子类中是保护权限
		//m_c=10;//父类中的私有权限成员 子类访问不到
	}
}; 
//私有继承
class Son3:private Base1
{
public:
	void func()
	{
		m_a=10;//父类中的公共权限成员 到子类中变为私有权限
		m_b=10;//父类中的保护权限成员 到子类中是私有权限
		//m_c=10;//父类中的私有权限成员 子类访问不到
	}
};
class GrandSon3:public Son3
{
public:
	void func()
	{
		//m_a=1000;//访问不到 ,因为Son3中的成员变量是私有继承来的,类外都访问不到
		
	}
};
int main() 
{
	//public权限在类内类外都可以访问
	//protected权限在子类中可以访问 但在类外不可访问
	//private权限只能在本类中使用
	Son1 s1;
	s1.m_a=100;
	//s1.m_b=100;//在son1中 m_b是保护权限,类外不可访问

	Son2 s2;
	//s2.m_a=1000;//到son2中 m_a变为保护权限,类外不可访问
	//s2.m_b=1000;//在son2中 m_b保护权限,类外不可访问

	Son3 s3;
	//s3.m_a=1000;//到Son3中变为私有成员 类外访问不到
	//s3.m_b=1000;//到Son3中变为私有成员 类外访问不到


}

继承中构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类的构造函数。

#include
using namespace std;

class Base1
{
public:
	Base1()
	{
		cout<<"Base1构造函数"<<endl;
	}
	~Base1()
	{
		cout<<"Base1析构函数"<<endl;
	}
};
//公共继承
class Son1:public Base1
{
public:
	Son1()
	{
		cout<<"Son1构造函数"<<endl;
	}
	~Son1()
	{
		cout<<"Son1析构函数"<<endl;
	}
};


int main() 
{
	//Base1 b;
	Son1 s;//先构造父类的,在构造子类,析构与构造顺序相反
	return 0;

}

继承同名成员处理方式

当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据?
访问子类同名成员 直接访问即可
访问父类同名成员 需要加作用域

#include
using namespace std;
//继承中同名成员处理
class Base1
{
public:
	Base1()
	{
		m_a=100;
	}
	int m_a;
	void func()
	{
		cout<<"Base1"<<endl;
	}
};
//公共继承
class Son1:public Base1
{
public:
	Son1()
	{
		m_a=200;
	}
	int m_a;
	void func()
	{
		cout<<"Son1"<<endl;
	}
};


int main() 
{

	Son1 s;
	cout<<s.m_a<<endl;//如果同名访问的是子类
	cout<<s.Base1::m_a<<endl;//如果需要访问父类,则需要加上作用域
	s.func();//直接调用 也是调用子类的同名函数
	s.Base1::func();
	return 0;

}

多继承语法

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

#include
using namespace std;
//继承中同名成员处理
class Base1
{
public:
	Base1()
	{
		m_a=100;
	}
	int m_a;
};
class Base2
{
public:
	Base2()
	{
		m_a=200;
	}
	int m_a;
};
//子类 需要继承 Base1和Base2
class Son1:public Base1,public Base2
{
public:
	Son1()
	{
		m_c=300;
		m_d=400;
	}
	int m_c;
	int m_d;
};


int main() 
{

	Son1 s;

	cout<<"sizeof Son="<<sizeof(s)<<endl;
	//当父类中出现同名成员,需要加作用域区分
	cout<<"Base1::m_a "<<s.Base1::m_a<<endl;
	cout<<"Base2::m_a "<<s.Base2::m_a<<endl;
	return 0;

}

菱形继承

菱形继承概念:
两个派生类继承同一个基类
又有某个类同时继承两个派生类
这种继承被称为菱形继承
利用虚继承解决菱形继承的问题

#include
using namespace std;
//继承中同名成员处 理
//继承之前加上关键字virtual 变为虚继承
//Animal成为虚基类 
class Animal
{
 public:
	int m_Age;
};
class Sheep: virtual public Animal
{
public:
	
};
class Tuo: virtual public Animal
{
public:
	
};

//子类 需要继承 Base1和Base2
class SheepTuo:public Sheep,public Tuo
{
public:
};

  
int main() 
{
	 SheepTuo st;
	 st.Sheep::m_Age=10;
	 st.Tuo::m_Age=20;//这个数据只有一个了
	 cout<<"st.Sheep::m_Age "<<st.Sheep::m_Age<<endl;
	cout<<"st.Tuo::m_Age "<<st.Tuo::m_Age<<endl;
	return 0;

}

多态

多态是C++面向对象三大特性之一
多态分为两类
静态多态 :函数重载和运算符重载属于静态多态,复用函数名
动态多态:派生类和虚函数实现多态

#include
using namespace std;
class Animal
{
 public:
	//虚函数
	 virtual void speak()
	 {
		cout<<"动物再说话"<<endl;
	 }
};
class Cat: public Animal
{
public:
	void speak()
	 {
		cout<<"小猫再说话"<<endl;
	 }

};
class Dog: public Animal
{
public:
	void speak()
	 {
		cout<<"小狗再说话"<<endl;
	 }

};
//动态多态满足条件
//1.有继承关系
//2.子类重写父类的虚函数
void doSpeak(Animal &animal)
{
	animal.speak();
}
int main() 
{
	Cat cat;
	doSpeak(cat);
	Dog dog;
	doSpeak(dog);	
	return 0;
}

纯虚函数和抽象类

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

#include
using namespace std;
class Base
{
public:
	//纯虚函数
	//只要有一个纯虚函数,这个类称为抽象类
	//抽象类的特点:
	//1.无法实例化对象
	//2.抽象类的子类 必须重写父类中的纯虚函数,否则也属于抽象类

	virtual void func()=0;

};
class Son:public Base
{
public:
	virtual void func()
	{
		cout<<"func函数调用"<<endl;
	}
};
int main()
{
	Base* base=new Son;
	base->func();
	return 0;
}

制作饮品

#include
using namespace std;
class AbstractDrinking
{
public:
	//煮水
	virtual void Boil()=0;
	//冲泡
	virtual void Brew()=0;
	//倒入杯中
	virtual void PourInCup()=0;
	//加入辅料
	virtual void PutSomething()=0;
	void makeDrink()
	{
		Boil();
		Brew();
		PourInCup();
		PutSomething();
	}
};

//制作咖啡
class Coffee:public AbstractDrinking
{
public:
	virtual void Boil()
	{
		cout<<"煮水"<<endl;
	}
	virtual void Brew()
	{
		cout<<"冲泡咖啡"<<endl;
	}
	virtual void PourInCup()
	{
		cout<<"倒入杯中"<<endl;
	}
	virtual void PutSomething()
	{
		cout<<"加入糖和牛奶"<<endl;
	}
};
//制作咖啡
class Tea:public AbstractDrinking
{
public:
	virtual void Boil()
	{
		cout<<"煮矿泉水"<<endl;
	}
	virtual void Brew()
	{
		cout<<"冲泡茶叶"<<endl;
	}
	virtual void PourInCup()
	{
		cout<<"倒入杯中"<<endl;
	}
	virtual void PutSomething()
	{
		cout<<"加入柠檬"<<endl;
	}
};
//一个接口多个实现
void doWork(AbstractDrinking *abs)//AbstractDrinking *abs=new Coffee
{
	abs->makeDrink();
	delete abs;
}

int main()
{
	doWork(new Coffee);
	doWork(new Tea);
	return 0;
}

虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调佣到子类的析构代码
解决方式 :将父类中的析构函数改为虚析构和纯虚析构
虚析构和纯虚析构共性:
可以解决父类指针释放子类对象
都需要有具体的函数实现
虚析构和纯虚析构区别:
如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:virtual ~类名(){}

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

#include
#include
using namespace std;
class Animal
{
 public:
	 Animal()
	 {
		cout<<"Animal的构造函数"<<endl;
	 }
	//虚函数
	 virtual void speak()=0;
	//利用虚析构可以解决父类指针释放子类对象时不干净的问题
	 //virtual ~Animal()//给父类析构加上虚析构,就能够执行子类的析构
	 //{
		//cout<<"Animal的析构函数"<
	 //}
	 //纯虚析构
	virtual ~Animal()=0;
};
Animal::~Animal()
{
	cout<<"Animal的纯虚析构函数"<<endl;
}
class Cat: public Animal
{
public:
	Cat(string name)
	{
		cout<<"Cat的构造函数"<<endl;
		m_name=new string(name);
	}
	virtual void speak()
	 {
		cout<<*m_name<<"小猫再说话"<<endl;
	 }
	 string* m_name;
	 ~Cat()
	 {
		if(m_name!=NULL)
		{
			cout<<"Cat的析构函数"<<endl;
			delete m_name;
			m_name=NULL;
		}
	 }
};


int main() 
{
	Animal* animal=new Cat("Tom");
	animal->speak();
	delete animal;
	return 0;
}



组装电脑

#include
#include
using namespace std;
class CPU
{
 public:
	 virtual void calculate()=0;
	
};
class VideoCard
{
 public:
	 virtual void display()=0;
	
};
class Memory
{
 public:
	 virtual void storage()=0;
	
};
class Computer
{
public:
	Computer(CPU* cpu,VideoCard* vc,Memory* mem)
	{
		m_cpu=cpu;
		m_vc=vc;
		m_mem=mem;
	}
	void work()
	{
		m_cpu-> calculate();
		m_vc ->display();
		m_mem->storage();
	}
	~Computer()
	{
		if(m_cpu!=NULL)
		{
			delete  m_cpu;
			m_cpu=NULL;
		}
		if(m_vc!=NULL)
		{
			delete  m_vc;
			m_vc=NULL;
		}
		if(m_mem!=NULL)
		{
			delete  m_mem;
			m_mem=NULL;
		}
	}
private:
	CPU* m_cpu;
	VideoCard* m_vc;
	Memory* m_mem;

};


class IntelCPU: public CPU
{
public:

	virtual void calculate()
	 {
		cout<<"Intel的Cpu开始计算了"<<endl;
	 }
};
class IntelVideoCard: public VideoCard
{
public:

	virtual void display()
	 {
		cout<<"Intel的显卡开始显示了"<<endl;
	 }
};
class IntelMemory: public Memory
{
public:

	virtual void storage()
	 {
		cout<<"Intel的内存条开始存储了"<<endl;
	 }
};


class LenovoCPU: public CPU
{
public:

	virtual void calculate()
	 {
		cout<<"Lenovo的Cpu开始计算了"<<endl;
	 }
};
class LenovoVideoCard: public VideoCard
{
public:

	virtual void display()
	 {
		cout<<"Lenovo的显卡开始显示了"<<endl;
	 }
};
class LenovoMemory: public Memory
{
public:

	virtual void storage()
	 {
		cout<<"Lenovo的内存条开始存储了"<<endl;
	 }
};


int main() 
{
	CPU* intelCpu=new IntelCPU;
	VideoCard* intelCard=new IntelVideoCard;
	Memory* intelMem=new IntelMemory;
	Computer* computer1=new Computer(intelCpu,intelCard,intelMem);
	computer1->work();
	delete computer1;
	Computer* computer2=new Computer(new LenovoCPU,new LenovoVideoCard,new LenovoMemory);
	computer2->work();
	delete computer2;
	return 0;
}



文件操作

程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放
通过文件可以将数据持久化
C++中对文件操作需要包含头文件
文件类型分为两种:
1.文本文件 -文本以ASCLL码值的形式存储在计算机中
2.二进制文件 -文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂他们。
ios::in 为读文件而打开文件
ios::out 为写文件而打开文件
ios::ate 初始位置:文件尾
ios::app 追加方式写文件
ios::trunc 如果文件存在先删除,在创建
ios::binary 二进制形式

写文件

在当前文件路径下便可以找到存储的内容

#include
#include
using namespace std;
int main()
{
	//写文件,将数据存入到磁盘中  
	ofstream ofs;
	ofs.open("test.txt", ios::out);//打开方式
	ofs << "姓名:张三" << endl;
	ofs << "性别:男" << endl;
	ofs.close();
	return 0;
}

读文件

#include
#include
#include
using namespace std;
int main()
{
	//读文件
	ifstream ifs;
	ifs.open("test.txt", ios::in);
	if (!ifs.is_open())
	{
		cout << "文件打开失败" << endl;
		return 0;
	}
	//第一种
	/*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();
	
	return 0;

}

二进制文件

写文件

#include
#include
#include
using namespace std;
class Person
{
public:
	char m_Name[64];
	int m_Age;
};
int main()
{
	ofstream ofs;
	ofs.open("person.txt",ios::out|ios::binary);
	
	Person p={"张三",18};
	ofs.write((const char*)&p,sizeof(Person));
	ofs.close();
	return 0;
}	

二进制读文件

#include
#include
#include
using namespace std;
class Person
{
public:
	char m_Name[64];
	int m_Age;
};
int main()
{
	ifstream ifs;
	ifs.open("person.txt",ios::in|ios::binary);
	
	if(!ifs.is_open())
	{
		cout<<"文件打开失败"<<endl;
		
	}
	Person p;
	ifs.read(( char*)&p,sizeof(Person));
	cout<<"姓名"<<p.m_Name<<"年龄"<<p.m_Age<<endl;
	ifs.close();

	return 0;
}	

你可能感兴趣的:(c++,开发语言,算法)