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


文章目录

  • 一、封装
    • 示例1:
    • 访问权限
    • struct 和 class 区别
    • 检测数据有效性:
    • 练习1:
    • 练习2:
    • 构造函数
    • 析构函数
    • 构造函数的调用和分类
    • 拷贝构造函数调用时机
    • 初始化列表
    • 类对象作为类对象
    • 静态成员
    • 静态成员函数
    • C++对象模型和this指针
    • this指针
    • 空指针访问成员函数
    • const修饰成员函数
    • 友元
    • 运算符重载
    • 1.加号运算符重载
    • 2.全局函数重载+号
    • 3.重载左移运算符,前置++运算符和后置++运算符
    • 4.赋值运算符= 重载
    • 5.关系运算符重载,将两个自定义对象进行比对
    • 重载()
  • 二、继承
    • 继承方式:
    • 继承中的对象模型
    • 继承中的析构和构造顺序
    • 继承中同名成员处理方式
    • 继承同名静态成员处理方式
    • 菱形继承
  • 三、多态
    • 动态多态满足条件
    • 动态多态使用
    • 案例
    • 纯虚函数和抽象类
    • 虚析构和纯虚析构
    • 虚析构
    • 纯虚析构
    • 案例


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) {//TODOcout << "相等" << endl;}else {//TODOcout << "不相等" << endl;}ref = c1.isSameByClass(c2);if (ref) {//TODOcout << "相等" << endl;}else {//TODOcout << "不相等" << 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_6Person(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;}//报错原因:传入指针为NULLcout << "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;
}

本文链接:https://my.lmcjl.com/post/10319.html

展开阅读全文

4 评论

留下您的评论.