C++语言基础:类和对象

  • 9.4 类和对象
    • C++面向对象的三大特征:封装、继承、多态
      • C++认为万物皆为对象,对象上有属性和行为
      • 具有相同性质的对象,可以抽象为类
        • 人属于人类,车属于车类
    • 9.4.1 封装
      • 9.4.1.1 封装的意义
        • (1)封装的意义
          • 1. 将属性和行为作为一个整体,表现生活中的事物
          • 2. 将属性和行为加以权限控制
        • (2)封装意义一:
          • 在设计类的之后,属性和行为写在一起,表现事物
          • 语法:class 类名 { 访问权限: 属性 / 行为 }
            • 类中的属性和行为统称为成员
              • 1. 属性 -> 成员属性 -> 成员变量
              • 2. 行为 -> 成员函数 -> 成员方法
          • 示例1:设计一个圆类,求圆的周长
            • const double PI = 3.14; // 圆周率
              class Circle
              {
              public: // 公共的访问权限
                  int m_r; // 属性:半径
                  double calculateZC() {
                      return 2 * m_r * PI;
                  }
              private: // 私有的访问权限
              };
              int main() {
                  Circle c1; // 实例化: 通过圆类创建具体的圆(对象)
                  c1.m_r = 10; // 圆对象的属性半径进行赋值
                  cout << "圆的周长: " << c1.calculateZC() << endl;
              }

          • 示例2:设计一个学生类,属性有姓名和学号
            • // 学生类
              class Student
              {
              public:
                  string m_name;
                  int m_id;
                  void showStudent() {
                      cout << " 姓名: " << m_name << " 学号: " << m_id << endl;
                  }
                  void setName(string name) {
                      m_name = name;
                  }
                  void setID(int id) {
                      m_id = id;
                  }
              };
              int main() {
                  Student s1;
                  s1.m_name = "张三";
                  s1.m_id = 1;
                  s1.showStudent();
                  s1.m_name = "李四";
                  s1.m_id = 2;
                  s1.showStudent();
              }

        • (3)封装意义二:
          • 类在设计时,可以把属性和行为放在不同的权限下,加以控制
          • 访问权限有三种
            • 1. public       公共权限    类内可以访问   类外可以访问
            • 2. protected  保护权限   类内可以访问   类外不可以访问  儿子可以访问父类中的保护内容
            • 3. private      私有权限   类内可以访问   类外不可以访问  儿子不可以访问父类中的保护内容
          • 示例:
            • class Person
              {
              public:
                  string m_Name; // 姓名
              protected:
                  string m_Car; // 汽车
              private:
                  int m_psw; // 银行卡密码
              public:
                  void func() {
                      m_Name = "张三";
                      m_Car = "拖拉机";
                      m_psw = 1234;
                  }
              };
              int main() {
                  Person p1;
                  p1.m_Name = "李四";
                  // p1.m_Car = "奔驰"; // protected权限在类外不能访问
                  // p1.m_psw = 123; // private权限在类外不能访问
                  p1.func();
              }

      • 9.4.1.2 struct和class区别
        • 在C++中 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; // 默认权限是共有,类外可以访问
            }

      • 9.4.1.3 成员属性设置为私有
        • 优点1:将所有成员属性设置为私有,可以控制读写权限
        • 优点2:对于写权限,我们可以检测数据的有效性
        • 示例:
          • class Person
            {
            public:
                void setName(string name) {
                    m_Name = name;
                }
                string getName() {
                    return m_Name;
                }
                void setAge(int age) {
                    if (age < 0 || age > 150) { // 优点2:检测数据的有效性
                        cout << " 年龄不合理 " << endl;
                    return;
                    }
                    m_Age = age;
                }
                int getAge() {
                    return m_Age;
                }
                void setLover(string lover) {
                    m_Lover = lover;
                }
            private: // 优点1: 控制读写权限
                string m_Name; // 姓名 可读可写
                int m_Age; // 年龄 可读可写
                string m_Lover; // 朋友 只写
            };
            int main() {
                Person p;
                p.setName("张三");
                cout << "名字是:" << p.getName() << endl;
            }

    • 9.4.2 对象的初始化和清理
      • 9.4.2.1 构造函数和析构函数
        • 1. 对象的初始化和清理是两个非常重要的安全问题
          • 1. 一个对象或者变量没有初始化状态,对其是用后果是未知的
          • 2. 统一的使用完一个对象或者变量,没有及时清理,也会造成一定的安全问题
        • 2. 如果不提供构造函数和析构函数的实现,则编译器会提供构造函数和析构函数的空实现
          • 1. 构造函数
            • 作用:主要用于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用
            • 语法:类名( ) { }
              • 1. 构造函数,没有返回值也不写void
              • 2. 函数名与类名相同
              • 3. 构造函数可以有参数,也可以重载
              • 4. 程序在调用对象时会自动调用构造,无须手动调用,而且只会调用一次
          • 2. 析构函数
            • 作用:主要作用在于对象销毁前系统自动调用,执行一些清理工作
            • 语法:~ 类名( ) { }
              • 1. 析造函数,没有返回值也不写void
              • 2. 析造函数名与类名相同,在名称前加~
              • 3. 析造函数没有参数,不可以重载
              • 4. 程序在销毁对象前会自动调用析造,无须手动调用,而且只会调用一次
          • 示例:构造函数和析构函数
            • class Person
              {
              public:
                  Person()
                  {
                      cout << "构造函数" << endl;
                  }
                  ~Person()
                  {
                      cout << "析构函数" << endl;
                  }
              };
              int main() {
                  Person p;
                  system("pause");
                  return 0;
              }

      • 9.4.2.2 构造函数的分类和使用
        • 两种分类方式:
          • 1. 按参数分为:有参构造和无参构造
          • 2. 按类型分为:普通构造和拷贝构造
        • 三种调用方式:
          • 1. 括号法
          • 2. 显示法
          • 3. 隐式转换法
        • 示例
          • // 构造函数的分类
            class Person
            {
            public:
                Person() // 无参构造
                {
                    cout << "构造函数-无参构造" << endl;
                }
                Person(int a) // 有参构造
                {
                    age = a;
                    cout << "构造函数-有参构造" << endl;
                }
                Person(const Person &p) // 拷贝构造函数
                {
                    age = p.age; // 将传入人身上所有的属性拷贝到我身上
                    cout << "拷贝构造函数" << endl;
                }
                ~Person()
                {
                    cout << "析构函数" << endl;
                }
                int age;
            };
            
            // 调用:三种调用方式
            void test01()
            {
                // 1. 括号法
                //Person p; // 默认构造函数
                //Person p1(); // 无参构造函数
                //Person p2(10); // 有参构造函数
                //Person p3(p2); // 拷贝构造函数
                //cout << "p2的年龄为:" << p2.age << endl;
                //cout << "p3的年龄为:" << p3.age << endl;
                // 注意事项1
                // 调用默认构造函数的时候,不要加()
                // 2. 显示法
                Person p; // 默认构造函数
                Person p2 = Person(10); // 有参构造函数
                Person p3 = Person(p2); // 拷贝构造函数
                Person(10); // 匿名对象,特点:当前执行结束狗,系统会马上回收内存
                // 注意事项2
                // 不要利用拷贝构造函数,初始化匿名对象,编译器会认为这是一个对象的声明
                // Person(p3);
                // 3. 隐式转换法
                Person p4 = 10; // 相当于写了Person p4 = Person(10) // 有参构造函数
                Person p5 = p4; // 拷贝构造函数
            }
            int main() {
                test01();
                system("pause");
                return 0;
            }

      • 9.4.2.3 拷贝构造函数调用时机
        • C++中拷贝构造函数调用时机通常有三种情况
          • 1. 使用一个已经创建完毕的对象来初始化一个新对象
          • 2. 值传递的方式给函数参数传值
          • 3. 以值方式返回局部对象
        • 示例
          • // 调用:三种调用方式, C++中拷贝构造函数调用时机通常有三种情况
            //1. 使用一个已经创建完毕的对象来初始化一个新对象
            void test01() {
                Person p1(20);
                Person p2(p1);
                cout << "p2的年龄:" << p2.age << endl; // 20
            }
            //2. 值传递的方式给函数参数传值
            void doWork(Person p) {
            }
            
            void test02() {
                Person p;
                doWork(p);
            }
            //3. 以值方式返回局部对象
            Person doWork2() {
                Person p1;
                cout << "doWork2 p1的地址:" << (int*)&p1 << endl; // 006AFAB8
                return p1;
            }
            void test03() {
                Person p = doWork2();
                cout << "test03 p1的地址:" << (int*)&p << endl; // 006AFBB0
            }
            int main() {
                test01();
                test02();
                test03();
                system("pause");
                return 0;
            }

      • 9.4.2.4 构造函数调用规则
      • 创建一个类,C++编译器会给每个类添加只是三个函数
        • 1. 默认构造(空实现)
        • 2. 析构函数(空实现)
        • 3. 拷贝构造(值拷贝)
      • 默认情况下,C++编译器至少给一个类添加3个函数
        • 1. 默认构造函数(无参,函数体为空)
        • 2. 默认析构函数(无参,函数体为空)
        • 3. 默认拷贝构造函数,对属性进行值拷贝
      • 构造函数调用规则如下:
        • 1. 如果用户定义有参构造函数,C++不再提供无参构造,但是会提供默认拷贝构造
        • 2. 如果用户定义拷贝构造函数,C++不会再提供其他构造函数
      • 9.4.2.5 深拷贝和浅拷贝
        • 深拷贝:在堆区重新申请空间,进行拷操作
        • 浅拷贝:简单的赋值拷贝操作(浅拷贝的问题是堆区的内容重复释放
        • 总结:如果属性是在堆区开辟的,一定要在代码中提供深拷贝函数,防止浅拷贝带来的问题
        • 示例
          • int* m_Height;
            Person(const Person &p) // 拷贝构造函数
            {
                age = p.age; // 将传入人身上所有的属性拷贝到我身上
                // m_Height = p.m_Height; // 浅拷贝的实现方式,也是编译器默认的实现方式
                m_Height = new int(*p.m_Height); // 深拷贝的实现方式,开辟一块新法内存空间
                cout << "拷贝构造函数" << endl;
            }

      • 9.4.2.6 初始化列表
        • 作用:C++提供了初始化列表语法,用来初始化属性
        • 语法:构造函数():属性1(值1),属性2(值2)... { }
        • 示例
          • 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(100,200,300);
                cout << p.m_A << p.m_B << p.m_C << endl;
            }

      • 9.4.2.7 类对象作为类成员
        • C++类中的成员可以是另一个类的对象,我们称该成员为对象成员
          • 例如:B类中有对象A作为成员,A为对象成员
            • class A {}
            • class B{
              • A a;
            • }
        • 那么当创建B对象的时候,A与B的构造和析构谁先谁后?
          • 结论:A的构造 -> B的构造 -> B的析构 -> A的析构
          • 构造的时候,先构造类内的对象,在构造自身,析构的时候相反。
      • 9.4.2.8 静态成员
        • 1 静态成员就是在成员变量和成员函数前加上关键字static,称其为静态成员
        • 2 静态成员分为:
          • 1. 静态成员变量
            • 1) 所有对象共享同一份数据
            • 2) 在编译阶段分配内存
            • 3) 类内声明,类外初始化
          • 2. 静态成员函数
            • 1) 所有对象共享同一个函数
            • 2) 静态成员函数只能访问静态成员变量
        • 3 访问静态成员的方式
          • 1. 通过对象访问
          • 2. 通过类名访问
        • 示例
          • class Person
            {
            public:
                static void func() { // 静态成员函数
                    m_A = 100; // 静态成员函数可以访问静态成员变量
                    // m_B = 200; // 会报错,因为静态成员函数不可以访问非静态成员变量
                    // 因为非静态成员变量,要通过对象进行访问,直接访问的话无法区分是哪个对象的成员变量
                    cout << "static void func() 调用" << endl;
                }
                static int m_A; // 静态成员变量
                int m_B; // 非静态成员变量
            private:
                // 静态成员函数也是有访问权限的
                static void func2() {
                    cout << "static void func2() 调用" << endl;
                }
            };
            int Person::m_A = 0; // 静态成员变量类外初始化
            void test01() {
                Person p;
                p.func(); // 1. 通过对象访问
                Person::func(); // 2. 通过类名访问
                // Person::func2(); // 报错,类外无权访问
            }

      • 9.4.3 C++对象模型this指针
        • 9.4.3.1 成员变量和成员函数分开存储
          • 在C++中,类的成员变量和成员函数分开存储
          • 只有非静态成员变量才属于类的对象上
          • tip
            • 1. 空对象占用内存空间为 1
              • 1 C++编译器为每个空对象分配一字节空间,是为了区分空对象栈内存的位置
              • 2 每个空对象也有独一无二的内存地址
            • 2.
          • 示例
            • class Person
              {
              public:
                  static int m_A; // 静态成员变量,不属于类对象上
                  int m_B; // 非静态成员变量,属于类对象上
                  int m_C; // 非静态成员变量,属于类对象上
                  void func() {} // 非静态成员函数,不属于类对象上
                  };
              void test01() {
                  Person p;
                  cout << "size of P = " << sizeof(p) << endl; // 输出8,8字节是m_B和m_C占用的内存
              }

        • 9.4.3.2 this指针
          • 每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会共用一块代码
          • 问题是:这一块代码是如何区分哪个对象调用自己的呢?
          • C++通过提供特殊的对象指针,this指针,解决上述问题
            • this指针指向被调用的成员函数所属的对象
            • this指针是隐含每一个非静态成员函数内的一种指针
            • this指针不需要定义,直接使用即可
          • this指针的用途
            • 1. 当形参和成员变量同名是,可用this指针来区分
            • 2. 在类的非静态成员函数中返回对象本身,可使用return *this;
          • 示例
            • class Person
              {
              public:
                  Person(int age)
                  {
                      this->age = age;
                  }
                  // 返回值必须是引用方式,如果不是引用方式返回,则无法返回对象本身,而是创建了一个新的对象返回
                  Person& PersonAddAge(Person& p)
                  {
                      this->age += p.age;
                      return *this;
                  }
                  int age;
              };
              void test01() {
                  Person p1(10);
                  Person p2(10);
                  p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1); // 链式编程
                  cout << "p2 age = " << p2.age << endl; // 输出40
              }

        • 9.4.3.3 空指针访问成员函数
          • C++空指针也可以调用成员函数的,但是也要注意有没有用到this指针
            • 如果用到this 指针,需要加以判断保证代码的健壮性
          • 示例
            • class Person
              {
              public:
                  void showClassName() {
                      cout << "This is a Person class!" << endl;
                  }
                  void showPerson() {
                      if (this == NULL) {
                          return;
                      }
                      cout << "age = " << m_Age << endl;
                  }
                  int m_Age;
              };
              void test01() {
                  Person* p1 = NULL; // 这是一个空指针
                  p1->showClassName();
                  p1->showPerson();
              }

        • 9.4.3.4 const修饰成员函数
          • 1.常函数
            • 1. 成员函数加const后我们称这个函数为常函数
            • 2. 常函数内不可以修改成员属性
            • 3. 长远属性声明是加关键字mutable后,在常函数中依然可以修改
          • 2. 常对象
            • 1. 声明对象前加const称该对象为常对象
            • 2. 常对象只能调用常函数
          • 示例
            • class Person
              {
              public:
                  // this 指针的本质 是指针常量 指针的指向是不可以修改的
                  // 在成员函数后面加const,修饰的是this指向,让指针指向的值也不可以修改
                  // 下一行相当于 const Person * const this;
                  void showPerson() const // 常函数
                  {
                      // this 指针可以表达为 Person* const this;
                      // this = NULL; // 表达式错误,this指针不可以修改指针的指向
                      // this->m_Age = 100; // 表达式错误,常函数内不可以修改成员属性
                      this->m_B = 100;
                  }
              
                  void func() {
                  }
              
                  int m_Age;
                  mutable int m_B; // 特殊变量,即使在常函数中也可以修改
              };
              
              void test01() {
                  Person p1;
                  p1.showPerson();
              }
              
              void test02() {
                  const Person p2; //对象前加const,变为常对象
                  // p2.m_Age = 100; // 表达式错误,常对象内不可以修改成员
                  p2.m_B = 200; // m_B是特殊值,在常对象下也可以修改
                  // p2.func(); // 表达式错误,常对象只能调用常函数
              }

      • 9.4.4 友元
        • 9.4.4.1 友元介绍
          • 比喻:客厅(public)、卧室(Private);
            • 客厅所有客人都可以去,卧室私有仅仅自己能进
            • 卧室也允许自己的朋友(friend)进去
          • 程序中,有一些私有属性也可以让类外的特殊函数或者类访问,需要用到友元
          • 友元的目的:让一个函数或者类访问另一个类的私有成员
          • 友元关键字:friend
          • 友元的三种实现
            • 1. 全局函数做友元
            • 2. 类做友元
            • 3. 成员函数做友元
        • 9.4.4.2 全局函数做友元
        • 9.4.4.3 类做友元
        • 9.4.4.4 成员函数做友元
        • 示例: 三种方式做友元
          • class Buliding
            {
                friend void goodGay(Buliding* buliding);// 方式1:全局函数做友元:goodGay全局函数是Buliding好朋友,可以访问Buliding中的私有成员
                friend class GoodGay; // 方式2:类做友元
                friend void BadGay::visit(); // 方式3:成员函数做友元
            
            public:
                Buliding() {
                    m_SittingRoom = "客厅";
                    m_BedRoom = "卧室";
                }
            
            public:
                string m_SittingRoom; // 客厅
            
            private:
                string m_BedRoom; // 卧室
            };
            
            // 方式1:全局函数做友元
            void goodGay(Buliding* buliding) {
                cout << "全局函数做友元 好朋友正在访问:" << buliding->m_SittingRoom << endl;
                cout << "全局函数做友元 好朋友正在访问:" << buliding->m_BedRoom << endl;// 方式1:全局函数做友元.访问私有成员
            }
            
            // 方式2:类做友元
            class GoodGay {
            public:
                GoodGay() {
                    buliding = new Buliding;
                }
            
                void visit() { // 参观函数,访问building中的属性
                    cout << "类做友元 好朋友正在访问:" << buliding->m_SittingRoom << endl;
                    cout << "类做友元 好朋友正在访问:" << buliding->m_BedRoom << endl; // 方式2:类做友元.访问私有成员
                }
            
                Buliding* buliding;
            };
            
            // 方式3:成员函数做友元
            class BadGay {
            public:
                BadGay();
                void visit();// visit1函数不可以访问BUliding中的私有成员
                void visit2(); //3.成员函数做友元: visit2参观函数,访问building中的私有成员
                Buliding* buliding;
            };
            
            BadGay::BadGay() {
                buliding = new Buliding;
            }
            
            void BadGay::visit() { //3.成员函数做友元: visit2参观函数,访问building中的私有成员
                cout << "普通函数 正在访问:" << buliding->m_SittingRoom << endl;
                cout << "成员函数做友元 好朋友正在访问:" << buliding->m_BedRoom << endl; // 方式2:类做友元.访问私有成员
            }
            
            void BadGay::visit2() {
                cout << "成员函数做友元 好朋友正在访问:" << buliding->m_SittingRoom << endl;
            }
            
            void test01() {
                Buliding buliding;
                goodGay(&buliding); // 方式1:全局函数做友元
                GoodGay goodGay;
                goodGay.visit(); // 方式2:类做友元
                BadGay badGay;
                badGay.visit(); // 方式3:成员函数做友元
            }

      • 9.4.5 运算符重载
      • 9.4.6 继承
        • 9.4.6.1 继承的基本语法
          • 语法:class 子类 : 继承方式 父类
            • 示例:class A : public B;
          • A类称为 子类 或者 派生类
            • 子类中的成员,包含两大部分:
              • 1. 一类是从基类继承过来的
              • 2. 另一类是自己增加的成员
              • 从基类继承过来的成员表现出其共性,新增的成员表现出其个性
          • B类称为 父类 或者 基类
        • 9.4.6.2 继承方式
          • 继承方式有三种:
            • 1. 公共继承
            • 2. 保护继承
            • 3. 私有继承
            • 三种继承方式展示

        • 9.4.6.3 继承中的对象模型
          • 问题:从父类继承过来的成员,哪些属于子类对象中?
            • 答案:父类中所有的非静态成员属性都会被子类继承。父类中私有成员属性是被编译器隐藏了,因此访问不到,但是依旧在子类中占有内存空间
            • sizeof(子类) = sizeof(父类) + sizeof(子类新增的成员属性)
          • 利用开发人员命令提示工具查看对象模型
            • 1. 跳转到code所在的路径
            • 2. 命令行输入具体的查看命令:
              • cl /d1 reportSingleClassLayout类名 文件名
              • 示例:
                • cl /d1 reportSingleClassLayoutSon "01C++HelloWorld.cpp"

        • 9.4.6.4 继承中构造和析构的顺序
          • 子类继承父类后,当创建子类对象,也会调用父类的构造函数
          • 问题:父类和子类的构造和析构顺序是谁先谁后?
            • 答案:父构造 -> 子构造 -> 子析构 -> 父析构
        • 9.4.6.5 继承同名成员处理方式
          • 问题:当子类和父类出现同名的成员,如果通过子类对象,访问到子类或者父类中同名的数据呢?
            • 访问子类同名成员:直接访问即可
            • 访问父类同名成员:需要加作用域
          • 如果子类中出现和父类同名的成员函数,子类的同名成员函数会将父类的同名成员函数全部隐藏
            • 如果想要访问父类的同名成员函数,就需要加作用域
          • 示例

        • 9.4.6.6 继承同名静态成员处理方式
          • 问题:继承中同名静态成员在子类对象上如何访问?
            • 静态和非静态出现同名时,处理方式一致
              • 访问子类同名成员:直接访问即可
              • 访问父类同名成员:需要加作用域
            • 静态成员有特殊之处
              • 静态成员可以通过类名访问,非静态成员不可以
            • 示例:

        • 9.4.6.7 多继承语法
          • C++允许一个类继承多个类
            • 多继承可能会引发父类有同名成员出现,需要加作用域区分
            • C++实际开发中不建议使用多继承
          • 语法:class 子类:继承方式 父类1,继承方式 父类2…
        • 9.4.6.8 菱形继承
          • 概念:
            • 1. 两个派生类继承同一个基类
            • 2. 又有某个类同时继承这两个派生类
            • 3. 这种继承称为菱形继承,或者钻石继承
          • 问题:可能会出现二义性
            • 解决:虚继承 virtual
            • vdptr: 虚基类指针

          • 示例

      • 9.4.7 多态
        • 9.4.7.1 多态的基本概念
          • 多态分为两类
            • 1. 静态多态函数重载运算符重载 属于静态多态,复用函数名
            • 2. 动态多态派生类和虚函数实现运行时多态
          • 静态多态和动态多态区别:
            • 1. 静态多态的函数地址早绑定 - 编译阶段确定函数地址
            • 2. 动态多态的函数地址晚绑定 - 运行阶段确定函数地址
          • 动态多态的条件
            • 1、有继承关系
            • 2、子类重写父类的虚函数
              • 重写:子类中的函数返回值类型、函数名称、参数列表与父类完全相同
          • 动态多态的使用
            • 1、父类指针 指向 子类的对象
          • 示例:
            • class Animal
              {
              public:
                  // 虚函数
                  virtual void speak() {
                  cout << "动物在说话" << endl;
                  }
              };
              
              class Cat : public Animal
              {
              public:
                  void speak() {
                      cout << "小猫在说话" << endl;
                  }
              };
              
              // 执行说话的函数
              // 如果不加virtual,地址早绑定,在编译阶段就确定了函数地址
              // 如果想让子类的方法被调用,父类同名方法要加virtual
              void doSpeak(Animal &animal) {
                  animal.speak();
              }
              
              void test01() {
                  Cat cat;
                  doSpeak(cat); // 小猫在说话
              }

          • 多态的底层原理

          • 工具查看底层时间的sizeof的大小

        • 9.4.7.2 多态案例一:计算器
          • 多态的优点:
            • 1. 代码组织结构清晰
            • 2.可读性强
            • 3. 利于前期和后勤的扩展和维护
              • 满足开发原则:对扩展开放、对修改关闭
        • 9.4.7.3 纯虚函数和抽象类
          • 在多态中,通常父类的虚函数实现是毫无意义的,主要都是调用子类重写的内容
          • 因此可以将虚函数改为纯虚函数
          • 纯虚函数语法:virtual 返回值类型 函数名 (参数列表) = 0
          • 当一个类中有了纯虚函数,这个类就被称为抽象类
            • 抽象类的特点:
              • 1. 无法实例化对象
              • 2. 子类必须重写抽象类中的纯虚函数,否则也属性抽象类
        • 9.4.7.4 多态案例二:制作饮品
        • 9.4.7.5 虚析构和纯虚析构
          • 多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用子类的析构代码
            • 解决方式:将父类的析构函数改成虚析构或者纯虚析构
          • 虚析构和纯虚析构的共性:
            • 1、可以解决父类指针释放子类对象
            • 2、都需要具体的函数实现
          • 虚析构和纯虚析构的区别:
            • 1、如果是纯虚析构,该类属于抽象类,无法实例化对象
          • 虚析构语法:virtual ~类名( ){ }
          • 纯虚析构语法:
            • 1、virtual ~类名( ) = 0;
            • 2、类名::~类名( ){ }
          • 总结
          • 1、虚析构和纯虚析构是用来解决父类指针释放子类对象的问题
          • 2、如果子类没有堆区数据,可以不写为虚析构和纯虚析构
          • 3、拥有纯虚析构的类也属于抽象类
        • 9.4.7.6 多态案例三:电脑组装
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值