C++学习笔记(18)—— 类和对象

 

 

C++面向对象的三大特性:

  • 封装
  • 继承
  • 多态

 

C++认为万事万物都皆为对象,对象上都有其属性和行为

 

列如:

  • 每个人是一个对象:属性有 姓名、年龄、身高、体重;行为有 走、跑、跳、吃饭、唱歌
  • 每辆车是一个对象:属性有 方向盘尺寸、轮胎大小、车灯亮度;   行为有 载人、放音乐、放空调、拉货
  • 具有相同性质的对象,我们可以抽象称为类,人属于人类,车属于车类

1、封装

1.1、封装的意义

封装是c++面向对象三大特性之一

意义:

  • 将属性和行为作为一个整体,表现生活中的事物
  • 将属性和行为加以权限控制
  • 在设计类的时候,属性和行为写在一起,表现事物

实例化:

  • 通过类创建对象的过程

语法:

  • class 类名{访问权限:属性\行为 ; 访问权限:属性、行为。。。。。}

eg: 设计一个圆类,求圆的周长

 

#include <iostream>
using namespace std;
//圆周率
const double PI = 3.14;

//设计一个圆类,球员的周长
//圆求周长公式:2*PI*半径

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

cout << "封装的意义:" << endl << endl;
//通过圆类创建一个具体的圆(对象)
//实例化 (通过一个类 创建一个对象的过程叫实例化)
Circl* cl= new Circl;
//给圆对象 的属性进行赋值
cl->m_r = 20;
//调用圆对象的 行为 计算周长,并打印
//3.14*2*20
double calc= cl->calculateZC();
cout << "这个圆的周长是:" << calc << endl;
delete cl;
system("pause");
return 0;
}

 

1.2、案例练习:

描述:

设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号

解释:

  • 类中的属性和行为  我们统称为 成员
  • 属性 成员属性:成员变量
  • 行为 成员行为 :成员方法

eg:

#include <iostream>
#include <string>
using namespace std;
//设计一个学生类,属性有姓名、学号,可以给姓名和学好进行赋值,可以显示学生的姓名和学号
//设计学生类
class Student
{
//权限控制
//公共权限
public:
//属性
string name;
int id;
//方法
void showStudent()
{
cout << "姓名:" << name << endl;
cout << "学号:" << id << endl;
}
void setname(string naems)
{
name = naems;
}
void setid(int idd)
{
id = idd;
}
};
int main()
{
cout << "封装一个学生类练习:" << endl;
//实例化一个对象
Student* s1= new Student;
s1->id = 1;
s1->name = "张雪优";
s1->showStudent();
delete s1;
Student* s2 = new Student;
s2->setid(2);
s2->setname("浅学神");
s2->showStudent();
delete s2;
system("pause");
return false;
}

 

1.3、成员访问权限

描述:

  • 类在设计时,可以把属性和行为放在不同的权限下,加以控制

有三种访问权限:

  • public  : 公共权限
    • 成员可以在类内访问,成员也可以在类外访问到
    • 子类可以访问父类公共权限下的成员
  • protected : 受保护的权限
    • 成员可以在类内访问,成员不可以被类外访问
    • 子类可以访问父类保护权限下的成员
  • private :私有权限
    • 成员可以在类内被访问,成员不可以被类外访问
    • 子类不可以访问父类私有权限下的成员

eg:

 

#include <iostream>
#include <string>
using namespace std;
//访问权限
//3种
//1.公共权限 public 类内可以访问成员,类外也可以访问成员
//2.保护权限 protected 类内可以访问成员,类外不能访问成员 子类可以访问父类的保护成员
//3.私有权限 private 类内可以访问成员,类外不能访问成员 子类不可以访问父类的私有成员

class Myclass
{
//公共权限
public:
void set(string car, int pword, string wiife)
{
m_car = car;
password = pword;
wife = wiife;
}
void show()
{
cout << "开的车是:" << m_car << endl;
cout << "房间密码是:" << password << endl;
cout << "人妻是:" << wife << endl;
}
int data;
//受保护权限
protected:
string m_car;
int password;
//私有权限
private:
string wife;
};
int main()
{

cout << "成员访问权限控制:" << endl << endl;
Myclass* mc = new Myclass;
mc->set("兰博金额", 1234, "进空");
mc->data = 100;
mc->show();
delete mc;
system("pause");
return 0;
} 

1.4、stuct 和class的区别:

  • struct默认是公共权限
  • class默认是私有权限

 

eg:

#include <iostream>
#include <string>
using namespace std;
//定义一个结构体
struct Student
{
//默认是公共权限
string fout;
//string m_name;
int id;
int ip;


};
//定义一个类
class Teacher
{
string m_name;
int id;
};
int main()
{

std::cout << "struct和class的区别:" << endl << endl;

//创建结构体变量
Student* st = new Student;
st->fout = "sda";
st->id = 10;
//st->m_name = "山海经";
st->ip = 12;
delete st;
//实例化
Teacher* te = new Teacher;
//te->id = 12; //提示不可以访问
delete te;
std::cout << "证明如此:" << endl;
std::system("pause");

return false;
}

 

1.5、成元属性设置为私有

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

 

eg:

#include <iostream>
#include <string>
using namespace std;
//1.控制属性的读写权限

//2.检测数据的有效性

//声明一个类
class Teache
{
//提供一个公共接口,对私有属性进行读和写
public:
void setName(string name)
{
m_Name = name;
}
string getName()
{
return m_Name;
}

//判断输入的数据的有效性
void setid(int a)
{
if (a > 0 && a < 150)
{
id = a;
}
else {
id = 0;
cout << "输入的id无效!" << endl;
return;
}
}
int getid()
{
return id;
}
private:
int id;
string m_Name;
};
int main()
{

cout << "类成员属性权限设置为私有权限:" << endl << endl;
//实例化一个对象
Teache* tea = new Teache;
tea->setName("合欢散");
cout << "老师的姓名是:" << tea->getName() << endl;
tea->setid(0);
cout << "老师的编号是:" << tea->getid() << endl;
system("pause");
delete tea;

system("pause");
return 0;
}

 

1.6、封装案例

1.6.1 立方体

描述:

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

eg:

 

#include <iostream>
using namespace std;
//设计立方体
//1.设计立方体类
class Cube
{
public:
//设置接口函数(修改属性值)
void setM_l(int l)
{
m_L = l;
}
void setM_w(int w)
{
m_W = w;
}
void setM_h(int h)
{
m_H = h;
}
//设置接口函数(获取属性值)
int getM_l()
{
return m_L ;
}
int getM_w()
{
return m_W ;
}
int getM_h()
{
return m_H ;
}
//设置函数获取立方体体积
int getVolum()
{
return m_L * m_W*m_H;
}
//设置函数获取立方体面积
int getAre()
{
return m_L*m_W*2+m_H*m_W*2+m_L*m_H*2;
}
//设置函数比较两个立方体是否相等
void thanCube(Cube* cub)
{
if (cub->m_L == m_L && cub->m_W == m_W && cub->m_H == m_H)
{
cout << "相等" << endl;
}
else
{
cout << "不相等" << endl;
}
}
private:
//属性
int m_L;
int m_W;
int m_H;
};
//2.获取立方体的体积

 

