C++——(继承,多态)

本文详细探讨了C++中的继承机制,包括公共、私有和受保护继承的区别,构造函数和析构函数的调用顺序,以及如何处理继承中的同名成员。此外,讲解了多继承和多态的概念,重点介绍了虚函数、纯虚函数、抽象类以及虚析构函数的应用,以解决内存泄露问题。

继承

  • 子类通过继承的方法可以调用到父类的功能
clacc CPeople  //父类 基类
{
public:
    cout << "学习";
};

class CChild : public CPeople  // CChild是子类,继承CPeople父类
{
public:
    void GoToSchool()
    {
        cout << "去学校" <<endl;
    }
};

int  main ( ) 
{ 
    CChild XiaoMing ; 
    XiaoMing . study ;   // XiaoMing是CChild类,CChild了类中没用定义study方法,但是他继承了CPeople方法,所以可以使用该方法。 
} 

继承中的运算符

  • public:共有的,都可以使用
  • private:纯私有,类内可见
  • protected:受保护的,类内、子类可见
  1. 有public时为共有继承,对象在父类中是哪种运算符,在子类中也是哪种运算符。父类的私有成员也被子类继承了,在子类中依然是父类的私有成员,他可以继承,但是不能调用。
  2. 有protected的时候,继承之后,父类的public降级成protected,父类中的protected,private不变。
  3. 有private的时候,私有继承之后,父类对象的public和private成员都会变成private成员,父类的private成员还是访问不到
clacc CPeople   //父类 基类 
{ 
private : 
    void fun1()
    {
        
    }
protected:
    void fun2()
    {
        
    }
public:
    void fun3()
    {

    }
};

class CChild : public CPeople  //有public时为共有继承
{
public:
    int a;
};

int main()
{
    CChild XiaoMing;
    XiaoMing.fun3(); 
}

继承的构造函数调用顺序

构造函数的调用顺序:先父类,后子类

clacc CPeople  //父类 基类
{
public : 
    cout  <<  "people" ; 
} ; 

class  CXiaoMing  :  public  CPeople   
{ 
public : 
    CXiaoMing ( ) 
    { 
        cout  <<  "XiaoMing"  << endl ; 
    } 
} ; 

class  CJob  :  public  CXiaoMing   
{ 
public : 
    CJob ( ) 
    { 
        cout  <<  "Job"  << endl ; 
    } 
} ; 

int  main ( ) 
{ 
   CJob job ; 
   cout  <<  job ; 
} 
// 会输出:people xiaoming job,顺序为先父类,后子类 

继承的析构函数调用顺序

析构函数调用顺序与构造函数正好先反:先子类,后父类

从父类继承的成员属不属于子类对象

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

  • 查看类的结构:
    1. 利用开发人员命令提示符
    2. cd 到类的路径下
    3. 查看命令dir
    4. 查看命令:cl /d1 reportSingleClassLayout类名(中文:报告单个类布局)(开头时CL空格/D一)

继承中同名的成员处理

当自身的成员与父类成员同名(仅同名就可以,有无参数不影响),同名的子类自身的成员会隐藏掉所有同名父类成员(包括同名不同参的),想要调用父类的成员可以s.farther ::a;加一个父类作用域,想调用子类中没有的同名不同参的成员也要加类名作用域

  • 继承同名的静态成员子类处理方式

// 父类 
    class Base 
    {
    public:
    // 静态成员变量
      static int m;
    // 同名的静态成员函数
      static void fun()
      {
        cout << "父类的fun"
      }
    };
    int Son::m =100;
    
 // 子类   
    class Son :public Base
   {
    public:
    // 静态成员变量
     static int m;  
    // 同名的静态成员函数
      static void fun()
      {
        cout << "子类的fun"
      }
    };
    
    int Base::m =200;
    
     void test1()
     {
      Son s;
      cout << s.m;  // 返回子类200
      cout << s.Base::m;   // 加上父类的类名作用域后,返回父类100
      
      void test2()
      {
        Son y;
        // 1.通过对象访问
        s.fun();
        s.Base::fun();
        
        // 2.通过类名访问
        Son::fun();
        
        // 3.子类通过类名的方式访问父类
        Son::Base::fun();
        
      }
      
     }

