类中的 属性和行为 统一称为成员
属性 又称为 成员属性 成员变量
行为 成员函数 成员方法

一 封装

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;elsereturn 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;elsereturn 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 declarationclass GoodGay // 注意这里
{public:void visit1(); // 注意这里,提前声明 Forward declarationvoid vist2();  // 注意这里,提前声明 Forward declarationGoodGay();     // 构造函数声明~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;}
};

2022-1-4 类相关推荐

  1. 【投资策略】2022 年大类资产配置展望:稳中求进-中金公司

    猜你喜欢 0.如果你想参与进元宇宙,究竟应该采取怎样的策略?1.未来10年,普通人的赚钱机会在哪里?2.华为2021数字化转型:从战略到执行.pdf3.2021抖音私域运营白皮书.pdf4.营销拓客思 ...

  2. CNS 2022学术报道 | 类脑计算与人工智能

    来源:遨游神经科学Navibrain 类脑计算与人工智能 2022年11月27日,中国神经科学学会第十五届全国学术会议(CNS 2022)开幕,此次大会采用线上形式举办.27日上午, CNS 2022 ...

  3. 2022年数学类保研经验整理(信息与计算科学、计算数学、计算机)

    记录一下自己在保研过程中的经验,希望给来年的学弟学妹们带来帮助. 个人情况简介: 末流211(几乎是最末的了)信息与计算科学专业,成绩排名第3,综合排名第1 数学建模国赛国二,美赛F奖,数学竞赛地区级 ...

  4. 2022 iapp对接类源码 iapp后台php源码

    搭建教程 1.准备一台服务器/主机 2.域名一条[二级也行] 3.居留后台源码 4.下载源码之后,打开压缩包,把居留.zip解压出来,打开居留.zip压缩包,打开config.php文件,修改数据库账 ...

  5. 2022年江苏专转本计算机大类考生经验分享(上岸南工程)

    文章目录 前言 一.考试出分与小复盘 二.转本时间规划 三.如何有效转本复习 四.转本学校建议参考 五.学长其他建议 前言 我是2022届江苏专转本计算机大类的考生,目前已经转本上岸南工程,然后来进行 ...

  6. 山西省2022年中级职称(工程师)评定条件及要求

    2022年工程类职称专业分类如下:(助理.中级) 1.建筑:建筑工程,建筑工程管理,建筑安全管理,建筑设计,建筑工程施工,工程监理,建筑给排水,土木工程,土建工程. 2.结构:建筑结构,结构工程,结构 ...

  7. Linux 用户管理 文件目录指令 时间日期指令 搜索查找类 解压压缩类

    目录 用户管理 添加用户: 指定/修改密码 删除用户 查询用户信息指令 切换用户 查看当前用户/登录用户 用户组 修改用户的组 用户和组相关文件 指定运行级别1 指定运行级别2 找回root密码 帮助 ...

  8. 2022年度项目管理软件排名揭晓:哪些软件在市场中脱颖而出?

    在项目管理软件的选择过程中,用户会倾向于参考一些软件排名来辅助自己进行选择.软件排名方面推荐参考G2,一个国外的靠谱软件评测网站,类似于软件版的"大众点评",软件评价来自于真实用户 ...

  9. Java常用类学习笔记

    Date类 Date类是在Java中获取日期时间最简单的一个类,可以通过直接实例化对象的方式获得当前的日期时间.得到的日期时间是国外的格式.Date类没有什么特殊的用法,就是简单的获取当前的日期时间. ...

  10. 2022年陕西省中高级工程师职称评审晋级条件和流程

    一.2022年工程类职称专业分类如下:(助理.中级.高级) 1. 建工类:工民建工程师.建筑工程师.建设设计工程师.建筑施工工程师.测量工程师.地质勘测工程师.建筑预算工程师.建筑管理工程师.道路与桥 ...

最新文章

  1. HTML基础学习(一)—HTML
  2. 使用.NET Core创建Windows服务(一) - 使用官方推荐方式
  3. Linux后台执行命令
  4. 特斯拉CEO马斯克:可能明年3月左右在中国推出Model S Plaid
  5. 查看pytorch和匹配的CUDA版本
  6. 2fsk调制解调原理框图_数字调制解调输出什么 数字调制解调输出特点介绍【图文】...
  7. C# WinForm开发系列 - Form/Window
  8. Struts2_day03--向值栈放数据
  9. 【工程源码】基于FPGA的数码管字体,用来在液晶屏上显示数字
  10. RAKE接收技术基础知识
  11. Yolov3 Keras版本训练详细教程
  12. Spring学习的书-夏昕(2)
  13. zfs文件服务器上传失败,解决 ZFS 文件系统问题
  14. 冒险岛里不合逻辑的地方
  15. 职业生涯必备——程序员“黑话”指南
  16. 2020 中秋、国庆快乐!
  17. 【每天1分钟】MarkDown语法学习之复选框
  18. 趣节点:互联网信息大爆炸时代,企业品牌口碑营销需要注意什么?
  19. neo4j桌面版安装
  20. 使用VBA代码在Excel中设置聚光灯高亮显示

热门文章

  1. excel中去重计数_如何在Excel中计数
  2. 检测系统中包文件是否完整的方法
  3. JavaScript 全局
  4. 使用数据处理技能优化Draftkings NBA阵容
  5. Android开发之路--1
  6. 字节跳动+京东+360+网易面试题整理,Java岗
  7. 上传Android程序到应用市场
  8. 华为认证考试费用是多少?
  9. 1.linux命令行的使用技巧
  10. Wsyscheck20080122(V1.68.21)