//3.获取立方体的面积

 

//4.比较两个立方体是否相等

 

//5.设置立方体的长宽高

 

//6.设置全局函数比较两个立方体是否相等

 

void ThanCube(Cube* cub1, Cube* cub2)
{
if (cub1->getM_l()== cub2->getM_l() && cub1->getM_w() == cub2->getM_w() && cub1->getM_h() == cub2->getM_h())
{
cout << "相等" << endl;
}
else
{
cout << "不相等" << endl;
}
}
int main()
{

 

cout << "比较两个立方体是否相等" << endl << endl;
//实例化
Cube*cube1 = new Cube;
Cube*cube2 = new Cube;
//设置每个立方体的长宽高
cube1->setM_l(10);
cube1->setM_w(10);
cube1->setM_h(10);

 

cube2->setM_l(10);
cube2->setM_w(10);
cube2->setM_h(10);

//获取立方体的面积
cout << "立方体cube1的面积是:" << cube1->getAre() << endl;
cout << "立方体cube2的面积是:" << cube1->getAre() << endl;
//获取立方体的体积
cout << "立方体cube1的面积是:" << cube1->getVolum()<< endl;
cout << "立方体cube2的面积是:" << cube1->getVolum() << endl;
//比较两个立方体是否相等
cube1->thanCube(cube2);

 

//调用全局函数比较两个立方体是否相等
ThanCube(cube1, cube2);
delete cube1;
delete cube2;
system("pause");
return false;
}

 1.6.2、练习案例:

点和圆的关系

设计一个圆类(circle),和一个点类(point),计算电和圆的关系

  • 点在圆外
  • 点在圆上
  • 点在园内

 eg:

#include <iostream>
using namespace std;
//点和圆的关系案例
//创建点类
class Point {
public:
//设置x
void setX(int x)
{
m_X = x;
}
//获取x
int getX()
{
return m_X;
}
//设置y
void setY(int y)
{
m_Y = 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 setCenter(Point* center)
{
m_Center = center;
}
//获取圆心
Point* getCenter()
{
return m_Center;
}
private:
//半径
int m_R;
//点(圆心)
Point* 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(){
cout << "点和圆的关系:" << endl << endl;

//创建圆
Circle* c = new Circle;
c->setR(10);
Point* center1 = new Point;
center1->setX(10);
center1->setY(0);
c->setCenter(center1);
//创建点
Point* center2 = new Point;
center2->setX(10);
center2->setY(8);
//判断关系
isInCircle(c, center2);
delete c;
delete center1;
delete center2;
system("pause");
return 0;
}

 

分文件编写:

#include "07point.h"

#pragma once

 

#ifndef __POINTH__
#define __POINTH__
//创建点类
class Point {
public:
//设置x
void setX(int x);

//获取x
int getX();

//设置y
void setY(int y);

//获取y
int getY();

private:
int m_X;
int m_Y;
};
#endif //__POINTH__

 

 

07point.cpp

 

 

#include "07point.h"

 

//设置x
void Point::setX(int x)
{

 

m_X = x;
}

 

//获取x
int Point::getX()
{
return m_X;

 

}

 

//设置y
void Point::setY(int y)
{
m_Y = y;
}

 

//获取y
int Point::getY()

 

{
return m_Y;
}

 

#include "07circle.h"

#pragma once
#ifndef __07CIRCLEH__
#define __07CIRCLEH__
#include "07point.h"
class Circle {
public:
//设置半径
void setR(int r);

//获取半经
int getR();

//设置圆心
void setCenter(Point* center);

//获取圆心
Point* getCenter();

private:
//半径
int m_R;
//点(圆心)
Point* m_Center;
};

#endif //__07CIRCLEH__

 

 

#include "07circle.h"
//设置半径
void Circle::setR(int r)
{
m_R = r;
}

//获取半经
int Circle::getR()
{

return m_R;
}

//设置圆心
void Circle::setCenter(Point* center)
{
m_Center = center;
}

//获取圆心
Point* Circle::getCenter()
{
return m_Center;
}

 

#include "07isincircle.h"

#pragma once
#ifndef __ISINCIRCLEH__
#define __ISINCIRCLEH__
#include <iostream>
#include "07circle.h"
#include "07point.h"
using namespace std;
void isInCircle(Circle* c, Point* p);

#endif /__ISINCIRCLEH__

 

#include "07isincircle.h"
//判断点和圆的关系
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;
}
}

 

main.cpp

#include "07isincircle.h"
int main(){
cout << "点和圆的关系:" << endl << endl;

//创建圆
Circle* c = new Circle;
c->setR(10);
Point* center1 = new Point;
center1->setX(10);
center1->setY(0);
c->setCenter(center1);
//创建点
Point* center2 = new Point;
center2->setX(10);
center2->setY(8);
//判断关系
isInCircle(c, center2);
delete c;
delete center1;
delete center2;
system("pause");
return 0;
}

 

2、对象的的初始化和清理

构造函数、析构函数

  • 生活中我们买电子铲平都基本会有出厂设置,在某一天我们不用时会删除一些自己的信息数据的设置
  • c++中面向对象来源于生活,每个对象也都会有初始值以及对象销毁前的数据清理设置

 

2.1、构造函数和析构函数

对象的初始化和清理也是两个非常重要的安全问题

  • 一个对象或者变量没有初始化状态,对其使用,后果是未知的
  • 同样的使用完一个对象或变量,没事及时清理,也会造成一个的安全问题

*c++利用了构造函数和析构函数解决上诉问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作

*对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构函数,编译器自动提供

  • 编译器提供的构造函数和析构函数是空实现

 

构造函数:

  • 主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用

析构函数:

  • 主要作用是在于对象销毁前系统自动调用,执行一些清理的工作

 

构造函数语法

类名(){}

  • 构造函数,没有返回值也不写void
  • 函数名与类名相同
  • 构造函数可以给有参数,因此可以发生重载
  • 程序在调用对象是会自动调用构造函数,无需手动调用,而且只会调用一次

 

析构函数语法:

~类名(){}

  • 析构函数,没有返回值也不写void
  • 函数名与类名相同,在名称前加上~
  • 析构函数不可以有参数,因此不能发生重载
  • 程序在对象销毁前会自动调用析构函数,无需手动调用,而且只会调用一次

 

eg:

