C++day10 学习笔记

1、类和对象
   类就是对对象的描述,主要从属性和行为两个方面描述。
   对于属性一般作成private , 行为作为public
   函数 (1)构造函数,初始化所有的成员变量,系统自动调用,可以重载
        (2)析构函数,在对象生命周期结束的时候自动被调用调用,不准重载
               构造函数和析构函数都是系统自动调用的,析构函数可以通过对象调用  
               A a;
               a.A();   //error 构造函数是不能手工调用的
               a.~A();  //right 手工调用析构函数时,会被当作一个普通的成员函数调用,其中的代码会被执行,对象不被销毁
        (3)get,set方法  用于访问私有的成员变量的,外界访问变量的唯一通道
        (4)类本身的行为 是我们在编码时要下功夫的地方
2、类的组成
   (1)数据部分
   (2)构造函数和析构函数
   (3)get & set方法
   (4)业务方法

3、栈的实现(存储int类型的栈)                          
   要想实现,需要一个数组做容器,保存用户插入的数据,用指针实现; int *p;
             还需要一个变量,保存栈顶位置   int top;   //top从0开始
             对于栈不能无限制的存数据,所以需要一个int类型的变量来记载数组的最大长度   int max;
   (1)构造函数
        当不给参数的时候,栈的长度是默认值Stack();也可以用户指定长度Stack(int)
   (2)析构函数
        一定要有,因为要在堆中申请空间
   (3)为保护栈数据的安全,对p不能提供get,set 方法
        对于top,max的值,取决于插入的数据,所有只能有get方法,不能设置,不需要有set方法
   (4)int push(int);  插入,当栈满的时候,返回-1,表示插入失败
        int pop();      删除栈顶的元素,如果正确删除一个数据,返回数据的值,若没有正确删除,返回-1,表示栈空了
        void disp();    现实数据
   (5)实现的时候,注意:
        在函数名前加 " Stack:: "
   (6)在栈中插入数据,就是在top的位置插入数据
              删除数据,就是把栈顶下移一个位置

4、继承
   (1)继承表现一种" is a " 的关系。Teacher extend Person
        其中Teacher是子类,Person是父类
        子类继承父类的所有属性和行为
   (2)class Teacher:public Person{};
         表示Teacher类继承Person类

   在extends_sample2中,子类可以直接使用父类的teach函数,但是不能使用父类private的name属性。name属性实际上存在在子类中,但是不能被子类直接使用,因为是private属性。private属性只能被本对象的函数访问,public的属性可以被子类直接使用,但是外部函数也可以访问public权限的属性。既想被子类使用又不想让外部函数访问,怎么办哪?使用protected修饰属性就可以了。

     下面是一个类的属性具有不同的访问控制修饰符在不同的对象中的可见性i(可见性就是可以访问):

                 本对象  子类对象  其他函数
 private属性      可见    不可见     不可见
 protected属性    可见     可见      不可见
 public属性       可见     可见       可见

    在继承关键字extends前面也有三种不同的访问控制修饰符号,被不同的继承方式继承后访问控制权限会发生变化。可以把继承方式理解成城门。无论外边的人多么胖,想通过不同宽度的城门只能减肥到相应的宽度才可以。

                      public extends       protected extends       private extends
父类的private属性       不能访问               不能访问                  不能访问
父类的protected属性  变成protected                 不变             变成private,子类可以访问,子类的子类不能访问
父类的public属性          不变               变成protected          变成private,子类可以访问,子类的子类不能访问

    构造函数不能被继承。因为构造函数只是适合于本类的产生方式。

    如extends_sample4,创建子类的时候需要首先创建父类。怎么理解哪?     考虑子类的构成,就像cs中的匪徒用的46是由ak47添加上瞄准镜组成的。

    创建子类的时候会首先调用父类的构造函数,因为继承的时候没有指定继承时使用的父类的构造函数。     构造函数有很多种,因为没有指定构造函数,就会默认使用无参的构造函数。如果父类没有无参的构造函数,那么就会出现编译错误。

    这是问题的产生,如何解决哪?     可以在父类中添加无参构造函数。如果我们不是父类的设计者,就应该在子类继承的时候指定使用父类的那个构造函数。     如在写子类构造函数时,使用这种形式Teacher(char* name, int age, double salary):Person(name,age){......},就可以指定使用父类的有参构造函数。

    构造时候是先父类后子类。析构函数哪?析构函数不能被继承。子类释放的时候,首先调用自身的析构函数,再调用父类的析构函数。这与构造函数的调用顺序相反。

