类中的 属性和行为 统一称为成员
属性 又称为 成员属性 成员变量
行为 成员函数 成员方法
一 封装
1 圆类
//&&&&&&&&&&&&&&&&&&&&&& 1.封装
// 设计一个圆类,求圆的周长
//周长:2*pi*R
class Circle
{
//访问权限
public:
//属性
int m_r;
//行为
//获取圆的周长
double claculateZC()
{
return 2 * 3.14 * m_r;
}
};
int main()
{
Circle C1;
C1.m_r = 10;
cout << "zc is " << C1.claculateZC() << endl;
return 0;
}
2 学生类
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std
案例2 :学生类
class Student
{
public:
int m_num;
string m_name;
void Show()
{
cout << "ID: " << m_num << endl;
cout << "name : " << m_name << endl;
}
void setName(string name)
{
m_name = name;
}
};
int main()
{
Student s1;
s1.m_num = 1;
s1.setName("zhangsan");
s1.Show();
Student s2;
s2.m_num = 2;
s2.m_name = "lisi";
s2.Show();
return 0;
}
3 访问权限
//访问权限
//三种
//公共权限 成员 类内可以访问 ,类外可以访问
//保护权限 成员 类内可以访问 ,类外不可以访问 ,儿子可以访问父亲的保护内容
//私有权限 成员 类内可以访问 ,类外不可以访问 ,儿子不可以访问父亲的私有内容
所谓不能访问,是指不仅仅不能修改,连调用都不可以
4. struct 和 class 的区别 ,唯一的区别是默认的访问权限是不同的
// struct 和 class 的区别
//唯一的区别是默认的访问权限是不同的
//struct默认的访问权限是公有
//class 默认的访问权限是私有
class C1
{
int m_a;
};
struct C2
{
int m_a;
};
int main()
{
C1 c1;
//c1.m_a = 100; //不可访问
C2 c2;
c2.m_a = 100;
return 0;
}
5 成员设置为私有的好处
//成员设置为私有的好处
//1.可以控制读写权限
class Person
{
public:
//姓名原本是私有,类外根本访问不到,通过函数接口,来间接的修改
void setName(string name) //写
{
m_name = name;
}
string getName() //读
{
return m_name;
}
//希望年龄是只读,那就只提供给一个读的接口
int getAge() //读
{
m_age = 20;
return m_age;
}
void setLover(string lover)
{
m_lover = lover;
}
private:
//可以自行设置权限
string m_name;
int m_age;
string m_lover;
};
int main()
{
Person p1;
p1.setName("zhangsan");
cout << "name = " << p1.getName() << endl;
cout << "age = " << p1.getAge() << endl;
p1.setLover("baby");
return 0;
}
6. 案例1 立方体类
// 案例 :立方体
//求出立方体的面积和体积
//分别用全局函数和成员函数来判断两个立方体是否相等
class Cube
{
public:
void setWidth(int width)
{
m_width = width;
}
int getWidth()
{
return m_width;
}
void setHeight(int height)
{
m_height = height;
}
int getHeight()
{
return m_height;
}
void setLenght(int length)
{
m_lenght = length;
}
int getLenght()
{
return m_lenght;
}
int getArea()
{
return 2 * (m_width * m_height + m_width * m_lenght + m_lenght * m_height);
}
int getVolumn()
{
return m_width * m_height * m_lenght;
}
bool isSame(Cube c2)
{
if (c2.getHeight() == m_height && c2.getLenght() == m_lenght && c2.getWidth() == m_width)
return true;
else
return false;
}
private:
int m_width;
int m_height;
int m_lenght;
};
bool isSameOrNot(Cube c1, Cube c2)
{
if (c2.getHeight() == c1.getHeight() && c2.getLenght() == c1.getLenght() && c2.getWidth() == c1.getWidth())
return true;
else
return false;
}
int main()
{
Cube c1;
c1.setHeight(10);
c1.setLenght(10);
c1.setWidth(10);
cout << "the area : " << c1.getArea() << endl;
cout << "the volumn : " << c1.getVolumn() << endl;
Cube c2;
c2.setHeight(10);
c2.setLenght(10);
c2.setWidth(10);
cout << "************ 全局函数判断两个立方体是否一样" << endl;
bool ret = isSameOrNot(c1, c2);
if (ret)
{
cout << "same" << endl;
}
else
{
cout << "not same" << endl;
}
cout << "************ 成员函数判断两个立方体是否一样" << endl;
bool ret1 = c1.isSame(c2);
if (ret)
{
cout << "same" << endl;
}
else
{
cout << "not same" << endl;
}
return 0;
}
7 案例2 圆和点的位置关系 一个类可以作为另一个类的成员
//案例:判断点和圆的关系
class Point
{
public:
void setX(int x)
{
m_x = x;
}
double getX()
{
return m_x;
}
void setY(int y)
{
m_y = y;
}
double getY()
{
return m_y;
}
private:
double m_x;
double m_y;
};
class Circle
{
public:
void setR(int r)
{
m_r = r;
}
double getR()
{
return m_r;
}
void setCenter(Point center)
{
m_center = center;
}
Point getCenter()
{
return m_center;
}
public:
void judgeRelation()
{
}
private:
double m_r;
Point m_center; //这里其实就是创建了一个点的类对象
};
void isInCircle(Circle &c ,Point &p)
{
int distance;
distance = (c.getCenter().getX() - p.getX()) * (c.getCenter().getX() - p.getX()) +
(c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY());
int rSquare = c.getR() * c.getR();
if (distance > rSquare)
{
cout << "out" << endl;
}
else if (distance < rSquare)
{
cout << "in" << endl;
}
else
{
cout << "on" << endl;
}
}
int main()
{
Circle c;
c.setR(10);
Point center;
center.setX(10);
center.setY(0);
c.setCenter(center);
Point p;
p.setX(10);
p.setY(20);
isInCircle(c, p);
return 0;
}
二 对象特性
1.构造函数和析构函数实现类的初始化和清理
class Person
{
public:
//1.构造函数实现初始化
Person()
{
cout << "构造函数" << endl;
}
//2.析构函数实现清理
~Person()
{
cout << "析构函数" << endl;
}
};
int main()
{
Person p1;
return 0;
}
2.构造函数的分类和调用
//
//1分类
//按照参数分类 无参构造函数(默认构造) 和 有参构造函数
//按照类型分类 拷贝构造 普通构造
class Person
{
public:
Person()
{
cout << "无参构造函数" << endl;
}
Person(int a)
{
m_age = a;
cout << "有参构造函数" << endl;
}
//拷贝构造
Person(const Person &p)
{
//将传入的人的所有属性,拷贝到当前的类中
m_age = p.m_age;
cout << "拷贝构造" << endl;
}
~Person()
{
cout << "析构函数" << endl;
}
int m_age;
};
//构造函数的调用
void test()
{
Person p1; //什么都不做,就会调用默认构造函数
//注意1,调用默认构造的时候不要写(),不会执行任何东西,因为编译器会认为下面语句是一个函数声明,不会认为在创建对象
//Person p();
//void func(); 两者类似,可以认为上面一行语句是函数声明
//1.括号法
Person p2(10); //括号法调用有参构造函数
Person p3(p2); //括号法调用拷贝构造函数
cout << "p2 age is " << p2.m_age << endl;
cout << "p3 age is " << p3.m_age << endl;
//2.显示法
Person p4 = Person(10); //显示法调用有参构造函数
Person p5 = Person(p4); //显示法调用有拷贝构造函数
//注意2:单独的一个语句 Person (10); 左侧没有接收,称为匿名对象,匿名对象的特点是当前行执行结束之后,系统立即释放
//注意3:不要用拷贝构造函数来初始化一个匿名对象
//Person(p5); 运行显示 Person p5重定义 因为 Person(p5) === Person p5,是一对象声明
//3.隐式转换法
Person p6 = 100; //相等于 Person p6 = Person(10);
Person p7 = p6; //隐式转换法 调用拷贝构造函数
}
int main()
{
test();
return 0;
}```
## 3.拷贝构造函数的使用时机
```cpp
//拷贝构造函数的调用时机
class Person
{
public:
Person()
{
cout << "无参构造函数" << endl;
}
Person(int a)
{
m_age = a;
cout << "有参构造函数" << endl;
}
//拷贝构造
Person(const Person &p)
{
//将传入的人的所有属性,拷贝到当前的类中
m_age = p.m_age;
cout << "拷贝构造" << endl;
}
~Person()
{
cout << "析构函数" << endl;
}
int m_age;
};
//1.使用一个已经创建完毕的对象来初始化一个新的对象
void test()
{
Person p1(20);
Person p2(p1);
}
//2.值传递的方式给函数参数传值
void doWork(Person p)
{
}
void test02()
{
Person p;
doWork(p);//形参传值给实参是拷贝之后才传递的
}
//3.值方式返回局部对象
Person doWork2()
{
Person p; //局部变量,函数调用结束就释放了
return p; //但是这里return 的是p的副本,调用了拷贝的新的p,不是创建的对象本身
}
void test03()
{
Person p = doWork2();
}
int main()
{
test03();
return 0;
}
3 构造函数的调用规则
//构造函数的调用规则
//默认情况下,c++会至少给一个类添加3个函数
//1.默认提供一个无参构造函数
//2.默认提供一个无参析构
//3.默认提供拷贝构造,对属性进行值拷贝
4 深拷贝与浅拷贝
1.浅拷贝
class Person
{
public:
Person()
{
cout << "无参构造函数" << endl;
}
Person(int age ,int height)
{
m_age = age;
m_height = new int(height);
cout << "有参构造函数" << endl;
}
//拷贝构造
Person(const Person &p)
{
//将传入的人的所有属性,拷贝到当前的类中
m_age = p.m_age;
cout << "拷贝构造" << endl;
}
~Person()
{
if (m_height != NULL)
{
delete m_height;
m_height = NULL;
}
cout << "析构函数" << endl;
}
int m_age;
int* m_height; //将这个数据开辟再堆区
};
void test01()
{
Person p(18, 160);
cout << "p.age = " << p.m_age << " p.m_height= " << *p.m_height << endl;
Person p1(p);
cout << "p1.age = " << p1.m_age << " p1.m_height= " << *p1.m_height << endl;
}
int main()
{
test01();
return 0;
}
上面的程序回报错,显示地址冲突,原因如下:
浅拷贝存在的问题,栈内的数据,先进后出,如果先创建了P1类对象,然后创建了P2类对象,那么在程序执行完之后,类会自己调用析构函数,释放开辟的栈区的内存空间,调用析构的时候,先调用的是P2的析构,后进先出。
但是由于浅拷贝,是完全的将一个类里面的东西,包括地址完全的拷贝过来。在释放的时候,P2先释放,将存放m_height的地址空间先释放了,然后P1回再一次调用析构释放他的内存空间,但是这个内存空间已经被释放了,在这里又对同一个内存空间重复释放
2.深拷贝
什么时候需要用到深拷贝:
当在堆区创建数据时,就需要自己来提供一个深拷贝的拷贝函数
//自己实现拷贝构造,解决浅拷贝的问题,也就是深拷贝,开辟一个新的内存,来存放堆区的数据
Person(const Person& p)
{
m_age = p.m_age;
m_height = new int(*p.m_height);
cout << "拷贝构造" << endl;
}
5 初始化列表也可以实现属性的初始化
1.传统的初始化操作,在构造函数中初始化
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;
};
2.列表初始化
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;
};
6 一个类作为另一个类的成员
class Phone
{
public:
Phone(string pName)
{
m_phoneName = pName;
}
string m_phoneName;
};
class Person
{
public:
Person(string name,string pName):m_name(name), m_phone(pName)
{
}
string m_name;
Phone m_phone;
};
void test()
{
Person p("zhang san ", "max");
cout << p.m_name << " have " << p.m_phone.m_phoneName << endl;
}
int main()
{
test();
return 0;
}
7 静态成员函数 和静态成员变量 的调用
//静态成员函数
//静态成员函数只能访问静态成员变量
//因为静态成员函数在类里面只有1个,也就是无论你创建多少个类对象,p1,p2,…,所有的类对象是共享这一个静态成员函数的,不会各自创建一个
//静态成员函数,所以,如果你在静态成员函数里面对非静态变量进行操作,非静态成员变量,是属于某一个特定的类对象的属性
//在静态成员函数里面,是无法区分谁是谁的,数据是共享的,两者矛盾,也就是无法区分静态成员函数里面操作的是哪一个类对象的
//成员
class Person
{
public:
static void func()
{
m_A = 100;
//m_b = 200; 报错,静态成员函数不能访问非静态的成员变量
cout << "static void func() " << endl;
}
static int m_A; //静态成员变量
int m_b;
};
//静态成员变量,需要在类内访问,类外声明
int Person::m_A = 0;
void test()
{
//静态函数的访问,方法1,通过对象访问
Person p;
p.func();
//静态函数的访问,方法2,通过类名访问
Person::func();
}
int main()
{
test();
return 0;
}
8 成员变量和成员函数是分开存储的
如何理解这里的分开存储:
也就是说,只有非静态的成员变量是属于类对象的,是和类本身放在一起存储的,打印类的大小时,静态成员函数,静态成员变量,以及成员函数,都对类的大小时没有贡献的。
另外:空类的大小是1,不是0,是因为如果创建多个空类,编译器需要将这些空类放在不同的地址空间,即使是空类,也要避免存放在同一块内存
9 this指针的理解和应用
1.作用1:
前面说了,静态成员函数,只有一份,不会随着创建类的增多,而创建,那么编译器如何区分是谁在调用这共用的函数,就是通过this指针来区分的
2.作用2:
成员函数里面的形参和成员变量同名的时候,可用this来解决
以下程序是运行异常的
class Person
{
public:
Person(int age)
{
age = age;
}
int age;
};
void test()
{
Person p(18);
cout << " age = " << p.age << endl;
}
int main()
{
test();
return 0;
}
可修改为
class Person
{
public:
Person(int age)
{
//this指针指向的是 被调用的成员函数 对应的对象
this->age = age;
}
int age;
};
void test()
{
Person p(18);
cout << " age = " << p.age << endl;
}
int main()
{
test();
return 0;
}
3.作用3:
可用于返回对象本身
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
&&&&&&&&&&&&&&&&&&&&&& 1.封装
案例1 :设计一个圆类,求圆的周长
周长:2*pi*R
//class Circle
//{
// //访问权限
//public:
//
// //属性
// int m_r;
//
// //行为
// //获取圆的周长
// double claculateZC()
// {
// return 2 * 3.14 * m_r;
// }
//};
//
//
//int main()
//{
// Circle C1;
//
// C1.m_r = 10;
//
// cout << "zc is " << C1.claculateZC() << endl;
// return 0;
//}
案例2 :学生类
//class Student
//{
//public:
// int m_num;
// string m_name;
//
// void Show()
// {
// cout << "ID: " << m_num << endl;
// cout << "name : " << m_name << endl;
// }
//
// void setName(string name)
// {
// m_name = name;
// }
//};
//
//int main()
//{
// Student s1;
// s1.m_num = 1;
// s1.setName("zhangsan");
// s1.Show();
//
//
// Student s2;
// s2.m_num = 2;
// s2.m_name = "lisi";
// s2.Show();
// return 0;
//}
//访问权限
//三种
//公共权限 成员 类内可以访问 ,类外可以访问
//保护权限 成员 类内可以访问 ,类外不可以访问 ,儿子可以访问父亲的保护内容
//私有权限 成员 类内可以访问 ,类外不可以访问 ,儿子不可以访问父亲的私有内容
// struct 和 class 的区别
//唯一的区别是默认的访问权限是不同的
//struct默认的访问权限是公有
//class 默认的访问权限是私有
//class C1
//{
// int m_a;
//
//};
//
//struct C2
//{
// int m_a;
//};
//
//
//int main()
//{
// C1 c1;
// //c1.m_a = 100; //不可访问
//
// C2 c2;
// c2.m_a = 100;
// return 0;
//}
//成员设置为私有的好处
//1.可以控制读写权限
//class Person
//{
//
//public:
// //姓名原本是私有,类外根本访问不到,通过函数接口,来间接的修改
// void setName(string name) //写
// {
// m_name = name;
// }
//
// string getName() //读
// {
// return m_name;
// }
//
//
// //希望年龄是只读,那就只提供给一个读的接口
// int getAge() //读
// {
// m_age = 20;
// return m_age;
// }
//
// void setLover(string lover)
// {
// m_lover = lover;
// }
//
//private:
// //可以自行设置权限
// string m_name;
// int m_age;
// string m_lover;
//
//};
//
//int main()
//{
// Person p1;
// p1.setName("zhangsan");
// cout << "name = " << p1.getName() << endl;
//
// cout << "age = " << p1.getAge() << endl;
//
// p1.setLover("baby");
// return 0;
//}
// 案例 :立方体
//求出立方体的面积和体积
//分别用全局函数和成员函数来判断两个立方体是否相等
//class Cube
//{
//
//public:
// void setWidth(int width)
// {
// m_width = width;
// }
// int getWidth()
// {
// return m_width;
// }
//
// void setHeight(int height)
// {
// m_height = height;
// }
// int getHeight()
// {
// return m_height;
// }
//
// void setLenght(int length)
// {
// m_lenght = length;
// }
// int getLenght()
// {
// return m_lenght;
// }
//
// int getArea()
// {
// return 2 * (m_width * m_height + m_width * m_lenght + m_lenght * m_height);
// }
//
// int getVolumn()
// {
// return m_width * m_height * m_lenght;
// }
//
// bool isSame(Cube c2)
// {
// if (c2.getHeight() == m_height && c2.getLenght() == m_lenght && c2.getWidth() == m_width)
// return true;
// else
// return false;
// }
//
//private:
// int m_width;
// int m_height;
// int m_lenght;
//
//};
//
//
//bool isSameOrNot(Cube c1, Cube c2)
//{
// if (c2.getHeight() == c1.getHeight() && c2.getLenght() == c1.getLenght() && c2.getWidth() == c1.getWidth())
// return true;
// else
// return false;
//}
//
//int main()
//{
// Cube c1;
// c1.setHeight(10);
// c1.setLenght(10);
// c1.setWidth(10);
//
// cout << "the area : " << c1.getArea() << endl;
// cout << "the volumn : " << c1.getVolumn() << endl;
//
// Cube c2;
// c2.setHeight(10);
// c2.setLenght(10);
// c2.setWidth(10);
//
// cout << "************ 全局函数判断两个立方体是否一样" << endl;
// bool ret = isSameOrNot(c1, c2);
// if (ret)
// {
// cout << "same" << endl;
// }
// else
// {
// cout << "not same" << endl;
// }
//
//
// cout << "************ 成员函数判断两个立方体是否一样" << endl;
// bool ret1 = c1.isSame(c2);
// if (ret)
// {
// cout << "same" << endl;
// }
// else
// {
// cout << "not same" << endl;
// }
// return 0;
//}
//案例:判断点和圆的关系
//class Point
//{
//public:
// void setX(int x)
// {
// m_x = x;
// }
// double getX()
// {
// return m_x;
// }
//
// void setY(int y)
// {
// m_y = y;
// }
// double getY()
// {
// return m_y;
// }
//private:
// double m_x;
// double m_y;
//};
//
//class Circle
//{
//public:
// void setR(int r)
// {
// m_r = r;
// }
// double getR()
// {
// return m_r;
// }
//
// void setCenter(Point center)
// {
// m_center = center;
// }
// Point getCenter()
// {
// return m_center;
// }
//
//public:
// void judgeRelation()
// {
//
// }
//
//private:
// double m_r;
// Point m_center; //这里其实就是创建了一个点的类对象
//};
//
//void isInCircle(Circle &c ,Point &p)
//{
// int distance;
// distance = (c.getCenter().getX() - p.getX()) * (c.getCenter().getX() - p.getX()) +
// (c.getCenter().getY() - p.getY()) * (c.getCenter().getY() - p.getY());
//
// int rSquare = c.getR() * c.getR();
//
// if (distance > rSquare)
// {
// cout << "out" << endl;
// }
// else if (distance < rSquare)
// {
// cout << "in" << endl;
// }
// else
// {
// cout << "on" << endl;
// }
//}
//
//int main()
//{
// Circle c;
// c.setR(10);
//
// Point center;
// center.setX(10);
// center.setY(0);
// c.setCenter(center);
//
// Point p;
// p.setX(10);
// p.setY(20);
// isInCircle(c, p);
//
// return 0;
//}
//************************* 对象特性
//1.构造和析构
//class Person
//{
//public:
// //1.构造函数实现初始化
// Person()
// {
// cout << "构造函数" << endl;
// }
//
// //2.析构函数实现清理
// ~Person()
// {
// cout << "析构函数" << endl;
// }
//};
//
//int main()
//{
// Person p1;
// return 0;
//}
构造函数的分类和调用
1分类
按照参数分类 无参构造函数(默认构造) 和 有参构造函数
按照类型分类 拷贝构造 普通构造
//
//class Person
//{
//public:
//
// Person()
// {
// cout << "无参构造函数" << endl;
// }
//
// Person(int a)
// {
// m_age = a;
// cout << "有参构造函数" << endl;
// }
//
// //拷贝构造
// Person(const Person &p)
// {
// //将传入的人的所有属性,拷贝到当前的类中
// m_age = p.m_age;
// cout << "拷贝构造" << endl;
// }
//
//
// ~Person()
// {
// cout << "析构函数" << endl;
// }
//
// int m_age;
//};
//
//
构造函数的调用
//void test()
//{
//
// Person p1; //什么都不做,就会调用默认构造函数
// //注意1,调用默认构造的时候不要写(),不会执行任何东西,因为编译器会认为下面语句是一个函数声明,不会认为在创建对象
// //Person p();
// //void func(); 两者类似,可以认为上面一行语句是函数声明
//
// //1.括号法
// Person p2(10); //括号法调用有参构造函数
// Person p3(p2); //括号法调用拷贝构造函数
//
// cout << "p2 age is " << p2.m_age << endl;
// cout << "p3 age is " << p3.m_age << endl;
//
// //2.显示法
// Person p4 = Person(10); //显示法调用有参构造函数
// Person p5 = Person(p4); //显示法调用有拷贝构造函数
//
// //注意2:单独的一个语句 Person (10); 左侧没有接收,称为匿名对象,匿名对象的特点是当前行执行结束之后,系统立即释放
// //注意3:不要用拷贝构造函数来初始化一个匿名对象
// //Person(p5); 运行显示 Person p5重定义 因为 Person(p5) === Person p5,是一对象声明
//
// //3.隐式转换法
// Person p6 = 100; //相等于 Person p6 = Person(10);
// Person p7 = p6; //隐式转换法 调用拷贝构造函数
//}
//int main()
//{
// test();
// return 0;
//}
//构造函数的调用规则
//默认情况下,c++会至少给一个类添加3个函数
//1.默认提供一个无参构造函数
//2.默认提供一个无参析构
//3.默认提供拷贝构造,对属性进行值拷贝
深拷贝和浅拷贝
//class Person
//{
//public:
//
// Person()
// {
// cout << "无参构造函数" << endl;
// }
//
// Person(int age ,int height)
// {
// m_age = age;
// m_height = new int(height);
// cout << "有参构造函数" << endl;
// }
//
// 拷贝构造
// //Person(const Person &p)
// //{
// // //将传入的人的所有属性,拷贝到当前的类中
// // m_age = p.m_age;
// // cout << "拷贝构造" << endl;
// //}
//
// //自己实现拷贝构造,解决浅拷贝的问题,也就是深拷贝,开辟一个新的内存,来存放堆区的数据
// Person(const Person& p)
// {
// m_age = p.m_age;
// m_height = new int(*p.m_height);
// cout << "拷贝构造" << endl;
// }
//
//
// ~Person()
// {
// if (m_height != NULL)
// {
// delete m_height;
// m_height = NULL;
// }
// cout << "析构函数" << endl;
// }
//
// int m_age;
// int* m_height; //将这个数据开辟再堆区
//};
//
//
//void test01()
//{
// Person p(18, 160);
//
// cout << "p.age = " << p.m_age << " p.m_height= " << *p.m_height << endl;
//
// Person p1(p);
//
// cout << "p1.age = " << p1.m_age << " p1.m_height= " << *p1.m_height << endl;
//}
//
//int main()
//{
// test01();
// return 0;
//}
//初始化列表也可以实现属性的初始化
//class Person
//{
//public:
// // 传统的初始化操作,在构造函数中初始化
// //Person(int a, int b, int c)
// //{
// // m_a = a;
// // m_b = b;
// // m_c = c;
// //}
//
// 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(10, 20, 30);
cout << "m_a = " << p.m_a << endl;
cout << "m_b = " << p.m_b << endl;
cout << "m_c = " << p.m_c << endl;
}
//
//void test02()
//{
// 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()
//{
// test02();
// return 0;
//}
//一个类作为另一个类的成员
//class Phone
//{
//public:
// Phone(string pName)
// {
// m_phoneName = pName;
// }
// string m_phoneName;
//};
//
//class Person
//{
//public:
// Person(string name,string pName):m_name(name), m_phone(pName)
// {
// }
//
// string m_name;
// Phone m_phone;
//};
//
//void test()
//{
// Person p("zhang san ", "max");
// cout << p.m_name << " have " << p.m_phone.m_phoneName << endl;
//}
//
//
//int main()
//{
// test();
// return 0;
//}
//静态成员函数
//静态成员函数只能访问静态成员变量
//因为静态成员函数在类里面只有1个,也就是无论你创建多少个类对象,p1,p2,...,所有的类对象是共享这一个静态成员函数的,不会各自创建一个
//静态成员函数,所以,如果你在静态成员函数里面对非静态变量进行操作,非静态成员变量,是属于某一个特定的类对象的属性
//在静态成员函数里面,是无法区分谁是谁的,数据是共享的,两者矛盾,也就是无法区分静态成员函数里面操作的是哪一个类对象的
//成员
//class Person
//{
//public:
// static void func()
// {
// m_A = 100;
// //m_b = 200; 报错,静态成员函数不能访问非静态的成员变量
// cout << "static void func() " << endl;
// }
//
// static int m_A; //静态成员变量
// int m_b;
//};
//
静态成员变量,需要在类内访问,类外声明
//int Person::m_A = 0;
//
//void test()
//{
// //静态函数的访问,方法1,通过对象访问
// Person p;
// p.func();
//
// //静态函数的访问,方法2,通过类名访问
// Person::func();
//}
//
//int main()
//{
// test();
// return 0;
//}
//成员变量和成员函数是分开存储的
class Person
{
public:
Person(int age)
{
this->age = age;
}
void PersonAddAge(Person &p)
{
//将传进来的数据加到自身上
this->age = this->age + p.age;
}
Person& PersonAddAge2(Person& p)
{
//将传进来的数据加到自身上
this->age = this->age + p.age;
//this是指向对象的指针,*this就是解引用
//return *this; 返回对象本身
return *this;
}
Person PersonAddAge3(Person& p)
{
//将传进来的数据加到自身上
this->age = this->age + p.age;
//this是指向对象的指针,*this就是解引用
//return *this; 返回对象本身
return *this;
}
int age;
};
void test()
{
Person p1(18);
cout << " p1 age = " << p1.age << endl;
Person p2(20);
p2.PersonAddAge(p1); //成员函数返回值类型是void,所以这个操作只能执行一次
cout << " p2 age = " << p2.age << endl;
Person p3(10);
p3.PersonAddAge2(p1).PersonAddAge2(p1).PersonAddAge2(p1); //成员函数返回值类型是类对象本身,所以这个操作能无限执行
cout << " p3 age = " << p3.age << endl;
Person p4(10);
p4.PersonAddAge3(p1).PersonAddAge3(p1).PersonAddAge3(p1); //成员函数返回值类型是类对象本身,所以这个操作能无限执行
cout << " p4 age = " << p4.age << endl;
}
int main()
{
test();
return 0;
}
p4为什么是10,没有实现累加,是因为:
Person PersonAddAge3(Person& p)
{
this->age = this->age + p.age;
return *this;
}
这里返回类对象,不是类本身,就会调用拷贝构造函数,拷贝构造函数的本质的,创建一个新的类对象,来存放一模一样的数据,也就是说,return的已经不是类对象本身了,而是一个新的类,一个存放了和当前类数据一摸一样的新类,所以
p4.PersonAddAge3(p1).PersonAddAge3(p1).PersonAddAge3(p1);
p4.PersonAddAge3(p1)执行结束后,做了一次累加,返回的类已经不是p4了,所以打印的值只是p4做了一次累加的结果
上述案例,如果返回的
10 为了防止空指针对象访问成员属性,可以再函数里面加一个if判断,防止程序出错
//空指针访问成员函数
class Person
{
public:
void showClassNmae()
{
cout << " showClassNmae() " << endl;
}
void showAge()
{
if (this == NULL)
{
return;
}
cout << "age = " << this->m_age << endl;
}
int m_age;
};
void test()
{
Person* p = NULL; //创建一个空指针
p->showClassNmae(); // 能正常执行
p->showAge(); //报错,显示地址冲突
}
int main()
{
test();
return 0;
}
11 const 修饰成员函数(常函数),const 成员属性(常变量),const 修饰对象(常对象)
//const 修饰成员函数
//常函数里面的对象是不可以修改的
class Person
{
public:
//常函数
//this指针的本质,是指针常量,指针的指向是不可以修改的
//在成员函数后面加 const,函数变成常函数,这个const修饰的是this指针,使得thsi指针指向的值也不能修改
void showPerson() const
{
//this->m_a = 100; 报错,表达式必须是可修改的左值 指针指向的值不可以修改
this->m_b = 100;
}
void fun()
{
}
int m_a;
mutable int m_b; //常变量,即使在常函数中也可以修改这个变量
};
//常对象
void test()
{
const Person p;
//p.m_a = 100; 报错,常函数和常对象都只能操作常变量,且常对象只能调用常函数
p.m_b = 100;
p.showPerson();
}
int main()
{
test();
return 0;
}
三 友元
1.全局函数做友元 (所谓全局函数,就是常规的定义在main函数外面的函数)
//全局函数做友元
class Building
{
//goodGay函数是 Building的好朋友,可以访问类内的私有成员
friend void goodGay(Building* building);
public:
Building()
{
m_sittingRoom = "客厅";
m_bedRoom = "卧室";
}
string m_sittingRoom;
private:
string m_bedRoom;
};
//全局函数
//所谓全局函数,就是放在main函数外面的函数
void goodGay(Building *building)
{
cout << "全局函数goodGay is visting :" << building->m_sittingRoom << endl;
cout << "全局函数goodGay is visting :" << building->m_bedRoom << endl;
}
int main()
{
Building b;
goodGay(&b);
return 0;
}
2 类做友元 以及 在类外实现成员函数
(1)类做友元
class Building
{
//goodGay函数是 Building的好朋友,可以访问类内的私有成员
friend class GoodGay;
public:
Building()
{
m_sittingRoom = "客厅";
m_bedRoom = "卧室";
}
string m_sittingRoom;
private:
string m_bedRoom;
};
class GoodGay
{
public:
GoodGay()
{
building = new Building; //new什么就返回什么,创建了一个Building指针
}
Building* building;
void visit()
{
cout << "GoodGay is visting :" << building->m_sittingRoom << endl;
cout << "GoodGay is visting :" << building->m_bedRoom << endl;
}
};
int main()
{
GoodGay gay;
gay.visit();
return 0;
}
(2)在类外实现成员函数
class Building
{
//goodGay函数是 Building的好朋友,可以访问类内的私有成员
friend class GoodGay;
public:
Building();
{
m_sittingRoom = "客厅";
m_bedRoom = "卧室";
}
string m_sittingRoom;
private:
string m_bedRoom;
};
class GoodGay
{
public:
GoodGay();
void visit(); //用来访问building对象里面的属性
Building* building;
};
//类外实现成员函数
Building::Building()
{
m_sittingRoom = "客厅";
m_bedRoom = "卧室";
}
GoodGay::GoodGay()
{
building = new Building; //new什么就返回什么,创建了一个Building指针
}
void GoodGay::visit()
{
cout << "GoodGay is visting :" << building->m_sittingRoom << endl;
cout << "GoodGay is visting :" << building->m_bedRoom << endl;
}
int main()
{
GoodGay gay;
gay.visit();
return 0;
}
3 全局函数做友元
#include <iostream>
using namespace std;
class Building; // 注意这里,提前声明 Forward declaration
class GoodGay // 注意这里
{
public:
void visit1(); // 注意这里,提前声明 Forward declaration
void vist2(); // 注意这里,提前声明 Forward declaration
GoodGay(); // 构造函数声明
~GoodGay(); // 析构函数声明
Building* building;
};
class Building // 注意这里
{
public:
Building()
{
m_sittingRoom = "客厅";
m_bedRoom = "卧室";
}
string m_sittingRoom;
friend void GoodGay::visit1();
private:
string m_bedRoom;
};
GoodGay::GoodGay() // 注意这里,必须在定义 class Building 之后才能定义
{
building = new Building;
}
GoodGay::~GoodGay()
{
delete building; // 凡是有 new,必然要有 delete
}
void GoodGay::visit1() // 注意这里,必须在定义 class Building 之后才能定义
{
cout << "visit1 is visting :" << building->m_sittingRoom << endl;
cout << "visit1 is visting :" << building->m_bedRoom << endl;
}
void GoodGay::vist2() // 注意这里,必须在定义 class Building 之后才能定义
{
cout << "visit2 is visting :" << building->m_sittingRoom << endl;
}
int main(){
GoodGay gay;
gay.visit1();
gay.vist2();
return 0;
}
四 运算符重载
作用:实现自定义变量的运算符重载
1.加号运算符重载(成员函数实现和全局函数实现)
(1).成员函数实现
class Person
{
public:
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 test()
{
Person p1;
p1.m_a = 10;
p1.m_b = 20;
Person p2;
p2.m_a = 10;
p2.m_b = 20;
// 下面语句等价于 Person p3 = p1 + p2;
Person p3 = p1.operator+(p2);
cout << " p3.m_a = " << p3.m_a << endl;
cout << " p3.m_b = " << p3.m_b << endl;
}
int main()
{
test();
return 0;
}
(2).全局函数重载
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_a;
temp.m_b = p1.m_b + p2.m_b;
return temp;
}
void test()
{
Person p1;
p1.m_a = 10;
p1.m_b = 20;
Person p2;
p2.m_a = 10;
p2.m_b = 20;
//下面语句等价于 Person p3 =operator+(p1,p2);
Person p3 = p1 + p2;
cout << " p3.m_a = " << p3.m_a << endl;
cout << " p3.m_b = " << p3.m_b << endl;
}
int main()
{
test();
return 0;
}
(3).运算符重载也可以实现函数重载
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_a;
temp.m_b = p1.m_b + p2.m_b;
return temp;
}
//运算符重载也可以实现函数重载
Person operator+(Person& p1, int num)
{
Person temp;
temp.m_a = p1.m_a + num;
temp.m_b = p1.m_b + num;
return temp;
}
void test()
{
Person p1;
p1.m_a = 10;
p1.m_b = 20;
Person p2;
p2.m_a = 10;
p2.m_b = 20;
Person p3 = p1 + p2;
cout << " p3.m_a = " << p3.m_a << endl;
cout << " p3.m_b = " << p3.m_b << endl;
Person p4 = p1 + 100;
cout << " p4.m_a = " << p4.m_a << endl;
cout << " p4.m_b = " << p4.m_b << endl;
}
int main()
{
test();
return 0;
}
2. 左移运算符重载 <<
//左移运算符( << )重载
//重载左移运算符 ,实现语句 cout << p 打印类里面的相关内容;
class Person
{
friend ostream& operator<<(ostream& cout, Person& p);
public:
Person(int a, int b)
{
m_a = a;
m_b = b;
}
private:
//不能用成员函数实现左移运算符 目标实现:cout << p
//void operator<<(cout) ,调用时候,调用语句是p.operator<<(cout) 调用时,可以简化为调用语句 p << cout,是反的
//{
//}
int m_a;
int m_b;
};
ostream& operator<<(ostream &cout , Person &p) //调用语句 operator<<( cout , p) == cout << p ,和前面的加号运算符重载可以简化为 p1+p2是一样的
{
cout << " p.m_a = " << p.m_a << endl;
cout << " p.m_b = " << p.m_b << endl;
return cout;
}
void test()
{
Person p(10,10);
cout << p << "hello " << endl;
}
int main()
{
test();
return 0;
}
3.赋值运算符重载
什么时候要用到赋值运算符重载,
数据创建在堆区的时候,想实现对象的赋值操作,就需要实现赋值运算符重载
避免堆区数据的重复释放
下列程序会报错
class Person
{
public:
Person(int age)
{
m_age = new int(age); //在堆区创建一个空间,为int * 来存放 age这个数据
}
~Person( )
{
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
}
int* m_age;
};
void test01()
{
//如下操作会导致程序崩溃
Person p1(18);
Person p2(20);
p2 = p1; //这里实现的是浅拷贝,也就是完全的值拷贝,p1的数据以及数据存放的地址都完全拷贝过来,不会开辟新的空间来存放这个数据
//在程序执行结束之后,类p1,p2会各自调用一次析构函数,也就是同一块内存被释放了两次,所以报错
cout << "p1.age = " << *p1.m_age << endl;
cout << "p2.age = " << *p2.m_age << endl;
}
int main()
{
test01();
return 0;
}
正解为:
但是无法实现编译器的连续相等的功能,p1 = p2 =p3
class Person
{
public:
Person(int age)
{
m_age = new int(age); //在堆区创建一个空间,为int * 来存放 age这个数据
}
~Person()
{
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
}
//赋值运算符重载
void 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); //指针m_age 指向新开辟的空间
}
int* m_age;
};
void test01()
{
//如下操作会导致程序崩溃
Person p1(18);
Person p2(20);
p2 = p1; //这里实现的是浅拷贝,也就是完全的值拷贝,p1的数据以及数据存放的地址都完全拷贝过来,不会开辟新的空间来存放这个数据
//在程序执行结束之后,类p1,p2会各自调用一次析构函数,也就是同一块内存被释放了两次,所以报错
cout << "p1.age = " << *p1.m_age << endl;
cout << "p2.age = " << *p2.m_age << endl;
}
int main()
{
test01();
return 0;
}
想要实现p1 = p2 = p3 = … 的功能,重载函数的返回值需要进行修改
class Person
{
public:
Person(int age)
{
m_age = new int(age); //在堆区创建一个空间,为int * 来存放 age这个数据
}
~Person()
{
if (m_age != NULL)
{
delete m_age;
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); //指针m_age 指向新开辟的空间
//返回对象本身
return *this; //this是自身指针,*是解引用
}
int* m_age;
};
void test01()
{
//如下操作会导致程序崩溃
Person p1(18);
Person p2(20);
Person p3(30);
p2 = p1 = p3; //这里实现的是浅拷贝,也就是完全的值拷贝,p1的数据以及数据存放的地址都完全拷贝过来,不会开辟新的空间来存放这个数据
//在程序执行结束之后,类p1,p2会各自调用一次析构函数,也就是同一块内存被释放了两次,所以报错
cout << "p1.age = " << *p1.m_age << endl;
cout << "p2.age = " << *p2.m_age << endl;
cout << "p3.age = " << *p3.m_age << endl;
}
int main()
{
test01();
return 0;
}
4.关系运算符重载 == !=
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class Person
{
public:
Person(string name, int age)
{
m_age = age;
m_name = name;
}
//重载 ==
bool operator==(Person &p)
{
if (this->m_age == p.m_age && this->m_name == p.m_name)
{
return true;
}
false;
}
int m_age;
string m_name;
};
void test()
{
Person p1("tom", 18);
Person p2("jerry", 18);
if (p1 == p2)
{
cout << " p1 = p2" << endl;
}
else
{
cout << " p1 != p2" << endl;
}
}
int main()
{
test();
return 0;
}
4 实现函数调用的 ()符号,又称为仿函数 ,匿名对象
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class MyPrint
{
public:
//重载函数调用运算符
void operator()(string test)
{
cout << test << endl;
}
};
void test()
{
MyPrint myprint;
myprint("hello world");
}
void MyPrint02(string test)
{
cout << test << endl;
}
void test02()
{
MyPrint02("hello world");
}
int main()
{
test();
test02();
return 0;
}
//仿函数非常灵活,没有固定的写法
//实现加法
class Add
{
public:
int operator()(int num1, int num2)
{
return num1 + num2;
}
};
void test02()
{
Add add;
add(100,200);
cout << "add(100,200) = " << add(100, 200) << endl;
//匿名函数对象 ,Add() == 前面的add ,小括号上下一样
cout << Add()(100, 100) << endl;
}
int main()
{
test02();
return 0;
}
五 继承
1.继承的基本语法
常规多类
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class Java
{
public:
void header()
{
cout << "首页,公开课,登录,注册。。。。" << endl;
}
void footer()
{
cout << "帮助中心,公开课,登录,注册。。。。" << endl;
}
void content()
{
cout << "java,python,c++,c。。。。" << endl;
}
void source()
{
cout << "java video" << endl;
}
};
class Python
{
public:
void header()
{
cout << "首页,公开课,登录,注册。。。。" << endl;
}
void footer()
{
cout << "帮助中心,公开课,登录,注册。。。。" << endl;
}
void content()
{
cout << "java,python,c++,c。。。。" << endl;
}
void source()
{
cout << "Python video" << endl;
}
};
class C
{
public:
void header()
{
cout << "首页,公开课,登录,注册。。。。" << endl;
}
void footer()
{
cout << "帮助中心,公开课,登录,注册。。。。" << endl;
}
void content()
{
cout << "java,python,c++,c。。。。" << endl;
}
void source()
{
cout << "c video" << endl;
}
};
void test()
{
cout << "java 页面 。。。" << endl;
Java ja;
ja.content();
ja.footer();
ja.header();
ja.source();
cout << "----------------------------。" << endl;
cout << "Python 页面 。。。" << endl;
Python py;
py.content();
py.footer();
py.header();
py.source();
cout << "----------------------------。" << endl;
cout << "c 页面 。。。" << endl;
C c;
c.content();
c.footer();
c.header();
c.source();
}
int main()
{
test();
return 0;
}
继承可以很大程度简化程序
继承实现:
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class BasePage
{
public:
void header()
{
cout << "首页,公开课,登录,注册。。。。" << endl;
}
void footer()
{
cout << "帮助中心,公开课,登录,注册。。。。" << endl;
}
void content()
{
cout << "java,python,c++,c。。。。" << endl;
}
void source()
{
cout << "java video" << endl;
}
};
class Java :public BasePage
{
public:
void content()
{
cout << "java 学科视频" << endl;
}
};
class CPP :public BasePage
{
public:
void content()
{
cout << "C++ 学科视频" << endl;
}
};
class Python :public BasePage
{
public:
void content()
{
cout << "Python 学科视频" << endl;
}
};
void test()
{
cout << "java 页面 。。。" << endl;
Java ja;
ja.content();
ja.footer();
ja.header();
ja.source();
cout << "----------------------------。" << endl;
cout << "Python 页面 。。。" << endl;
Python py;
py.content();
py.footer();
py.header();
py.source();
cout << "----------------------------。" << endl;
cout << "c 页面 。。。" << endl;
CPP c;
c.content();
c.footer();
c.header();
c.source();
}
int main()
{
test();
return 0;
}```
## 2.继承方式
继承方式有3种:公共继承,保护继承,私有继承
![在这里插入图片描述](https://img-blog.csdnimg.cn/3659bf53332f40b0bb5826dba54990f0.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA54yq54yq6Jm-55qE5Lia5L2Z55Sf5rS7,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
## 3 子类继承父类之后,子类和父类调用构造函数和析构函数的顺序
```cpp
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
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;
}
};
int main()
{
Son s;
return 0;
}
4. 继承中,如果子类和父类有同名成员或者成员函数,如何访问到子类或者父类的这个成员
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
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 test()
{
Son s;
cout << "m_a = " << s.m_a << endl; //访问到的是子类自己的对象
cout << "m_a = " << s.Base::m_a << endl; //访问到的是父类的对象
}
//同名成员函数的调用
void test02()
{
Son s;
s.func(); //访问到的是子类自己的对象
s.Base::func(); //访问到的是父类的对象
}
int main()
{
test();
test02();
return 0;
}
5.继承中出现 静态成员变量或者静态成员函数 如何访问 (同非静态的请情况)
6.多继承,允许一个类继承多个类
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class Base1
{
public:
Base1()
{
m_a = 100;
}
int m_a;
};
class Base2
{
public:
Base2()
{
m_a = 10000;
m_b = 200;
}
int m_b;
int m_a;
};
class Son:public Base1, public Base2
{
public:
Son()
{
m_c = 300;
m_d = 400;
}
int m_c;
int m_d;
};
void test01()
{
Son s;
cout << "size of son : " << sizeof(s) << endl;
cout << "Base1 : m_a = " << s.Base1::m_a << endl;
cout << "Base2 : m_a = " << s.Base2::m_a << endl;
}
int main()
{
test01();
return 0;
}
7 菱形继承的问题
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class Animal
{
public:
int m_age = 10;
};
class Sheep :virtual public Animal
{};
class Tuo:virtual public Animal
{};
class SheppTuo:public Sheep, public Tuo
{};
int main()
{
SheppTuo st;
st.Sheep::m_age = 18;
st.Tuo::m_age = 20;
//当菱形继承时,两个父类具有相同的数据,需要加作用域做区分
cout << "st.Sheep::m_age = " << st.Sheep::m_age << endl;
cout << "st.Tuo::m_age = " << st.Tuo::m_age << endl;
//SheppTuo 继承了两份动物的类的数据Sheep 的age 和 Tuo 的age ,究竟现在他几岁,资源浪费
//利用虚继承,可以解决这个问题
//原因:
cout << "st.m_age = " << st.m_age << endl;
return 0;
}
虚继承的底层实现:
继承的指针,指针会指向唯一的一个地址
六 多态
1.多态的基本语法
动态多态使用的前提条件:
1.存在继承关系,在父类里面定义虚函数,实现动态多态
2.子类需要重写父类里的虚函数,就是完全copy下面,函数里面是什么不管,但是要去函数名称,
函数传入参数完全一致,virtual关键字可写可不写
打印的是 Animal is speaking
传入的是cat对象,本意是希望调用cat类,但是执行的是 Animal 类
因为函数地址是早绑定,在编译阶段就以已经确定了地址,无论你传什么,都会走animal类
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class Animal
{
public:
void speak()
{
cout << "Animal is speaking " << endl;
}
};
class Cat: public Animal
{
public:
void speak()
{
cout << "Cat is speaking " << endl;
}
};
void DoSpeak(Animal &animal) //传的时引用
{
animal.speak();
}
int main()
{
Cat cat;
DoSpeak(cat);
}
如果想执行让猫说话,地址就不能早绑定,做法如下
在父类里面将函数定义为虚函数
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class Animal
{
public:
//虚函数
virtual void speak()
{
cout << "Animal is speaking " << endl;
}
};
class Cat: public Animal
{
public:
void speak()
{
cout << "Cat is speaking " << endl;
}
};
class Dog : public Animal
{
public:
void speak()
{
cout << "Dog is speaking " << endl;
}
};
void DoSpeak(Animal &animal)
{
animal.speak();
}
int main()
{
Cat cat;
DoSpeak(cat);
Dog dog;
DoSpeak(dog);
}
2.多态的进一步理解
cout << "sizeof(Animal) = " << sizeof(Animal) << endl; //不加 virtual 返回值1 ,加了之后返回值4
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class Animal
{
public:
virtual void speak()
{
cout << "Animal is speaking " << endl;
}
};
class Cat: public Animal
{
public:
void speak()
{
cout << "Cat is speaking " << endl;
}
};
class Dog : public Animal
{
public:
void speak()
{
cout << "Dog is speaking " << endl;
}
};
void DoSpeak(Animal &animal)
{
animal.speak();
}
void test1()
{
Cat cat;
DoSpeak(cat);
Dog dog;
DoSpeak(dog);
}
void test2()
{
cout << "sizeof(Animal) = " << sizeof(Animal) << endl; //不加 virtual 返回值1 ,加了之后返回值4
}
int main()
{
test2();
}
3.多态案例 计算器
常规实现,不用多态
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class Calculate
{
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;
int m_num2;
};
void test()
{
Calculate 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()
{
test();
return 0;
}
上述案例,如果要扩展新的功能,就要在原来的程序上接着写,
且一个出错,就要查整体的程序
下面用多态实现上述功能:
组织结构清晰;
可读性强;
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class AbstractCalculate
{
public:
virtual int getResult(string oper)
{
return 0;
}
int m_num1;
int m_num2;
};
//加法计算器类
class Add :public AbstractCalculate
{
public:
virtual int getResult(string oper)
{
return m_num1 + m_num2;;
}
};
class Sub :public AbstractCalculate
{
public:
virtual int getResult(string oper)
{
return m_num1 - m_num2;;
}
};
class Multiply :public AbstractCalculate
{
public:
virtual int getResult(string oper)
{
return m_num1 * m_num2;;
}
};
void test()
{
//多态使用条件
//父类指针或者引用指向子类对象
AbstractCalculate* abc = new Add;
abc->m_num1 = 10;
abc->m_num2 = 20;
AbstractCalculate* abd = new Sub;
abd->m_num1 = 10;
abd->m_num2 = 20;
AbstractCalculate* abe = new Multiply;
abe->m_num1 = 10;
abe->m_num2 = 20;
cout << "c.m_num1 + c.m_num2 = " << abc->getResult("+") << endl;
cout << "c.m_num1 - c.m_num2 = " << abd->getResult("-") << endl;
cout << "c.m_num1 * c.m_num2 = " << abe->getResult("*") << endl;
delete abc;
delete abd;
delete abe;
}
int main()
{
test();
return 0;
}
4.纯虚函数和抽象类
为什么要使用纯虚函数?
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class Base
{
public:
//纯虚函数的写法,函数后面定义 = 0
//类里面只要有一个纯虚函数,这个类就叫做抽象类
//1.抽象类无法实例化对象 见test01()函数
//2.抽象类的子类,必须要重写父类的纯虚函数,否则也是抽象类
virtual void func() = 0;
};
class Son:public Base
{
public:
//纯虚函数的写法,函数后面定义 = 0
//类里面只要有一个纯虚函数,这个类就叫做抽象类
//1.抽象类无法实例化对象 见test01()函数
//2.抽象类的子类,必须要重写父类的纯虚函数,否则也是抽象类
virtual void func()
{
cout << " func() " << endl;
};
};
//void test01()
//{
// Base B; //抽象类无法实例化对象,这行报错
//}
void test02()
{
//父类指针指向子类对象,是多态的前提
Base* base = new Son;
base->func();
delete base;
}
int main()
{
test02();
return 0;
}
5.多态案例:冲泡饮品
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class AbstractDrink
{
public:
virtual void Boil() = 0;
virtual void Brew() = 0;
virtual void PourInCup() = 0;
virtual void AddSomething() = 0;
void makeDrink()
{
Boil();
Brew();
PourInCup();
AddSomething();
}
};
class Coffee :public AbstractDrink
{
public:
virtual void Boil()
{
cout << " 农夫山泉 " << endl;
}
virtual void Brew()
{
cout << " 冲泡咖啡 " << endl;
}
virtual void PourInCup()
{
cout << " 倒入杯中 " << endl;
}
virtual void AddSomething()
{
cout << " 加糖 " << endl;
}
};
class Tea :public AbstractDrink
{
public:
virtual void Boil()
{
cout << " 矿泉水 " << endl;
}
virtual void Brew()
{
cout << " 冲泡茶叶 " << endl;
}
virtual void PourInCup()
{
cout << " 倒入杯中 " << endl;
}
virtual void AddSomething()
{
cout << " 加柠檬 " << endl;
}
};
void doWork(AbstractDrink *abs)
{
abs->makeDrink();
delete abs;
}
int main()
{
doWork(new Tea);
cout << "***************" << endl;
doWork(new Coffee);
return 0;
}
6 虚析构和纯虚析构
使用场景
如下案例是为了说明,多态且子类有数据创建在堆区的时候,父类调用析构函数,释放数据时,并不会调用子类的析构,也就是子类的数据是没有释放的
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class Animai
{
public:
virtual void speak() = 0;
Animai()
{
cout << "Animai 构造函数调用 " << endl;
}
~Animai()
{
cout << "Animai 析构函数调用 " << endl;
}
};
class CAT :public Animai
{
public:
CAT(string name)
{
m_name = new string(name);
cout << "cat 构造函数调用 " << endl;
}
virtual void speak()
{
cout << *m_name << " cat is speaking" << endl;
}
~CAT()
{
if (m_name != NULL)
{
cout << "cat 析构函数调用 " << endl;
delete m_name;
m_name = NULL;
}
}
string * m_name;
};
void test()
{
Animai* ani = new CAT("Tom" );
ani->speak();
delete ani;
}
int main()
{
test();
return 0;
}
在父类的析构函数前加virtual 关键字,形成虚析构,就可以成功释放子类的数据
#include <stdio.h>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
class Animai
{
public:
virtual void speak() = 0;
Animai()
{
cout << "Animai 构造函数调用 " << endl;
}
virtual ~Animai()
{
cout << "Animai 析构函数调用 " << endl;
}
};