#include <iostream>
using namespace std;
//对象的初始化和清理
//1.构造函数 进行初始化操作
class Person
{
public:
//1.构造函数
//没有返回值 不用写void
//函数名和类名相同
//构造函数可有有参数,所以可以发生重载
//创建对象的时候,狗在函数会自动调用,而且只调用一次
Person(){
cout << "构造函数被调用!" << endl<<endl;
}
//2.析构函数 进行清理操作
//没有返回值 不用写void
//函数名和类名相同,名称前加~
//构造函数不可以有参数,所以不能发生重载
//在对象销毁的时候,析构函数会自动调用,而且只调用一次
~Person()
{
cout << "析构函数被调用!" << endl<<endl;
}
};


void test01() {

Person* per = new Person;
delete per;
}
int main()
{
cout << "构造函数和析构函数:" << endl<<endl;
//test01();
Person* per = new Person;
delete per;
system("pause");
return 0;
}

 

2.2、构造函数的分类和调用

两种分类方式:

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

三种调用方式:

  • 括号法
  • 显示法
  • 隐士转换法

 

eg:

#include "02构造函数的分类和调用.h"

#pragma once
#ifndef __02构造函数的分类和调用H__
#define __02构造函数的分类和调用H__
#include <iostream>
using namespace std;
//1.构造函数的分类和调用
//分类
//按照参数分类 无参构造(无参构造就是系统提供的默认构造函数) 、有参构造
class Person2
{
public:
//构造函数
//无参构造
Person2();
//有参构造
Person2(int a);
//拷贝构造函数
Person2( Person2* p);
//析构函数
~Person2();

void setAge(int a);
int getAge();
private:
int age;
};
#endif //__02构造函数的分类和调用H__

 

02构造函数的分类和调用.cpp

#include "02构造函数的分类和调用.h"
//构造函数
//无参构造
Person2::Person2()
{
std::cout << "Person的无参构造函数!" << endl << endl;
}
//有参构造
Person2::Person2(int a)
{
age = a;
cout << "Person的有参构造函数!" << endl << endl;
}
//拷贝构造函数
Person2::Person2( Person2* p)
{
//将传入的人的身上所有的属性,拷贝到的身上.
cout << "person的拷贝构造函数:" << endl << endl;
age = p->age;
}
//析构函数
Person2::~Person2()
{
cout << "Person的析构函数!" << endl << endl;
}

void Person2::setAge(int a)
{
age = a;
}
int Person2::getAge() {
return age;
}

 

#include "02function.h"

#pragma once
#ifndef __02FUNCTIONH__
#define __02FUNCTIONH__
#include "02构造函数的分类和调用.h"
void test02();
#endif //__02FUNCTIONH__

 

02function.cpp

#include "02function.h"
void test02()
{
Person2* per2 = new Person2;
Person2* per2_1 = new Person2(1);

per2->setAge(10);
cout << "per2年龄是:" << per2->getAge() << endl;
Person2* per2_2 = new Person2(per2);
cout << "per2_2年龄是:" << per2_2->getAge()<< endl;
delete per2;
delete per2_1;
delete per2_2;
}

 

main.cpp

#include <iostream>
#include "02function.h"
using namespace std;
int main()
{
cout << "构造函数的分类和调用:" << endl << endl;
test02();
system("pause");
return 0;
}

 

2.2、拷贝构造函数的调用时机

c++中拷贝构造函数调用通常有三种情况:

  • 使用一个已经创建好的对象来初始化一个新对象
  • 值传递的方式给函数传值
  • 以值的方式返回局部对象

 

eg:

#include "03拷贝函数的调用时机.h"

#pragma once
#ifndef __03拷贝函数的调用时机H__
#define __03拷贝函数的调用时机H__
#include <iostream>
using namespace std;

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

class Person3
{
public:
//无参构造函数
Person3();
//有参构造函数
Person3(int a);
//拷贝构造函数
Person3(Person3* person);
//析构函数
~Person3();
//设置参数接口函数
void setAge(int a);
//获取参数的接口函数
int getAge();
private:
int age3;

};

#endif //__03拷贝函数的调用时机H__

 

03拷贝函数的调用时机.cpp

#include "03拷贝函数的调用时机.h"

//无参构造函数
Person3::Person3()
{
cout << "person3的默认构造函数被调用:" << endl << endl;
}
//有参构造函数
Person3::Person3(int a)
{
age3 = a;
cout << "person3的有参构造函数被调用:" << endl << endl;
}
//拷贝构造函数
Person3::Person3(Person3* person)
{
cout << "person3的拷贝构造函数被调用:" << endl << endl;
age3 = person->age3;
}
//析构函数
Person3::~Person3()
{
cout << "person3的析构函数被调用:" << endl << endl;
}
//设置参数接口函数
void Person3::setAge(int a)
{
age3 = a;
}
//获取参数的接口函数
int Person3::getAge()
{
return age3;
}

 

#include "03function.h"

#include "03function.h"

#pragma once
#ifndef __03FUNCTIONH__
#define __03FUNCTIONH__
#include "03拷贝函数的调用时机.h"
//1.使用一个已经创建的对象初始化一个新对象
void test03();
//2.值传递的方式给函数参数传值
void test03_1(Person3 per3_1);
void test03_2();
//3.值方式返回局部变量
Person3 test03_3();
void test03_4();
#endif //__03FUNCTIONH__

 

03function.cpp

#include "03function.h"
//1.使用一个已经创建的对象初始化一个新对象
void test03()
{
//调用无参构造函数
Person3* per3= new Person3(10);
cout << "per3的年龄是:" << per3->getAge()<< endl << endl;
Person3* per3_1 = new Person3(per3);
cout << "per3_1的年龄是:" << per3_1->getAge() << endl<<endl;

delete per3;
delete per3_1;
}
//2.值传递的方式给函数参数传值
void test03_1(Person3 per3_11)
{
cout << "what?" << endl << endl;
cout << "per3_1的年龄是:" << per3_11.getAge() << endl << endl;
}
void test03_2()
{
Person3 per3_2;
per3_2.setAge(20);
test03_1(per3_2);
}

Person3 test03_3()
{
Person3 p3_3;
return p3_3;
}

void test03_4()
{
Person3 per3_4 = test03_3();
}

 

main.cpp

#include <iostream>
#include "03function.h"
using namespace std;
int main()
{
cout << "拷贝函数的调用时机:" << endl<<endl;
test03();
cout << endl << "-------------------------" << endl<<endl;
test03_2();
cout << endl << "-------------------------" << endl << endl;
test03_4();
system("pause");
return 0;
}

 

2.4、构造函数调用规则

默认情况下,c++编译器至少给一个类添加三个函数

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

构造函数调用规则如下:

  • 如果用户提供了有参构造函数,c++不会再提供默认无参构造函数,但是会提供默认拷贝构造函数
  • 如果用提供了拷贝构造函数,c++就不会再提供其他构造函数

 

eg:

#include "04构造函数调用规则.h"