5、在子类中可以修改父类的行为,叫方法的覆盖
   (1)在子类中的函数名必须与父类的一样
   (2)隐藏,无法产生多态

        class Base{
        public:
              void fn( int a ){
                    cout<<"Base a = " << a << endl;
             }
        };
        class Sub:public Base{
        public:
              void fn(  ){
                    cout<<"Sub b = 20" << endl;
              }
        };

    (3)调用父类的方法

          a.Base::fn(10);  //可以有针对性的调用父类的函数

6、函数的高内聚,低耦合
   高内聚:函数的功能尽量单一,这样的代码可维护性高
   低耦合:避免修改函数的时候,产生连锁反映。
  
7、多态
   (1)什么是多态?
        一个int类型的指针,只能指向一个int类型的变量
        对于对象来讲Person类型的指针 ,能指向Person类型的对象
        Person类型的指针能指向一个Teacher(extend Person)类型的对象
       
   (2)多态的特征:
        父类的指针可以指向子类的对象
        通过父类指针只能调用父类中声明的方法
        通过指针调用函数的时候,若函数是个虚函数,表现出来的行为是对象自身的行为
       
        Person *p = new Teacher ;
        编译时类型     运行时类型
       
   (3)子类的指针不能指向父类对象
        因为子类中会有子类特有的函数,运行时不能通过指针调用子类特有的函数
       
   (4)" virtual "父类函数的返回值前加此关键字,则为虚函数
  
   (5)产生多态的必要前提:
        继承,方法覆盖,虚函数

8、虚函数的实现原理   在每个对象中都有一个虚函数列表的指针,虚函数列表是一个栈。   在构造对象时,根据先构造父类,再构造子类的原则,父类的函数先入栈,在子类中覆盖的函数放在上面。   等对象构造完毕时,子类的函数在最上面,根据栈后进先出的原则,先调用的是子类函数

9、在释放资源的时候    delete p ;    只会调用Person类的析构函数,因为指针的类型是Person的,这样会造成子类的空间得不到及时的释放,会造成内存泄露    把析构函数也写成虚函数,这样就会先调用子类的析构函数,再析构父类的析构函数       在继承关系中,父类的析构函数必须是虚函数!!!

10、多态的使用

        #include <iostream>
        using namespace std;
        
        class Person{
        public:
            virtual double buy(){          
                return 2 ;
            } 
        };
        
        class Teacher : public Person{
        public:
            virtual double buy(){
                return 1 ;
            }
        };
        
        class Student : public Person{
        public:
            virtual double buy(){
                return 0.5 ;
            }
        };
        
        class CEO : public Person{
        public:
            virtual double buy(){
                return 1000 ;
            }
        };
        
        void shoufei( Person * p ){
            cout<< p->buy() << endl;
        }
        
        
        int main(){
            Person p ;
            Teacher t ;
            Student s ;
            CEO c ;
        
            shoufei( &p ) ;    //通过传入不同对象的地址,调用相应的函数
            shoufei( &t ) ;    //与if...else对比
            shoufei( &s ) ;    //写程序要尽量少改动写好的源码,这样实现代码的通用及低耦合
            shoufei( &c ) ;
        
            return 0 ;     
        } 

作业:写一个校车类,用数组装乘客Bus
      Bus : 上车,下车 , 卖票等行为
            车上有Teacher,Student ,计算总票价

原文地址:https://www.cnblogs.com/tangzhengyue/p/2622611.html