C++面向对象编程(类和对象)

全部学完提供一个练习的项目,在博主的主页可以找到该免费资源



C++面向对象三大特性: 封装,继承,多态


一、封装

意义:将属性和行为加以权限控制

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

示例1:

#include <iostream>
using namespace std;

//圆周率
const double PI=3.14;

//class代表一个类,后面紧跟类名
class Circle 
{
	//访问权限
	//公共权限
public:
	//属性	
	//半径
	int m_r;
	//行为
	//获取圆的周长
	double calculateZC()
	{
		return 2*PI*m_r;
	}
};

int main()
{
	//通过圆类 创建具体的圆(对象)
	//实例化(通过一个类创建一个对象的过程)
	Circle c1;
	//给圆对象属性进行赋值
	c1.m_r=10;
	
	cout<<"圆的周长为:"<<c1.calculateZC()<<endl;
	
	system("pause");
	return 0;
}

访问权限

有三种:
1.public 公共权限
2.protected 保护权限
3.private 私有权限

#include <iostream>
#include <string>
using namespace std;

//访问权限 
//三种
//公共权限 public     成员 类内可以访问 类外可以访问
//私有权限 protected  成员 类内可以访问 类外不可以访问
//保护权限 private    成员 类内可以访问 类外不可以访问

class Person 
{
public:
	//公共权限
	string m_Name;
	
protected:
	//保护权限
	string m_Car;
	
private:
	//私有权限
	int m_Password;
	
private:
	void func()
	{
		m_Name="小黄";
		m_Car="托马斯小火车";
		m_Password=031116;
	}
};

int main()
{
	//实例化对象
	Person p1;
	p1.m_Name="马斯特";
//	p1.m_Car="特斯拉"; 保护权限的内容在类外无法访问
//	p1.m_Password=000000; 私有权限的内容在类外无法访问
//  p1.func;

	system("pause");
	return 0;
}

struct 和 class 区别

默认访问权限不同

struct 默认权限为公共
class 默认权限为私有

#include <iostream>
using namespace std;

class C1
{
	int m_A; //默认权限 私有	
};

struct C2
{
	int m_A;	//默认权限 私有
};

int main()
{
	C1 c1;
	//c1.m_A=100;
	
	C2 c2;
	c2.m_A=100;

	system("pause");
	return 0;
}

将成员属性设置为私有,可以自己控制读写权限,便于检测数据的有效性

#include <iostream>
#include <string>
using namespace std;

//成员属性设置为私有
class Person
{
public:
	void setName(string name)
	{
		m_Name=name;
	}
	string getName()
	{
		return m_Name;
	}
	
	int getAge()
	{
		m_Age=0;	//初始化
		return m_Age;
	}
	
	void setLover(string lover)
	{
		m_Lover=lover;
	}
	
private:	//class中private可以省略
	string m_Name;	//可读可写
	int m_Age;	//只读
	string m_Lover;	//只写
	
	
};

int main()
{
	Person p;
	
	p.setName("红色风暴");
	cout<<"姓名为:"<<p.getName()<<endl;
	cout<<"英雄可以受侮辱,但你不可以踩我的黄金切尔西"<<endl;
	
	cout<<"年龄为:"<<p.getAge()<<endl;
	//p.Age=18;
	//p.setAge=18; 只读不可写
	
	p.setLover("霉你不行");
//	cout<<"情人:"<<p.m_Lover<<endl;
//	cout<<"情人:"<<p.getLover<<endl;	只写权限
	
	system("pause");
	return 0;
}

定义属性为private,若想可读可写则定义public权限的set和get函数

检测数据有效性:

#include <iostream>
#include <string>
using namespace std;

//成员属性设置为私有
class Person
{
public:
	void setName(string name)
	{
		m_Name=name;
	}
	string getName()
	{
		return m_Name;
	}
	
	//可读可写 如果想修改(年龄的范围必须是0~150)
	int getAge()
	{
		m_Age=0;	//初始化
		return m_Age;
	}
	void setAge(int age)
	{
		if(age<0||age>150)
		{
			m_Age=0;
			cout<<"小伙子你很优秀啊"<<endl;
			return;
		}
		m_Age=age;
	}
	
	void setLover(string lover)
	{
		m_Lover=lover;
	}
	
private:	//class中private可以省略
	string m_Name;	//可读可写
	int m_Age;	//只读
	string m_Lover;	//只写
	
	
};

int main()
{
	Person p;
	
	p.setName("红色风暴");
	cout<<"姓名为:"<<p.getName()<<endl;
	cout<<"英雄可以受侮辱,但你不可以踩我的黄金切尔西"<<endl;
	
	p.setAge(1000);
	cout<<"年龄为:"<<p.getAge()<<endl;
	//p.Age=18;
	//p.setAge=18; 只读不可写
	
	p.setLover("霉你不行");
//	cout<<"情人:"<<p.m_Lover<<endl;
//	cout<<"情人:"<<p.getLover<<endl;	只写权限
	
	system("pause");
	return 0;
}

练习1:

#include <iostream>
#include <string>
using namespace std;

//分别利用全局函数和成员函数判断立方体是否相等
class Cube
{
public:
	//行为
	//设置长宽高
	void setZ(int l, int w, int h)
	{
		m_L = l;
		m_W = w;
		m_H = h;
	}