#pragma once
#ifndef __04构造函数调用规则H__
#define __04构造函数调用规则H__
#include <iostream>
using namespace std;

//构造函数调用规则
//1.创建一个类,c++编译器至少提供三个函数
//构造函数 (空实现)
//析构函数 (空实现)
//拷贝构造 (值拷贝)
class Person4
{
public:
Person4();
Person4(int age);
Person4(const Person4* p);
~Person4();

void setAge(int ag);
int getAge();

private:
int m_age;
};

#endif //__04构造函数调用规则H__

 

04构造函数调用规则.cpp

#include "04构造函数调用规则.h"
Person4::Person4() {
cout << "person4 的默认构造函数调用:" << endl << endl;
}
Person4::Person4(int age) {
cout << "person4 的有参造函数调用:" << endl << endl;
m_age = age;
}
Person4::~Person4() {
cout << "person4 的析构函数调用:" << endl << endl;
}
Person4::Person4(const Person4* p) {
m_age = p->m_age;
cout << "person4 的拷贝构造函数调用:" << endl << endl;
}

void Person4::setAge(int ag) {
m_age = ag;
}
int Person4::getAge() {
return m_age;
}

 

#include "04function.h"

#pragma once
#ifndef __04FUNCTIONH__
#define __04FUNCTIONH__
#include "04构造函数调用规则.h"
void test4();
#endif //__04FUNCTIONH__

 

04function.cpp

#include "04function.h"

void test4()
{
Person4* p = new Person4;
p->setAge(20);
Person4* p2 = new Person4(p);
cout << "p2的年龄是:" << p2->getAge() << endl;

delete p;
delete p2;
}

 

main

#include <iostream>
#include "04function.h"
using namespace std;

int main()
{
cout << "构造函数调用规则:" << endl << endl;
test4();
system("pause");
return 0;
}

 

 

2.5、深拷贝与浅拷贝

深浅拷贝是面试问题

浅拷贝:

  • 简单的赋值拷贝操作

深拷贝:

  • 在堆区重新申请空间,进行拷贝操作

 

eg:

#include "05深拷贝与浅拷贝.h"

#pragma once
#ifndef __05深拷贝与浅拷贝H__
#define __05深拷贝与浅拷贝H__
#include <iostream>
#include <string>
using namespace std;
class Person5
{
public:
Person5();
Person5(int a,int b);
Person5(Person5& per);
~Person5();

int age;
int* id;

};

#endif //__05深拷贝与浅拷贝H__

 

05深拷贝与浅拷贝.cpp

#include "05深拷贝与浅拷贝.h"
Person5::Person5() {
cout << "person5 调用默认构造函数:" << endl<<endl;
}
Person5::Person5(int a,int b) {
age = a;
id = new int(b);
cout << "person5 调用有参构造函数:" << endl << endl;
}
//我们自己实现拷贝函数,解决前来拷贝
Person5::Person5(Person5& per) {
age = per.age;
//id = per.id;//编译器提供的默认拷贝构造函数所写的代码
id = new int(*per.id); //使用深拷贝 ,解决内存权限访问冲突的问题
cout << "person5 调用拷贝构造函数:" << endl << endl;
}
Person5::~Person5() {
//析构函数的用途来了 , 用来释放在堆区创建的内存
if (id != NULL)
{
delete id;
id = NULL;
}
cout << "person5 调用析构构造函数:" << endl << endl;
}

 

#include "05function.h"

#pragma once
#ifndef __FUNCTIONH__
#define __FUNCTIONH__
#include "05深拷贝与浅拷贝.h"
void test5();
#endif //__FUNCTIONH__

 

05function.cpp

#include "05function.h"
void test5() {
Person5 p5(12,12);
cout << "p5的年龄是:" << p5.age << endl;
Person5 p5_1(p5); //如果浅拷贝的话,堆区的内存已经被释放,销毁当前对象时又会去释放堆区内存
cout << "p5_1的年龄是:" << p5_1.age << endl;
}

 

main

#include <iostream>
#include "05function.h"
using namespace std;
int main()
{
cout << "深拷贝与浅拷贝:" << endl << endl;
test5();
system("pause");
return 0;
}

 

2.6、初始化列表

作用:

c++提供了初始化列表的语法,用来初始属性

语法:

构造函数():属性1(属性值),属性2(属性值),属性。。。。。{}

 

eg:

#include "06初始化列表.h"

#pragma once
#ifndef __06初始化列表H__
#define __06初始化列表H__
#include <iostream>
using namespace std;
class Person6
{
public:
//Person6(); 不够灵活
Person6(int a,int b); //领活方便
~Person6();
int getAge();
int getScore();
protected:

private:
int age;
int sorce;

};
#endif //__06初始化列表H_

 

06初始化列表.cpp

#include "06初始化列表.h"
//不够灵活
// Person6::Person6():age(12),sorce(100)
// {
// std:: cout << "默认构造函数调用" << std::endl;
// }

//领活方便
Person6::Person6(int a,int b):age(a),sorce(b)
{
std:: cout << "有参构造函数调用" << std::endl;
}
Person6::~Person6() {
std::cout << "析构函数调用" << std::endl;
}
int Person6::getAge() {
return age;
}
int Person6::getScore() {
return sorce;
}

 

#include "06function.h"

#pragma once
#ifndef __06FUNCTIONH__
#define __06FUNCTIONH__
#include "06初始化列表.h"
void test06();

#endif //__06FUNCTIONH__

 

06function.cpp

#include "06function.h"
void test06() {
//Person6 p6; //不够灵活
Person6 p6(18,100); //领活方便
cout << "p6的年纪是:" << p6.getAge() << endl;
cout << "p6的成绩是:" << p6.getScore() << endl;

}

 

main
#include "06function.h"

#include <iostream>
using namespace std;
int main()
{
cout << "构造函数初始化列表:" << endl << endl;
test06();
system("pause");
return 0;
}

 

2.7、类对象作为类成员

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

eg:

#include "07类中调用其它类成员.h"

#pragma once

#ifndef __07类中调用其他类成员H__
#define __07类中调用其他类成员H__
#include <iostream>
using namespace std;
#include <string>
//声明一个手机类
class Phone
{
public:
Phone(string name);
~Phone();
string getName();
protected:

private:
string p_Name;
};
//声明一个人类
class Person7
{
public:
Person7(string r_name,string p_Name);
~Person7();
string getName();
Phone getPhone();
protected:

private:
string r_Name;
Phone phone;
};
#endif //__07类中调用其他类成员H__

 

07类中调用其它类成员.cpp

#include "07类中调用其它类成员.h"
//手机成员函数的实现
Phone::Phone(string name) {
cout << "Phone有参构造函数调用:" << endl<<endl;
p_Name = name;
}
Phone::~Phone() {
cout << "Phone析构函数调用:" << endl << endl;
}
string Phone::getName() {
return p_Name;
}