多继承

语法: class 子类 :父类1 : 父类2 : 父类3

  • 由于多继承会引发父类中同名成员,实际开发中不建议使用

多态

多态是一种编程思想,虚函数是实现这个思想的语法基础

  • 父类的一个指针,可以有多种执行状态,即多态

  • 多态是一种泛型编程思想同样的代码实现不同的功能,宏观的体现是父类的指针调用子类函数CFather* fa = new CSon;

  • 多态分为两种:

    • 静态多态:函数重载运算符重载属于静态多态复用函数名
    • 动态多态:派生类虚函数实现运行时的多态。
  • 两种多态的区别:

    • 静态多态的函数地址早绑定—编译阶段确定函数地址(父类引用指向子类对象时,会调用父类的对象;早绑定的地址即为父类的引用。)
    • 动态多态的函数地址晚绑定—运行阶段确定函数地址(子类重写父类的虚函数。)
  • 虚函数的关键词:virtual,有这个关键字表明这个函数是个虚函数,在父类的指针调用子类函数的时候,如果子类对象调用父类和子类都有同名的函数,则调用子类自己的函数。

  • 变量是没有虚的,只有函数有虚函数。

class CFather 
{
public:
    // 父类的指针调用子类函数时,会调用父类的函数
    void show()
    {
        cout<< "class father";
    }
    // 该函数是个虚函数,父类的指针调用子类函数时,会调用子类的函数
    virtual void shuchu()  
    {
        cout<< "fafafa";
    }
};

class CSon : public CFather
{
    int aa;
    void show()
    {
        cout<< "class son";
    }
    void shuchu()
    {
        cout<< "sososo";
    }
};

int main()
{
    CFather* fa = new CSon  //父类的指针调用子类函数
    
    fa.show();  // 会调用父类的show函数,由定义的类型决定
    fa.shuchu(); // 会调用子类的shuchu函数

    free(fa);
}
  • 覆盖是子类的对象名和父类的对象名是相同的,然后在子类的作用范围内,子类的对象就会把父类的对象给覆盖掉。
  • 重写是针对虚函数,在覆盖的基础上,在父类的函数前面加上一个virtual就构成了一个虚函数,就构成了一个重写关系,子类重写父类。

纯虚函数和抽象类

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

  • 纯虚函数的语法:virtual 返回值类型 函数名 (参数列表) = 0;
    当类中有一个纯虚函数这个类被称为抽象类
  • 抽象类的特点:
    1. 无法实例化对象
    2. 子类必须重写抽象类中纯虚函数,否则也属于抽象类。
class  Base 
{ 
public : 

	virtual  void  func  =  0 ; 
} ; 



class Son1 :public Base
{
public :

};

class Son2 :public Base
{
public:
	virtual void func()
	{
		cout << "Son2's func" << endl;
	}
};

void test()
{
	// 两个会报错,抽象类无法实例化对象
	Base b;
	new Base;

	//由于Son1没有重写抽象类的纯虚函数,所以也是抽象类,无法实例化,会报错
	Son1 a;

	Base* base = new Son2;
	base->func(); //就会输出Son2的func函数:Son2's func
}

虚析构函数与纯虚析构

父类指针,在析构时不会调用子类中的析构函数,导致子类如果有堆区属性,会出现内存泄露

利用虚析构函数,把父类中的析构函数定义为虚函数,这样在子类中重写虚函数就可以避免内存泄漏的出现

// 虚析构 
virtual  ~ Animal ( ) 
{ 
    delete  pt ; 
} 

// 纯虚析构 
virtual  ~ Animal ( )  =  0  ; 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值