	//获取长宽高
	int getL()
	{
		return m_L;
	}
	int getW()
	{
		return m_W;
	}
	int getH()
	{
		return m_H;
	}

	//获取立方体面积
	int cS()
	{
		return 2 * m_L * m_W + 2 * m_L * m_H + 2 * m_W * m_H;
	}
	//获取立方体体积
	int cV()
	{
		return m_H * m_L * m_W;
	}

	//利用成员函数判断两个立方体是否相等
	bool isSameByClass(Cube &c)
	{
		if (m_L == c.getL() && m_H == c.getH() && m_W == c.getW())
		{
			return true;
		}
		return false;
	}
	
private:
	//属性
	int m_L;	//长
	int m_W;	//宽
	int m_H;	//高
};

//利用全局函数判断两个立方体是否相等
bool isSame(Cube c1, Cube c2)
{
	if (c1.getH() == c2.getH() && c1.getL() == c2.getL() && c1.getW() == c2.getW())
	{
		return true;
	}
	return false;
}

int main()
{
	//创建立方体对象
	Cube c1;
	c1.setZ(10, 10, 10);
	cout << "c1的面积:" << c1.cS() << endl;
	cout << "c1的体积:" << c1.cV() << endl;

	Cube c2;
	c2.setZ(10, 10, 10);

	bool ref = isSame(c1, c2);
	if (ref) {
		//TODO
		cout << "相等" << endl;
	}
	else {
		//TODO
		cout << "不相等" << endl;
	}

	ref = c1.isSameByClass(c2);
	if (ref) {
		//TODO
		cout << "相等" << endl;
	}
	else {
		//TODO
		cout << "不相等" << endl;
	}

	system("pause");
	return 0;
}

练习2:

#include <iostream>
using namespace std;

class Point
{
public:
 void setX(int x)
 {
  m_X = x;
 }
 int getX()
 {
  return m_X;
 }

 void setY(int y)
 {
  m_Y = y;
 }
 int getY()
 {
  return m_Y;
 }

private:
 int m_X;
 int m_Y;
};

class Circle
{
public:
 void setR(int r)
 {
  m_R = r;
 }
 int getR()
 {
  return m_R;
 }

 void setC(Point c)
 {
  m_Center = c;
 }
 Point getC()
 {
  return m_Center;
 }

private:
 int m_R;

 Point m_Center;
};

//判断点和圆关系
void isInCircle(Circle & c, Point & p)
{
 int d=(c.getC().getX() - p.getX())* (c.getC().getX() - p.getX()) + (c.getC().getY() - p.getY()) * (c.getC().getY() - p.getY());

 int rD = c.getR()*c.getR();

 if (d == rD)
 {
  cout << "点在圆上" << endl;
 }
 else if (d > rD)
 {
  cout << "点在圆外" << endl;
 }
 else
 {
  cout << "点在圆内" << endl;
 }
}

int main()
{
 //创建圆
 Circle c;
 c.setR(10);
 Point Center;
 Center.setX(10);
 Center.setY(0);
 c.setC(Center);

 //创建点
 Point p;
 p.setX(10);
 p.setY(10);

 //判断关系
 isInCircle(c, p);

 system("pause");
 
 return 0;
}

构造函数

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

析构函数

语法:~类名(){}
1.没有返回值也不写void
2.函数名称与类名相同,在名称前加上符号
3.不可以有参数,因此不能重载
4.程序在对象销毁钱会自动调用析构,无须手动调用,而且只会调用一次

#include <iostream>
using namespace std;

//对象的初始化和清理
class Person
{
public:
	//1.构造函数
	Person()//若未写,编译器会自动构造
	{
		cout << "构造函数的调用" << endl;
	}

	//对象在销毁前会自动调用析构函数,而且只会调用一次
	//2.析构函数 进行清理的操作
	~Person()
	{
		cout << "Person的析构函数的调用" << endl;
	}
};

void test01()
{
	Person p; //栈上
}

int main()
{
	test01();

	system("pause");
	return 0;
}

构造和析构都是必须有的实现,如果我们自己不提供,编译器会提供一个空是心啊的构造和析构

构造函数的调用和分类

1.按参数分类:有参构造和无参构造
2.按类型分类:普通构造和拷贝构造

三种调用方式:
括号法
显示法
隐式转换法

#include <iostream>
using namespace std;

class Person
{
public:
	//非拷贝构造的均为普通构造
	//无参构造
	Person()//默认情况提供无参构造
	{
		cout << "Person的无参构造函数调用" << endl;
	}
	//有参构造
	Person(int a)
	{
		age = a;
		cout << "Person的有参构造函数调用" << endl;
	}
	
	//拷贝构造
	Person(const Person &p)
	{
		//将传入的人身上的所有属性,拷贝到自己身上
		age = p.age;
		cout << "拷贝构造函数!" << endl;
	}

	//析构函数,作用为释放
	~Person()
	{
		cout << "Person的析构函数调用" << endl;
	}

public:
	int age;
};

//构造函数的调用
//调用无参构造函数
void test01()
{
	Person p(10);
}