//人类成员实现
Person7::Person7(string r_name, string p_Name):r_Name(r_name),phone(p_Name){
cout << "person有参构造函数调用:" << endl << endl;
}
Person7::~Person7() {
cout << "person析构函数调用:" << endl << endl;
}
string Person7::getName() {
return r_Name;
}
Phone Person7::getPhone() {
return phone;
}

 

#include "07function.h"

#pragma once
#ifndef __FUNCTIONH__
#define __FUNCTIONH__
#include "07类中调用其它类成员.h"
void test07();
#endif //__FUNCTIONH__

 

07function.cpp

#include "07function.h"
void test07() {
Person7 p7("孙羽","魅族1p");
cout << p7.getName() << "的手机是:" << p7.getPhone().getName()<< endl;

}

 

 

2.8、静态成员

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

静态成员分为:

  • 静态成员变量
    • 所有对象共享一份数据
    • 在编译阶段分配内存
    • 类内声明,类外初始化
  • 静态成员函数
    • 所有对象共享一个函数
    • 静态成员函数只能访问静态成员变量

 

静态成员函数访问方式:

  • 通过对象进行访问
  • 直接天通过类名进行访问

 静态成员变量访问方式:

  • 只能通过静态成员函数进行访问

 

注意:类外同样不能访问私有 的静态函数

 

eg:

#include "08静态成员.h"

#pragma once
#ifndef __静态成员H__
#define __静态成员H__
#include <iostream>
using namespace std;
class Person8
{
public:
static void func();
protected:

private:
static int age;
int a;
};

#endif //__静态成员H__

 

08静态成员.cpp

#include "08静态成员.h"
int Person8::age = 0;

void Person8::func()
{
cout << "static void func静态函数调用!" << endl << endl;
age = 100;//静态成员函数只能访问静态的成员变量
//a = 0; //静态成员函数不能访问非静态的成员变量
}

 

#include "08function.h"

#pragma once
#ifndef __FUNCTIONH__
#define __FUNCTIONH__
#include "08静态成员.h"
void test08();

#endif //__FUNCTIONH__

 

08function.cpp

#include "08function.h"
void test08() {
//静态成员函数访问方式:
//1.通过对象进行访问
Person8 p8;
p8.func();

//2.直接通过类名进行访问
Person8::func();

}

 

main

#include <iostream>
#include "08function.h"
using namespace std;
int main()
{

cout << "静态成员;" << endl << endl;
test08();
system("pause");
return 0;
}

 

3、c++对象模型和this指针

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

  • 在c++中,类内的成员变量和成员函数分开存储
  • 只有非静态成员变量才属于类的对象上
  • c++编译器会为每一个空对象也分配一个字节的内存空间,是为了区分空对象占内存的位置
  • 空对象占用内存空间为:1 个字节
  • 静态成员变量 不属于类对象上
  • 非静态成员函数 不属于类对象上
  • 静态成员函数  不属于类对象上

 

3.2、this指针概念

每一个非静态成员函数只会有一份函数实例,也就是说多个同类型的对象会公用一块代码

问题?这一个代码区是如何区分哪个对象在调用自己呢?

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

 

this指针的用途:

  • 当形参和成员变量同名时,可用this指针来区分
  • 在类的非静态成员函数中返回对象本身,可使用return *this

 

eg:

#include "01this指针的使用.h"

#pragma once
#ifndef __01THIS指针的使用H__
#define __01THIS指针的使用H__
#include <iostream>
using namespace std;
class Person1
{
public:
Person1();
~Person1();
void setAge(int age);
int getAge();
Person1& ageNum();
protected:

private:
int age;
};

#endif //__01THIS指针的使用H__

 

01this指针的使用.cpp

#include "01this指针的使用.h"
Person1:: Person1(){
cout << "默认构造函数调用!" << endl << endl;
}
Person1::~Person1() {
cout << "析构函数调用!" << endl << endl;
}
void Person1::setAge(int age) {
//形参名 和 变量名一样时,默认是将形参 赋值给形参
//age = age;
//可以使用this解决此问题
this->age = age;

}
int Person1::getAge() {
return age;
}
Person1& Person1::ageNum() {
this->age += 10;
return *this;
}

 

#include "01function.h"

#pragma once
#ifndef __FUNCTIONH__
#define __FUNCTIONH__
#include "01this指针的使用.h"
void test01();

#endif //__FUNCTIONH__

 

01function.cpp

#include "01function.h"
void test01() {
Person1 p1;
p1.setAge(12);
cout << "p1的年龄是:" << p1.getAge() << endl << endl;
p1.ageNum().ageNum();
cout << "p1的年龄是:" << p1.getAge() << endl << endl;

}

 

main

#include <iostream>
#include "01function.h"
using namespace std;
int main()
{

cout << "this指针的使用:" << endl<<endl;
test01();
system("pause");
return 0;
}

 

3.3、空指针访问成员函数

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

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

 

eg:

#include "02空指针访问成员函数注意事项.h"

#pragma once
#ifndef __02空指针访问成员函数注意事项H__
#define __02空指针访问成员函数注意事项H__
#include <iostream>
#include <string>
using namespace std;
class Person2
{
public:
Person2();
~Person2();
void printClassName();
void printPersonName();
void setPersonName( string name);
protected:

private:
string m_Name;
};

#endif //__02空指针访问成员函数注意事项H__

 

02空指针访问成员函数注意事项.cpp

#include "02空指针访问成员函数注意事项.h"
Person2::Person2() {
cout << "默认构造函数调!" << endl<<endl;
}
Person2::~Person2() {
cout << "析构函数调!" << endl;
}
void Person2::printClassName() {
cout << "类的名字是:" << "person" << endl<<endl;
}
void Person2::printPersonName() {
//空指针不能访问成员属性,因为成员属性属于对象上的,但是指针对象是空的,所以不能进行访问
//可以加一个判断,如果只针对象是空就退出函数,提高代码的健壮性
if (this == NULL)
{
return;
}
cout << "对象的名字是:" << m_Name << endl<<endl;
}
void Person2::setPersonName(string name) {
m_Name = name;
}

 

#include "02function.h"

#pragma once
#ifndef __02FUNCTIONH__
#define __02FUNCTIONH__
#include "02空指针访问成员函数注意事项.h"
void test02();
#endif /__02FUNCTIONH__

 

02function.cpp

#include "02function.h"
void test02() {

Person2* p2 = NULL;
p2->printClassName();
p2->printPersonName();
}

 

 

main

#include <iostream>
#include "02function.h"
using namespace std;
int main()
{
cout << "空指针访问成员函数:" << endl << endl;
test02();
system("pause");
return 0;
}

 

3.4、const修饰成员函数

常函数:

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

常对象:

  • 声明对象前加const称该对象为常对象
  • 常对象只能调用常函数

 

 

 

 