//调用有参构造函数
void test02()
{
	//1.括号法
	Person p1; //默认构造函数调用
	Person p2(10); //有参构造函数调用
	Person p3(p2); //拷贝构造函数调用

	cout << "p2的年龄:" << p2.age <<endl;
	cout << "p2的年龄:" << p3.age <<endl;

	//2.显式法
	Person p4;
	Person p5 = Person(10);
	Person p6 = Person(p5);

	Person(10);	//匿名对象 特点:当前行执行结束后,系统会立即回收匿名对象
	cout << "aaaa" << endl;

	//不要利用拷贝构造函数 初始化匿名对象
	//编译器会认为 Person(p_6)===Person p_6
	Person(p_6);

	//3.隐式转换法
	Person p7 = 10; //相当于写了 Perosn p4 = Person(10);
	Person p8 = p7;	//Person p5 = Person (p4);

}

int main()
{
	test01();
	test02();

	system("pause");
	return 0;
}

编译结构如下:
在这里插入图片描述

拷贝构造函数调用时机

1.使用一个已经创建完毕的对象来初始化一个新对象
2.值传递的方式给函数参数传值
3.以值方式返回局部对象

#include <iostream>
using namespace std;

//拷贝构造函数使用时机

class Person
{
public:
	Person()
	{
		cout << "Person默认函数调用" << endl;
	}

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

	Person(const Person& p)
	{
		m_Age = p.m_Age;
		cout << "拷贝构造函数调用" << endl;
	}

	~Person()
	{
		cout << "Person析构函数调用" << endl;
	}

	int m_Age;
};

//1.使用一个已经创建完毕的对象来初始化一个新对象
void test01()
{
	Person p1(20);
	Person p2(p1);	//调用拷贝构造函数

	cout << "p2的年龄:" << p2.m_Age << endl;
}

//2.值传递的方式给函数参数传值
void doWork(Person p)
{

}

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

//3.值方式返回局部对象
Person doWork2()
{
	Person p3;
	return p3;
}

void test03()
{
	Person p4 = doWork2();
}

int main()
{
	test01();
	cout << endl;
	test02();
	cout << endl;
	test03();

	system("pause");
	return 0;
}

初始化列表

作用:
C++提供了初始化列表语法,用来初始化属性

语法:构造函数():属性1(值1),属性2(值2)…()

#include <iostream>
using namespace std;

//初始化列表
class Person
{
public:
	//初始化列表初始化属性
	Person() : m_A(30), m_B(20), m_C(10)
	{

	}

	int m_A;
	int m_B;
	int m_C;
};

void test01()
{
	Person p;

	cout << "m_A=" << p.m_A << endl;
	cout << "m_B=" << p.m_B << endl;
	cout << "m_C=" << p.m_C << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

当在外需要赋初值时改为:

#include <iostream>
using namespace std;

//初始化列表
class Person
{
public:
	//初始化列表初始化属性
	Person(int a,int b,int c) : m_A(a), m_B(b), m_C(c)
	{

	}

	int m_A;
	int m_B;
	int m_C;
};

void test01()
{
	Person p(30,20,10);

	cout << "m_A=" << p.m_A << endl;
	cout << "m_B=" << p.m_B << endl;
	cout << "m_C=" << p.m_C << endl;
}

int main()
{
	test01();

	system("pause");
	
	return 0;
}

类对象作为类对象

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

例如:

class A{}
class B
{
	A a;
}

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

当其他类对象作为本类成员
构造时先构造类对象,再构造自身
析构顺序和构造相反

#include <iostream>
#include <string>
using namespace std;

class Phone
{
public:

	Phone(string pName)
	{
		m_pName = pName;
	}

	~Phone()
	{
		cout << "Phone的析构函数调用" << endl;
	}

	string m_pName;
};

//类对象作为类成员
class Person
{
public:

	Person(string name, string pName):m_Name(name),m_Phone(pName)
	{

	}

	string m_Name;
	Phone m_Phone;

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

void test01()
{
	Person p("李华", "垃圾苹果");
	cout << p.m_Name << "拿着" << p.m_Phone.m_pName << endl;
}

int main()
{
	test01();

	system("pause");
	
	return 0;
}

静态成员

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

静态成员分为:

静态成员变量

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

#include <iostream>
using namespace std;

//静态成员变量
class Person
{
public :
	//所有对象都共享同一份数据
	//编译阶段就分配内存
	//类内声明,类外初始化
	static int m_A;

	//静态成员变量也有访问权限
private:
	static int m_B;
};

//类外初始化
int Person :: m_A = 100;
int Person :: m_B = 200;

void test01()
{	
	Person p1;
	cout << p1.m_A << endl;

	Person p2;
	p2.m_A = 200;
	cout << p2.m_A << endl;
}

void test02()
{
	//静态成员变量 不属于某个对象 所有对象都共享同一份数据
	//因此静态成员变量有两种访问方式

	//1.通过对象进行访问
	Person p3;
	cout << p3.m_A << endl;

	//2.通过类名进行访问
	cout << Person::m_A << endl;
	//cout << Person::m_B << endl;
}

int main()
{
	test01();
	test02();

	system("pause");

	return 0;
}

静态成员函数

1.所有对象共享同一个函数
2.静态成员函数只能访问静态成员变量

#include <iostream>
using namespace std;

//静态成员函数
//所有对象共享同一个函数
//静态成员函数只能访问静态成员变量

class Person
{
public:

	static void func()
	{
		m_A = 100;	//静态成员函数可以访问静态成员变量
		//m_B = 200;	静态成员函数不可以访问非静态成员变量
		cout << "static void func调用" << endl;
	}

	static int m_A;	//静态成员变量
	int m_B;	//非精要成员变量

	//静态成员函数也有访问权限
private:
	static void func2()
	{
		cout << "static void func2调用"<<endl;
	}
};

int Person::m_A = 0;

void test01()
{
	//1.通过对象访问
	Person p;
	p.func();

	//2.通过类名访问
	Person::func();

	//Person::func2(); 类外访问不到私有静态成员函数
}

int main()
{
	test01();

	system("pause");

	return 0;
}

C++对象模型和this指针

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

C++中,类内的成员变量和成员函数分开存储

只有非静态成员变量才属于类的对象上

#include <iostream>
using namespace std;

//成员变量和成员函数是分开存储的
class Person
{
	int m_A;	//非静态成员变量

	static int m_B;	//静态成员变量 不属于类对象上

	void func() {}//非静态成员函数 不属于类对象上

	static void func2() {}	//静态成员函数 不属于类对象上
};

int Person::m_B=0;

void test01()
{
	Person p;
	//空对象占用内存空间为:1
	//每个空对象也应该有一个独一无二的内存地址
	cout << "size of p=" << sizeof(p) << endl;
}

int main()
{
	test01();

	system("pause");

	return 0;
}

this指针

this指针指向被调用的成员函数所属的对象

this指针是隐含每一个非静态成员函数内的一种指针

this指针不需要定义,直接使用即可

#include <iostream>
using namespace std;

class Person
{
public:
	Person(int age)
	{
		//this指针指向被调用的成员函数所属的对象
		this->age = age;
	}

	int age;
};

//1.解决名称冲突
void test01()
{
	Person p1(19);
	cout << "p1的年龄是:" << p1.age << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}
#include <iostream>
using namespace std;

class Person
{
public:
	Person(int age)
	{
		this->age = age;
	}
	 Person PersonAddAge(Person &p)
	{
		this -> age += p.age;

		//this指向p2的指针,而*this指向的就是p2对象本体
		return *this;
	}

	int age;
};

//1.解决名称冲突
void test01()
{
	Person p1(19);
	cout << "p1的年龄是:" << p1.age << endl;
}

//返回对象本身用 *this
void test02()
{
	Person p1(10);
	Person p2(p1);

	p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);

	cout << "p2的年龄为:" << p2.age << endl;
}

int main()
{
	test01();
	test02();

	system("pause");
	return 0;
}

空指针访问成员函数

C++空指针也可以调用成员函数,但是要注意有没有用到this指针

如果用到this指针,需要加以判断保证代码的健壮性

#include <iostream>
using namespace std;

//空指针调用成员函数
class Person
{
public:
	void showClassName()
	{
		cout << "this is Person class" << endl;
	}

	void showPersonAge()
	{
		if (this == NULL)
		{
			return;
		}

		//报错原因:传入指针为NULL
		cout << "age=" << this -> m_Age << endl;
	}

	int m_Age;
};

void test01()
{
	Person* p = NULL;

	p->showClassName();
//	p->showPersonAge();
}

int main()
{
	

	system("pause");
	return 0;
}

const修饰成员函数

常函数:

成员函数后加const后我们称这个函数为常函数

常函数内不可以修改成员属性

成员属性声明时加关键字mutable后,在常函数中依然可以修改

常对象:

声明对象前加const称该对象为常对象

常对象只能调用常函数

#include <iostream>
using namespace std;

//常函数
class Person
{
public :

	//this指针的本质 指针常量 指针的指向时不可以修改的
	//const Person * const this;
	//在成员函数后面加上const,修饰的是this指向,让指针指向的值也不可以修改
	void showPerson() const
	{
	//	m_A = 100;
	//  this指针不可以修改指针的指向
	//	this=NULL;
	}

	void func();

	int m_A;
	mutable int m_B;	//特殊变量,即使在常函数中,也可以修改这个值
	//加关键字mutable
};

//常对象
void test02()
{
	const Person p;	//在对象前加const,变为常对象
	//p.m_A = 100;
	p.m_B = 1000;	//m_B是特殊值,在常对象下也可以修改

	//常对象只能调用常函数
	p.showPerson();
	//p.func();
}



void test01()
{
	Person p;
	p.showPerson();
}

int main()
{
	test01();

	system("pause");
	return 0;
}

友元

友元的目的: 让一个函数或者类访问另一个类中私有成员

友元关键字: friend

友元的三种实现:
1.全局函数做友元
2.类做友元
3.成员函数做友元

全局函数做友元示例

#include <iostream>
#include <string>
using namespace std;

//建筑物类
class Building
{
	//全局函数是友元,可以访问私有成员
	friend void goodGay(Building* building);

public:
	Building()
	{
		m_SittingRoom = "客厅";
		m_BedRoom = "卧室";
	}

	string m_SittingRoom;

private:
	string m_BedRoom;
};

//全局函数
void goodGay(Building *building)
{
	cout << "好基友全局函数 正在访问:" << building->m_SittingRoom << endl;
	cout << "好基友全局函数 正在访问:" << building->m_BedRoom << endl;
}

void test01()
{
	Building building;
	goodGay(&building);
}

int main()
{
	test01();

	system("pause");
	
	return 0;
}

类做友元示例

#include <iostream>
#include <string>
using namespace std;

//类做友元
class Building;

class GoodGay
{
public :
	GoodGay();

	void visit();	//参观函数 访问Building中的属性

	Building * building;
};

class Building
{
	friend class GoodGay;
public :
	Building();

	string m_SittingRoom;

private :
	string m_BedRoom;
};

//类外写成员函数
Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}