3.4、友元

  • 生活中你的家里有客厅(public),有你的卧室(private)
  • 客厅所有的来客都可以进去,但是你的卧室是私有的,也就是说只有你能进去
  • 但是呢,你也可以允许你的好朋友进去。

 

  • 在程序里,有些私有属性,也想让类外一些特殊函数或者类进行访问,就需要用到友元技术
  • 友元的目的,就是让一个函数或者类访问另一个类中的私有成员

友元关键字:friend

友元的三种实现:

  • 全局函数做友元
  • 类做友元
  • 成员函数做友元

注意:此处需要注意一点:类内友元、类内成员函数友元、或类内创建另一个类的指针对象时,必须先有这这个类,或先声明这个类之后在定义,保存程序不出错 

eg1:

全局函数做友元:

#include "01全局函数做友元.h"

#pragma once
#ifndef __01全局函数做友元H__
#define __01全局函数做友元H__
#include <iostream>
#include <string>
using namespace std;
class Building
{//goodGay全局函数是 Builing好朋友,可以访问building中的私有成员
friend void goodGay(Building* building);
public:
Building();
~Building();

public:
string m_SittingRoom; //客厅

protected:
string m_BedRoom;//卧室
private:
};

#endif //__01全局函数做友元H__

 

01全局函数做友元.cpp

#include "01全局函数做友元.h"
Building::Building()
{
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
cout << "默认构造函数调用:" << endl << endl;
}
Building::~Building() {
cout << "析构函数调用:" << endl << endl;
}

 

#include "01function.h"

#pragma once
#ifndef __01FUNCTIONH__
#define __01FUNCTIONH__
#include "01全局函数做友元.h"
//全局函数 做友元
void goodGay(Building* building);
void test01();
#endif // __01FUNCTIONH__

 

01function.cpp

#include "01function.h"
void goodGay(Building* building) {
cout << "全局函数 在整访问:" << building->m_SittingRoom << endl;
cout << "全局函数 在整访问:" << building->m_BedRoom << endl;
}
void test01() {
Building building;
goodGay(& building);
}

 

main

#include <iostream>
#include "01function.h"
using namespace std;
int main()
{
cout << "全局函数做友元:" << endl << endl;
test01();
system("pause");
return 0;
}

 

 

eg2:

类做友元:

#include "02类做友元.h"

#pragma once
#ifndef __02类做友元H__
#define __02类做友元H__
#include <iostream>
#include <string>
using namespace std;

//声明一个手机类
class Phone
{
//在手机类中,声明人类是友元类,就可以在人类中调用手机的私有方法或属性
friend class Person;
public:
Phone();
~Phone();
protected:

private:
string p_Name;

};
//声明一个人类
class Person
{

public:
Person();
~Person();
void setName();
string getName();
void getPhone();
protected:

private:
string m_Name;
Phone* phone;
};
#endif //__02类做友元H__

 

02类做友元.cpp

#include "02类做友元.h"
//手机类函数实现
Phone::Phone() {
cout << "手机默认构造函数调用!" << endl << endl;
p_Name = "iPhone12pro";
//初始化对象时:给手机起个名字
}
Phone::~Phone() {
cout << "手机析构函数调用!" << endl << endl;

}

//人类实现
Person::Person() {
cout << "人类默认构造函数调用!" << endl << endl;
phone = new Phone;
m_Name = "华天度";
//初始化人时:给人起个名
}
Person::~Person() {
cout << "人类析构函数调用!" << endl << endl;
delete phone;
}
void Person::setName() {

}
string Person::getName() {
return m_Name;
}
void Person::getPhone() {

cout << ",他的手机是:"<<phone->p_Name <<endl;
//在人类成员函数内,调用手机类私有成员函数下的手机名
}

 

#include "02function.h"

#pragma once
#ifndef __02FUNCTIONH__
#define __02FUNCTIONH__
#include "02类做友元.h"
void test02();

#endif //__02FUNCTIONH__

 

02function.cpp

#include "02function.h"
void test02() {

Person p2;
cout << "这个人叫:" << p2.getName() << endl;
p2.getPhone();
}

 

main

#include <iostream>
#include "02function.h"
using namespace std;
int main()
{
cout << "类做友元:" << endl << endl;
test02();
system("pause");
return 0;
}

 

 

 

eg3:

 

成员函数做友元:

 

#include "03成员函数做友元.h"

#pragma once
#ifndef __03成员函数做友元H__
#define __03成员函数做友元H__
#include <iostream>
#include <string>
using namespace std;

//此处需要注意一点:类内友元、类内成员函数友元、或类内创建另一个类的指针对象时,必须先有这这个类,或先声明这个类之后在定义,保存程序不出错
class Phone2;

//人类
class Person2
{

public:
Person2();
~Person2();
void printPhone();
void tetss();
protected:

private:
string rName;
Phone2* phone2;
};

//声明手机类
class Phone2
{
friend void Person2::printPhone();
//friend void Person2::tetss();
public:
Phone2();
~Phone2();
protected:

private:
string Pname;
};

#endif //__03成员函数做友元H__

 

03成员函数做友元.cpp

#include "03成员函数做友元.h"
//手机类成员函数实现
Phone2::Phone2() {
cout << "手机 调用默认构造函!" << endl << endl;
Pname = "摩托罗拉";
}
Phone2::~Phone2() {
cout << "手机 析构构造函!" << endl << endl;
}

//人类成员数实现
Person2::Person2() {
cout << "人类 调用默认构造函!" << endl << endl;
phone2 = new Phone2;
rName = "月不穷";
}
Person2::~Person2() {
cout << "人类 析构构造函!" << endl << endl;
delete phone2;
}
void Person2::printPhone() {
//在人类成员函数下,访问手机类的私有权限的手机 名
phone2->Pname;
cout << "手机名是:" << phone2->Pname << endl;
}
void Person2::tetss() {

}

 

 

#include "03function.h"

#pragma once
#ifndef __03FUNCTIONH__
#define __03FUNCTIONH__
#include "03成员函数做友元.h"
void test03();
#endif //__03FUNCTIONH__

 

03function.cpp

#include "03function.h"
void test03() {

Person2 pp;
pp.printPhone();
}

 

main

#include <iostream>
#include "03function.h"
using namespace std;
int main()
{
cout << "成员函数做友元:" << endl << endl;
test03();
system("pause");
return 0;
}

 

 

3.5、运算符重载  (跳过了)

 

3.6、继承

继承是面向对象三大特性之一

有些类与类之间存在特殊关系,我们发现,定义这些类时,下级别的成员除了拥有上一级的共性,还有自己的特性。

这个时候我们就可以考虑利用继承的技术,减少重复代码

 

3.6.1、继承的基本语法:

class 子类名称 :  继承权限 父类名称 {}

 

eg:

 

#include "01继承的基本语法.h"