GoodGay::GoodGay()
{
	//创建建筑物对象
	building = new Building;	//在堆区创建对象
}

void GoodGay::visit()
{
	cout << "好基友类正在访问:" << building->m_SittingRoom << endl;
	cout << "好基友类正在访问:" << building->m_BedRoom << endl;
}

void test01()
{
	GoodGay gg;
	gg.visit();
}

int main()
{
	test01();

	system("pause");
	
	return 0;
}

成员函数做友元

#include <iostream>
#include <string>
using namespace std;

class Building;

class GoodGay 
{
public:

	GoodGay();

	void visit1();	//让visit1函数可以访问Building中的私有成员
	void visit2();	//让visit2函数不可以访问Building中的私有成员

	Building * building;
};

class Building
{
	//告诉编译器 GoodGay类下的visit函数成员函数作为本类的好朋友,可以访问自由成员
	friend void GoodGay::visit1();

public:

	Building();

	string m_SittingRoom;

private:

	string m_BedRoom;
};

//类外实现成员函数
Building::Building()
{
	m_SittingRoom = "客厅";
	m_BedRoom = "卧室";
}

GoodGay::GoodGay()
{
	building = new Building;

}

void GoodGay::visit1()
{
	cout << "visit1函数正在访问" << building->m_SittingRoom << endl;
	cout << "visit1函数正在访问" << building->m_BedRoom << endl;
}
void GoodGay::visit2()
{
	cout << "visit2函数正在访问" << building->m_SittingRoom << endl;
	//  cout << "visit2函数正在访问" << building->m_BedRoom << endl;
}

void test01()
{
	GoodGay gg;
	gg.visit1();
	gg.visit2();
}

int main()
{
	test01();

	system("pause");

	return 0;
}

运算符重载

运算重载概念:

对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型

1.加号运算符重载

#include <iostream>
using namespace std;

//加号运算符重载

class Person
{
public :
	//1.成员函数重载+号
	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 m_A;
	int m_B;
};

void test01()
{
	Person p1;
	p1.m_A = 10;
	p1.m_B = 10;

	Person p2;
	p2.m_A = 20;
	p2.m_B = 20;

	Person p3=p1+p2;
	cout << "p3.m_A=" << p3.m_A << endl;
	cout << "p3.m_B=" << p3.m_B << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

2.全局函数重载+号

#include <iostream>
using namespace std;

//加号运算符重载
class Person
{
public :

	int m_A;
	int m_B;
};

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

void test01()
{
	Person p1;
	p1.m_A = 10;
	p1.m_B = 10;

	Person p2;
	p2.m_A = 20;
	p2.m_B = 20;

	Person p3=p1+p2;
	cout << "p3.m_A=" << p3.m_A << endl;
	cout << "p3.m_B=" << p3.m_B << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

3.重载左移运算符,前置++运算符和后置++运算符

#include <iostream>
using namespace std;

//重载递增运算符

//自定义整型
class MyInteger
{
	friend ostream& operator <<(ostream& cout, MyInteger);

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;
}

void test01()
{
	MyInteger myint;
	cout << myint << endl;

	cout << ++(++myint) << endl;
	cout << myint << endl;
}

void test02()
{
	MyInteger myint;
	cout << myint << endl;

	cout << myint++ << endl;
	cout << myint << endl;
}

int main()
{
	test01();
	cout << endl;
	test02();

	system("pause");
	return 0;
}

4.赋值运算符= 重载

#include <iostream>
using namespace std;

//赋值运算符重载

class Person
{
public:

	Person(int age)
	{
		m_Age=new int(age);
	}

	~Person()
	{
		if (m_Age != NULL)
		{

		}
	}

	//重载 赋值运算符
	Person& operator=(Person & p)
	{
		//编译器提供浅拷贝
		//m_Age = p.m_Age;

		//应该判断是否有属性在堆区,如果有,先释放干净再深拷贝
		if (m_Age != NULL)
		{
			delete m_Age;
			m_Age = NULL;
		}
		//深拷贝
		m_Age = new int(*p.m_Age);

		//返回对象本身
		return *this;
	}

	int * m_Age;
};


void test01()
{
	Person p1(18);
	Person p2(20);
	Person p3(24);

	p3 = p2 = p1;	//赋值操作

	cout << "p1的年龄:" << *p1.m_Age << endl;
	cout << "p2的年龄:" << *p2.m_Age << endl;
	cout << "p3的年龄:" << *p3.m_Age << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

5.关系运算符重载,将两个自定义对象进行比对

#include <iostream>
#include <string>
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;
};

void test01()
{
	Person p1("Tom", 18);

	Person p2("Jerry", 3);

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

int main()
{
	test01();

	system("pause");
	return 0;
}

重载()

又称仿函数,没有固定写法,非常灵活

#include <iostream>
#include <string>
using namespace std;

//函数调用运算符重载

//打印输出类
class MyPrint
{
public:

	//重载函数调用运算符
	void operator ()(string test)
	{
		cout << test << endl;
	}

};

void test02(string test)
{
	cout << test << endl;
}

void test01()
{
	MyPrint myprint;

	myprint("hello world");	//由于使用起来非常类似于函数调用,因此被称为仿函数

	test02("hello world");
}

int main()
{
	test01();

	system("pause");
	return 0;
}

没有固定写法

#include <iostream>
#include <string>
using namespace std;

//函数调用运算符重载

//自加类
class MyAdd
{
public:

	int operator ()(int num1, int num2)
	{
		return num1 + num2;
	}
};

void test01()
{
	MyAdd myadd;
	int res=myadd(100, 200);
	cout << "res= " << res << endl;

	//匿名函数对象
	cout << MyAdd()(100, 100) << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

二、继承

继承的好处:减少重复代码
语法:class 子类:继承方式:父类

子类也称为派生类
父类也称为基类

继承方式:

1.公共继承
2.保护继承
3.私有继承

公共继承示例

//公共继承
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;	//父类中的私有权限成员 子类访问不到
	}
};

void test01()
{
	Son1 s1;
	s1.m_A = 100;
	//s1.m_B = 100;	保护权限类外访问不到
}

保护继承示例:

//保护继承
class Base2
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son2 :protected Base2
{
public:
	void func()
	{
		m_A = 100;	//父类公共成员 到子类中变为保护权限
		m_B = 100;	//父类保护成员 到子类中权限不变
	//	m_C = 100;	父类私有成员 子类访问不到
	}
};

void test02()
{
	Son2 s2;
	//s2.m_A = 1000;	在Son2中m_A变为保护权限,因此类外访问不到
}

私有继承示例

//私有继承
class Base3
{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son3 : private Base3
{
public:
	void func()
	{
		m_A = 100;	//父类中公共成员到子类中变为私有成员
		m_B = 100;	//父类中保护成员到子类中变为私有成员
	//	m_C = 100 父类中私有成员子类访问不到
	}
};

void test03()
{
	Son3 s3;
	//s3.m_A = 1000;	到Son3中变为私有成员,类外访问不到
}

class Grandson3 : public Son3
{
public:
	void func()
	{
	//	m_A = 1000;	到了Son3中m_A变为私有成员,其子类也访问不到
	}
};

继承中的对象模型

#include <iostream>
#include <string>
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 << "size of Son=" << sizeof(Son) << endl;//16
	//父类中所有非静态成员属性都会被子类继承下去
	//父类中私有成员属性被百年一起隐藏,访问不到,但确实被继承
}

int main()
{
	test01();

	system("pause");
	return 0;
}

继承中的析构和构造顺序

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

#include <iostream>
#include <string>
using namespace std;

//继承中的构造和析构顺序
class Base
{
public:
	Base()
	{
		cout << "Base的构造函数!" << endl;
	}
	~Base()
	{
		cout << "Base的析构函数!" << endl;
	}
};

class Son :public Base
{
public:

	Son()
	{
		cout << "Son的构造函数!" << endl;
	}
	~Son()
	{
		cout << "Son的析构函数!" << endl;
	}
};

void test01()
{
	//先构造父类再构造子类,析构的顺序与构造的顺序相反
	Son s;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

继承中同名成员处理方式

访问 子类同名成员 直接访问即可
访问 父类同名成员 需要加作用域

#include <iostream>
#include <string>
using namespace std;

//继承中同名成员处理
class Base
{
public:
	Base()
	{
		m_A = 100;
	}
	void func()
	{
		cout << "Base-func()调用" << endl;
	}

	int m_A;
};

class Son :public Base
{
public:

	Son()
	{
		m_A = 200;
	}

	void func()
	{
		cout << "Son-func()调用" << endl;
	}

	int m_A;
};

//同名属性处理
void test01()
{
	Son s1;
	cout << "Son下m_A=" << s1.m_A << endl;
	cout << "Base下m_A=" << s1.Base::m_A << endl;	//需要加作用域
}

//同名成员函数处理
void test02()
{
	Son s2;

	s2.func();	//直接调用调用的是子类中的同名成员函数
	s2.Base::func();
}

int main()
{
	test01();
	test02();

	system("pause");
	return 0;
}

如果子类中出现和父类同名的成员函数

#include <iostream>
#include <string>
using namespace std;

//继承中同名成员处理
class Base
{
public:
	Base()
	{
		m_A = 100;
	}
	void func()
	{
		cout << "Base-func()调用" << endl;
	}

	void func(int a)
	{
		cout << "Base-func(int a)调用" << endl;
	}

	int m_A;
};

class Son :public Base
{
public:

	Son()
	{
		m_A = 200;
	}

	void func()
	{
		cout << "Son-func()调用" << endl;
	}

	int m_A;
};

//同名属性处理
void test01()
{
	Son s1;
	cout << "Son下m_A=" << s1.m_A << endl;
	cout << "Base下m_A=" << s1.Base::m_A << endl;	//需要加作用域
}

//同名成员函数处理
void test02()
{
	Son s2;

	s2.func();	//直接调用调用的是子类中的同名成员函数
	s2.Base::func();
	//如果子类中出现和父类同名的成员函数
	//子类的同名成员会隐藏掉父类中所有同名成员函数
	//如果想访问需要加作用域
	s2.Base::func(100);
}

int main()
{
	test01();
	test02();

	system("pause");
	return 0;
}

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

静态成员和非静态成员出现同名,处理方式一致

#include <iostream>
#include <string>
using namespace std;

//继承同名静态成员处理方式
//同名静态属性
class Base
{public:

	static int m_A;	//静态成员变量类内声明,类外初始化

	static void func()
	{
		cout << "Base-static void func()" << endl;
	}

	static void func(int a)
	{
		cout << "Base-static void func(int a)" << endl;
	}
};
int Base::m_A = 100;	

class Son :public Base
{
public:
	static int m_A;

	static void func()
	{
		cout << "Son-static void func()" << endl;
	}
};
int Son::m_A = 200;	//静态成员变量类内声明,类外初始化

//访问静态属性有两种
void test01()
{
	//1.通过对象访问
	cout << "通过对象访问:" << endl;
	Son s;
	cout << "Son 下m_A=" << s.m_A << endl;
	cout << "Base 下m_A=" << s.Base::m_A << endl;

	//2.通过类名访问
	cout << "通过类名访问" << endl;
	cout << "Son下m_A=" << Son::m_A << endl;
	cout << "1.Base下m_A=" << Base::m_A << endl;//直接通过父类作用域访问
	cout << "2.Base下m_A=" << Son::Base::m_A << endl;	//通过子类访问父类
}

void test02()
{
	//1.通过对象访问
	cout << "通过对象访问" << endl;
	Son s;
	s.func();
	s.Base::func();

	//2.通过类名访问
	cout << "通过类名访问" << endl;
	Son::func();
	Son::Base::func();
	Son::Base::func(100);
}

int main()
{
	test01();
	cout << endl;
	test02();

	system("pause");
	return 0;
}

区别仅在于静态成员有两种访问方式

菱形继承

能看懂即可

概念:
两个派生类继承一个基类
又有某个类同时继承这两个类

典型案例:

#include <iostream>
#include <string>
using namespace std;

class Animal
{
public:
	int m_Age;
};

//利用虚继承 解决菱形继承的问题
//在继承之前加上关键字 virtual 变为虚继承
//Animal类称为虚基类
class Yang :virtual public Animal {};

class Tuo :virtual public Animal {};

class YangTuo :public Yang, public Tuo {};

void test01()
{
	YangTuo yt;

	yt.Yang::m_Age = 18;
	yt.Tuo::m_Age = 24;
	//当菱形继承,两个父类拥有相同数据,需要加以作用域区分
	cout << "yt.Yang::m_Age =" << yt.Yang::m_Age << endl;
	cout << "yt.Tuo::m_Age =" << yt.Tuo::m_Age << endl;

	//virtual修饰后
	cout << "yt.m_Age =" << yt.m_Age << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

三、多态

多态分为两类:

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

区别:

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

#include <iostream>
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;
	}
};


//执行说话的函数
void doSpeak(Animal& animal)
{
	animal.speak();
}

void test01()
{
	Cat cat;
	doSpeak(cat);
	Dog dog;
	doSpeak(dog);
}

int main()
{
	test01();
}

动态多态满足条件

1.有继承关系
2.子类重写父类的虚函数

动态多态使用

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

多态使用条件:

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

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

多态优点:

1.代码组织结构清晰
2.可读性强
3.利于前期和后期的扩展及维护

案例

》》实现计算器(两种写法)

普通写法:

#include <iostream>
#include <string>
using namespace std;

class Calculator
{
public:

	int getResult(string oper)
	{
		if (oper == "+")
		{
			return m_Num1 + m_Num2;
		}

		else if (oper == "-")
		{
			return m_Num1 - m_Num2;
		}

		else if (oper == "*")
		{
			return m_Num1 * m_Num2;
		}

		//如果想扩展新的功能,需要修改源码
	}

	int m_Num1, m_Num2;
};

void test01()
{
	//创建计算器对象
	Calculator c;
	c.m_Num1 = 10;
	c.m_Num2 = 20;

	cout << c.m_Num1 << "+" << c.m_Num2 << "=" << c.getResult("+") << endl;
	cout << c.m_Num1 << "-" << c.m_Num2 << "=" << c.getResult("-") << endl;
	cout << c.m_Num1 << "*" << c.m_Num2 << "=" << c.getResult("*") << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

在真实开发中,提倡 开闭原则
对扩展进行开发,对修改进行关闭

多态写法:

#include <iostream>
#include <string>
using namespace std;

//利用多态实现计算器

//实现计算器抽象类
class AbstractCalculator
{
public:

	virtual int getResult()
	{
		return 0;
	}

	int m_Num1;
	int m_Num2;
};


//加法计算器
class AddCalculator :public AbstractCalculator
{
public:

	int getResult()
	{
		return m_Num1 + m_Num2;
	}

};

//减法计数器

class SubCalculator :public AbstractCalculator
{
public:

	int getResult()
	{
		return m_Num1 - m_Num2;
	}
};

//乘法计算器

class MulCalculator :public AbstractCalculator
{
public:

	int getResult()
	{
		return m_Num1 * m_Num2;
	}
};

void test01()
{
	//多态使用条件
	//父类指针活引用指向子类对象

	//加法运算
	AbstractCalculator* abc = new AddCalculator;

	abc->m_Num1 = 10;
	abc->m_Num2 = 20;

	cout << abc->m_Num1 << "+" << abc->m_Num2 << "=" << abc->getResult() << endl;
	//用完后记得销毁
	delete abc;

	//减法运算
	abc = new SubCalculator;
	abc->m_Num1 = 100;
	abc->m_Num2 = 100;

	cout << abc->m_Num1 << "" << abc->m_Num2 << "=" << abc->getResult() << endl;
	delete abc;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

纯虚函数和抽象类

在多态中,通常父类中虚函数的实现毫无意义,主要都是调用子类重写的内容

因此可以将虚函数改为纯虚函数

纯虚函数语法:
virtual 返回值类型 函数名 (参数列表)=0

当类中有了春旭函数,这个类也称为抽象类

抽象类特点:

1.无法实例化对象
2.子类必须重写抽象类中的纯虚函数,否则也属于抽象类

#include <iostream>
#include <string>
using namespace std;

//纯虚数和抽象类
class Base
{
public:
	//纯虚函数
	//只要有一个纯虚函数,这个类称为抽象类
	virtual void func() = 0;
	
};

class Son :public Base
{
public:
	virtual void func()
	{
		cout << "func()调用" << endl;
	};
	//子类必须重写父类中的纯虚函数,否则无法实例化对象
};

void test01()
{
	Base * base = new Son;
	base->func();
}

int main()
{
	test01();

	system("pause");
	return 0;
}

虚析构和纯虚析构

多态使用中,如果子类中有属性开辟到堆区,父类指针在释放时无法调用到子类的析构代码

解决方式: 将父类中的析构函数改为虚析构或者纯虚析构

虚析构

#include <iostream>
#include <string>
using namespace std;

//虚析构和纯虚析构
class Animal
{
public:
	Animal()
	{
		cout << "Animal构造函数调用" << endl;
	}
	//纯虚函数
	virtual void speak() = 0;
	
	virtual ~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;
		}
	}
};

void test01()
{
	Animal* animal = new Cat("Tom");
	animal->speak();
	//父类指针在析构时,不会调用子类析构函数,导致子类中如果有堆区属性,出现内存泄露
	delete animal;
}

int main()
{
	test01();
}

利用虚析构可以解决父类指针释放子类对象时不干净的问题

纯虚析构

#include <iostream>
#include <string>
using namespace std;

//虚析构和纯虚析构
class Animal
{
public:
	Animal()
	{
		cout << "Animal构造函数调用" << endl;
	}
	
	virtual void speak() = 0;
	//纯虚函数
	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;
		}
	}
};