#pragma once
#ifndef __01继承的基本语法H__
#define __01继承的基本语法H__
#include <iostream>
using namespace std;
//普通页面实现
//java页面
class BasePage
{
public:
BasePage();
~BasePage();
void header();
void footer();
void left();

public:

};


//java页面
class Java :public BasePage
{
public:
Java();
~Java();
public:
void java();
};
//python页面
class Python :public BasePage
{
public:
Python();
~Python();
public:
void python();
};
//c++页面
class Cpp :public BasePage
{
public:
Cpp();
~Cpp();
public:
void cpp();
};
#endif //__01继承的基本语法H__

 

 01继承的基本语法.cpp

#include "01继承的基本语法.h"
BasePage::BasePage() {
cout << "BasePage的默认构造函数:" << endl<<endl;
}
BasePage::~BasePage() {
cout << "BasePage的析构造函数:" << endl << endl;
}
void BasePage::header() {
cout << "首页、公开课、登录(公共头部)" << endl << endl;
}
void BasePage::footer() {
cout << "帮助中心、交流合作、站内地图(公共底部)" << endl << endl;
}
void BasePage::left() {
cout << "java、Python、c++(公共分类列表)" << endl << endl;
}
//java页面
Java::Java() {
cout << "java的默认构造函数调用" << endl;
}
Java::~Java() {
cout << "java的析构函数调用" << endl;
}
void Java::java() {
cout << "java视频页面" << endl;
}
//python页面
Python::Python() {
cout << "Python的默认构造函数调用" << endl;
}
Python::~Python() {
cout << "Python的析构函数调用" << endl;
}
void Python::python() {
cout << "Python视频页面" << endl;
}
//c++页面
Cpp::Cpp() {
cout << "Cpp的默认构造函数调用" << endl;
}
Cpp::~Cpp() {
cout << "Cpp的析构函数调用" << endl;
}
void Cpp::cpp() {
cout << "cpp视频页面" << endl;
}

 

#include "01function.h"

#pragma once
#ifndef __01FUNCTIONH__
#define __01FUNCTIONH__
#include "01继承的基本语法.h"
void test01();


#endif /__01FUNCTIONH__

 

01function.cpp

#include "01function.h"
void test01() {
cout << "java视频界面" << endl;
Java java;
java.header();
java.footer();
java.left();
java.java();
Python python;
python.header();
python.footer();
python.left();
python.python();
Cpp cpp;
cpp.header();
cpp.footer();
cpp.left();
cpp.cpp();
}

 

 

mian

#include <iostream>
#include "01function.h"
using namespace std;
int main()
{
cout << "继承的基本语法:" << endl;
test01();
system("pause");
return 0;
}

 

3.6.2、继承方式

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

继承方式一共有三种:

  • 公共继承
    • 不能继承父类的私有成员,其他权限成员原样继承,父类的公共成员依旧在子类的公共的成员下,父类受保护的成员依旧在子类的受保护成员下
  • 保护继承
    • 不能继承父类的私有成员,父类的其他权限的成员,全部继承到子类的受保护成员下
  • 私有继承
    • 不能继承父类私有成员,父类的其他权限成员,全部进程到子类的私有成员下

 

 

 

 

eg:

#include "02继承方式.h"

#pragma once
#ifndef __02继承方式H__
#define __02继承方式H__
#include <iostream>
using namespace std;
//声明父类
class Father
{
public:
Father();
~Father();
public:
int m_A;
protected:
int m_B;
private:
int m_C;

};
//声明子类继承方式
//1.public继承
class Son1 :public Father
{
public:
Son1();
~Son1();
};
//2.protected 继承
class Son2 :protected father
{
public:
Son2();
~Son2();
};
//3.private 继承
class Son3 :private father
{
public:
Son3();
~Son3();
};
#endif //__02继承方式H__

 

02继承方式.cpp

#include "02继承方式.h"
Father::Father() {
cout << "调用父亲的默认构造函数" << endl;
}
Father::~Father()
{
cout << "调用父亲析构函数" << endl;
}

Son1::Son1() {
cout << "调用1儿子的默认构造函数" << endl;
}
Son1::~Son1() {
cout << "调用1儿子析构函数" << endl;
}

Son2::Son2() {
cout << "调用2儿子的默认构造函数" << endl;
}
Son2::~Son2() {
cout << "调用2儿子析构函数" << endl;
}

Son2::Son2() {
cout << "调用3儿子的默认构造函数" << endl;
}
Son2::~Son2() {
cout << "调用3儿子析构函数" << endl;
}

 

#include "02function.h"

#pragma once
#ifndef __02FUNCTIONH__
#define __02FUNCTIONH__
#include "02继承方式.h"
void test02();
#endif //__02FUNCTIONH__

 

02function.cpp

#include "02function.h"
void test02() {

Son1 son1;
son1.m_A; //继承父亲共有成员,可以进行访问

//其他两个儿子,没写,因为继承到保护成员下,和私有成员下,不能进行访问
}

 

 

mian

#include <iostream>
#include "02function.h"
using namespace std;
int main()
{

cout << "继承方式:" << endl<<endl;

system("pause");
return 0;
}

 

3.6.3、继承中的对象模型

问?

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

答案:

  • 父类中所有的非静态的成员属性都会被子类继承下去
  • 父类中私有成员属性 是被编译器给隐藏了,因此访问不到,但是确实是被继承了

 

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

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

问?

父类和子类的构造和析构顺序是谁先谁后

答案:

构造时: 先有父类构造,再有子类构造

析构时:先有子类析构,再有父类析构

 

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

问题?:

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

  • 访问子类同名成员,字节访问即可
  • 访问父类同名成员,需要加作用域

 

 

 

    

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

问?

继承中同名的静态成员在子类对象上如何进行访问?

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

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

 

 

 

3.6.7、多继承语法

c++允许一个子类继承多个父类

语法:
class 子类 :继承方式 父类1,继承方式 父类2 .。。。。{}

 

多继承可能引发父类中同名成员出现,需要加作用域区分

 

 

3.6.8、菱形继承

菱形继承概念:

两个派生类继承同一个基类

又有某个类同时继承两个派生类

这种继承被称为菱形继承,或者砖石继承

 

问题;

1.羊继承了动物数据,驼同样继承了动物的数据,当羊驼使用数据时,就会产生二义性

2.羊驼继承自动物的数据继承了两份,其实我们应该清楚,这分数据我们只需要一份既可以

 

 

  • 3.7、多态
  • 3.7.1、多态的基本概念

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

多态分为两类

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

静态多态和动态区别:

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

满足多态的条件:

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

多态使用条件:

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

重写要求:

子类 函数返回值类型、函数名、参数列表、完全和父类一致称为重写

 

 

 

 

虚函数深入刨析:

 

 

 

 

 

 

 

 

3.7.2、多态案例——计算器类

分别用普通技术和多态技术,设计实现两个操作数进行运算的计算器类

多态的优点:

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

 