void test01()
{
	Animal* animal = new Cat("Tom");
	animal->speak();
	//父类指针在析构时,不会调用子类析构函数,导致子类中如果有堆区属性,出现内存泄露
	delete animal;
}

int main()
{
	test01();
}

有了纯虚析构就属于抽象类

案例

》》电脑装载

#include <iostream>
#include <string>
using namespace std;

class CPU
{
public:
	//抽象计算函数
	virtual void calculate() = 0;
};

class Memory
{
public:
	//抽象存储函数
	virtual void storage() = 0;
};

class VideoCard
{
public:
	//抽象显示函数
	virtual void display() = 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;	//cpu零件指针
	VideoCard* m_Vc;	//显卡零件指针
	Memory* m_Mem;	//内存条
};

//具体厂商
//Intel厂商
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;
	}
};

void test01()
{
	//第一台电脑零件
	CPU* intelCPU = new IntelCPU;
	VideoCard* intelCard = new IntelVideoCard;
	Memory* intelMem = new IntelMemory;

	//创建第一台电脑
	Computer* computer1 = new Computer(intelCPU, intelCard, intelMem);
	computer1->work();
	cout << "第一台电脑开始工作" << endl;
	delete computer1;

	cout << "-----------------------" << endl;

	//第二台电脑零件
	CPU* lenovoCPU = new LenovoCPU;
	VideoCard* lenovoVC = new LenovoVideoCard;
	Memory* lenovoMem = new LenovoMemory;


	//创建第二台电脑
	Computer* computer2 = new Computer(lenovoCPU, lenovoVC, lenovoMem);
	computer2->work();
	cout << "第二台电脑开始工作" << endl;
	delete computer2;

	cout << "-----------------------" << endl;

	//第三台电脑组装
	Computer* computer3 = new Computer(new LenovoCPU, new IntelVideoCard, new LenovoMemory);
	computer3->work();
	cout << "第三台电脑开始工作" << endl;
	delete computer3;
}

int main()
{
	test01();

	system("pause");
	return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值