#include <iostream>
using namespace std;
//基类 Person
class Person
{
public:
virtual int calc()
{
return 0;
}
public:
int m_A;
int m_B;
protected:

private:
};

//派生类1
class LiMing :public Person
{
public:
int calc()
{

cout << "m_A + m_B" << endl;
return m_A + m_B;
}
};
//派生类2
class ZiMing :public Person
{
public:
int calc()
{
cout << "m_A * m_B" << endl;
return m_A * m_B;

}
};
void calcc(Person* per) {

cout << per->calc() << endl;
}
void test03() {
Person* p3 = new ZiMing;
p3->m_A = 10;
p3->m_B = 20;
calcc(p3);
delete p3;
}
int main()
{

cout << "虚函数的案例——两个操作数运算:" << endl << endl;
test03();
system("pause");
return 0;
}

 

3.7.3、纯虚函数和抽象类

在多态中,通常父类中虚函数的实现是没有意义的,主要是调用子类重写的内容,因此可以将虚函数改为纯虚函数

语法:

  • virtual 返回值类型 函数名 (参数列表)= 0 ;

当类中有了这个纯虚函数后,这个类也称为抽象类

抽象类的特点:

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

意义:

  • 纯虚函数的意义,由父类创建指针,指向不同的子类对象,实现不同的功能调用。
  • 这也是多态的一种体现,虚函数好比提前放置了一个接口,方便以后调用这个接口实现不同的功能

.eg:

#include <iostream>
using namespace std;

class Base
{
public:
//纯虚函数
//只要有一个纯虚函数,这个类称谓抽象类
//抽象类特点:
//1.无法实例化对象
//2.抽象类的子类 必须重写父类的抽象类,否则子类也属于抽象类
virtual void func() = 0;
};
class Son :public Base
{
public:
//子类必须重写父类的虚函数
void func() {
cout << "子类重写虚函数" << endl;
}


};
void test001()
{
//Base b; //不允许抽象类实例化对象(栈区)
//Base* c = new Base; //不允许抽象类实例化对象(堆区)
Son son;//子类必须重写父类的虚函数,否则也是抽象类,不能实例化对象
Base* base = new Son; //父类的指针指向子类的地址
base->func();
delete base;
}
int main()
{
cout << "纯虚函数和抽象类:" << endl << endl;
test001();
system("pause");
return 0;
}

 

//纯虚函数的意义,由父类创建指针,指向不同的子类对象,实现不同的功能调用。
//这也是多态的一种体现,虚函数好比提前放置了一个接口,方便以后调用这个接口实现不同的功能

 

3.7.4、多态案例——制作饮品

案例描述:

制作饮品的流程大致分为:煮熟 - 冲泡 - 倒入杯中 - 加入辅料

利用多态技术实现案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶

 

多态含义:

一个接口由于传入的对象不同有多种形态。

 

eg:

#include <iostream>
using namespace std;
class Base
{
public:
void boilWater()
{
cout << "第一步:开始烧水!" << endl;
}
virtual void brew() = 0; //第二部:开始冲泡具体东西,咖啡或则茶叶
void pourInCup() {
cout << "第三步:倒入杯中!" << endl;
}
virtual void add() = 0; // 第四步:添加辅料
void make()
{
boilWater();
brew();
pourInCup();
add();
}
};
class Coffee :public Base
{
public:
void brew()
{
//第二部:开始冲泡具体东西,咖啡或则茶叶
cout << "第二步:开始冲泡咖啡!" << endl;
}
void add()
{
// 第四步:添加辅料
cout << "第四步:添加糖和牛奶!" << endl;
}
};
class Tea :public Base
{
public:
void brew()
{
//第二部:开始冲泡具体东西,咖啡或则茶叶
cout << "第二步:开始冲泡茶叶!" << endl;
}
void add()
{
// 第四步:添加辅料
cout << "第四步:添加柠檬!" << endl;
}
};

void test05(Base* object)
{
object->make();

delete object;
}
void test05_1()
{
test05(new Coffee);
cout << "-------------------" << endl;
test05(new Coffee);
}

int main()
{
cout << "多态案例二—制作饮品:" << endl << endl;
test05_1();


system("pause");
return 0;
}

 

3.7.5、虚析构和纯虚析构

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

解决方法:

将父类中的析构函数改为虚析构或者纯虚析构

 

虚析构和纯虚析构共性:

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

虚析构和纯虚析构区别:

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

 

虚析构语法

virtual ~类名(){}

纯虚析构语法:

virtual ~类名 ()=0; 这句话在类内声明

virtual ~类名 (){}  ; 这句话在类外实现

 

 

eg:

#include <iostream>
#include <string>
using namespace std;
//虚析构和纯虚析构
class Aniamal
{
public:
Aniamal() {
cout << "animal构造函数调用" << endl;
}
//父类析构变为虚析构后,父类在析构时就能执行子类析构函数了
//虚析构
// virtual ~Aniamal()
// {
// cout << "animal析构函数调用!" << endl;
// }
//纯虚函数
virtual void speak() = 0;
//纯虚析构
//类内声明纯虚析构,在类外实现
//有了纯虚析构之后,这个类就属于抽象类,无法实例化对象
virtual ~Aniamal() = 0;

};
Aniamal::~Aniamal()
{
cout << "animal析构函数调用!" << endl;
}
class Cat :public Aniamal
{
public:
Cat(string name) {
cout << "cat构造函数调用!" << endl;
m_Name = new string(name);
}
void speak() {
cout << *m_Name<< "小猫在说话" << endl;
}
~Cat()
{
cout << "cat析构函数调用!" << endl;
if (m_Name != NULL)
{
delete m_Name;
m_Name = NULL;
}
}
string * m_Name;
};

void test06_1(Aniamal * animal)
{
animal->speak();
//父类指针在析构时后,不会调用子类中析构函数,导致子类如果有堆区数据,出现内存泄漏
delete animal;
}
void test06()
{
// Aniamal * animal = new Cat("Tom");
// animal->speak();
// delete animal;
test06_1(new Cat("Tom"));
}
int main()
{
cout << "虚析构和纯虚析构练习:" << endl << endl;
test06();
system("pause");
return 0;
}

 

总结:

1.虚析构或纯虚析构就是用来解决通过父类指针释放子类对象

2.如果子中没有在堆区的数据,可以不用写虚构函数或纯虚析构

3.拥有纯虚析构函数的类也称为抽象类,不能实例化对象

 

 3.7.6、多态案例三——但脑组装

案例描述:

电脑主要组成部件为cpu 用于计算,显卡(用于显示),内存条(用于存储),

将每个零件封装出抽象基类,并且提供不同的厂商生产的不同的零件,例如Intel厂商的Lenovo厂商,

创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口

测试时组装三台不同的电脑尽心工作

 

posted @ 2022-04-16 22:48  雾枫  阅读(72)  评论(0编辑  收藏  举报