多态基本知识点

简介: 多态基本知识点

多态


导航:


1.多态基本概念

2.多态小案例

3.纯虚函数

4.虚析构与纯虚析构


———————————————————————————————————

多态的基本概念


多态分为两类:


1.静态多态:函数重载以及运算符重载都属于静态多态,复用函数名

2.动态多态:派生类和虚函数实现运行多态


如何区分:


静态多态的函数地址早绑定——编译阶段确定函数地址

动态多态的函数地址晚绑定——运行阶段确定函数地址


动态多态的满足条件:

1.具有继承关系。

2.子类重写父类的虚函数。(虚函数就是在成员函数前加上关键字virtual)

其中重写条件:类中函数的返回值类型,函数名,参数列表必须完全相同


动态多态的使用方法:父类的指针或者引用指向子类地址。


例子:


#include <iostream>
#include <string>
using namespace std;
class cost   //定义一个父类
{
public:
  void shop() //仅仅只是一个成员函数,函数地址早绑定,也就是早确定函数地址
  {
  cout<<"最高消费0元"<<endl;
  }
};
class shoper1:public cost //定义一个子类
{
public:
  void shop()
  {
  cout<<"最高消费1000元"<<endl;
  }
};
class shoper2:public cost
{
public:
  void shop()
  {
  cout<<"最高消费1500元"<<endl;
  }
};
void doshop(cost &cs) //父类的指针或者引用可以指向子类对象
{
  cs .shop();  //进行成员函数调用
}
void test01()
{
  shoper1 s1;
  doshop(s1);   //进行引用传递
  shoper2 s2;
  doshop(s2);
}
int main()
{
  test01();
  system("pause");
  return 0;
}


运行程序:


最高消费0元
最高消费0元


如何将其重写,然后输出该有结果

这时候就要在父类中的函数前加上virtual,此时便为虚函数,是动态多类,函数地址晚绑定,就可以就行重写。


例如:


public:
  virtual void shop() //加上virtual则叫做虚函数,这样函数地址是晚绑定,不加则就是早绑定,确定了函数地址
  {
  cout<<"最高消费0元"<<endl;
  }
};


这个时候再运行程序:


最高消费1000元
最高消费1500元


———————————————————————————————————

类中仅有一个函数,那么就是一个空类,占1个字节

若类中有一个虚函数,那么占4个字节

虚函数-vfptr (虚函数表指针)指向vftable-(虚函数表)


当子类重写父类的虚函数

子类的虚函数表内部会替换成子类虚函数地址


利用普通和多态写的案例:


#include <iostream>
#include <string>
using namespace std;
//普通写法
class Calculator
{
public:
  int getresult(string oper)
  {
  if(oper == "+")
  {
    return num1+num2;
  }
  if(oper == "-")
  {
    return num1-num2;
  }
  if(oper == "*")
  {
    return num1*num2;
  }
  return 0;
  }
  int num1;
  int num2;
};
//普通的进行测试
void test()
{
  Calculator c1;
  c1.num1 = 100;
  c1.num2 = 100;
  cout<<c1.num1<<"+"<<c1.num2<<"="<<c1.getresult("+")<<endl;
  cout<<c1.num1<<"-"<<c1.num2<<"="<<c1.getresult("-")<<endl;
  cout<<c1.num1<<"*"<<c1.num2<<"="<<c1.getresult("*")<<endl;
}
//利用多态进行写
class AbstractCalculator
{
public:
  virtual int getresult()
  {
  return 0;
  }
  int num1;
  int num2;
};
//加法
class AddCalculator:public AbstractCalculator
{
public:
  int getresult()
  {
  return num1 + num2;
  }
};
//减法
class SubCalculator:public AbstractCalculator
{
public:
  int getresult()
  {
  return num1 - num2;
  }
};
//乘法
class MulCalculator:public AbstractCalculator
{
public:
  int getresult()
  {
  return num1 * num2;
  }
};
void test02()
{
  //调用加法
  AbstractCalculator *abs = new AddCalculator;//父类指针或者引用指向子类对象
  abs->num1 = 100;
  abs->num2 = 100;
  cout<<abs->num1<<"+"<<abs->num2<<"="<<abs->getresult()<<endl;
  delete abs; //手动进行释放
  //调用减法
  abs = new SubCalculator;
  abs->num1 = 100;
  abs->num2 = 100;
  cout<<abs->num1<<"-"<<abs->num2<<"="<<abs->getresult()<<endl;
  delete abs;
  //调用乘法
  abs = new MulCalculator;
  abs->num1 = 100;
  abs->num2 = 100;
  cout<<abs->num1<<"-"<<abs->num2<<"="<<abs->getresult()<<endl;
  delete abs;
}
int main()
{
  test02();
  system("pause");
  return 0;
}



用多态写的好处是:有利于扩展,结构清晰,可读性强


———————————————————————————————————

纯虚函数

语法:virtual 返回值类型 函数名 (参数列表) = 0

若父类中含有一个纯虚函数,那么称为抽象类,无法进行实例化

同时子类中也无法实例化对象,也被称为抽象类


例子:


#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
  //纯虚函数
  virtual void func() = 0;  
};
class son:public Base
{
public:
  virtual void func()
  {
  cout<<"子类中func的调用"<<endl;
  }
};
void test()
{
  //Base b1;  //其中有纯虚函数,无法实例化
  //new Base; //也无法实例化
  Base *b = new son;
  b->func();
  delete b; //手动释放
}
int main()
{
  test();
  system("pause");
  return 0;
}


———————————————————————————————————虚析构与纯虚析构

共同点:


1.可以解决父类指针释放子类对象

2.都需要有具体的函数实现


区别:


如果是纯虚析构,该类属于抽象类,无法实例化对象


虚析构:virtual ~类名() {}

纯虚析构: virtual ~类名() = 0 (需要声明,也要有代码的实现)


能用到纯析构,是因为要在堆区开辟新数据,父类指针释放子类对象

例子:


#include <iostream>
#include <string>
using namespace std;
//纯析构和纯虚函数
class Animal
{
public:
  Animal()
  {
  cout<<"Aniaml构造函数调用"<<endl;
  }
  //纯虚函数
  virtual void speak() = 0;
  //利用虚析构可以解决 父类指针释放子类对象时不干净问题
  //纯虚析构 必须要有代码实现 需要声明也需要实现
  //有纯虚析构 也无法进行实例化对象
  virtual ~Animal() = 0;
  //虚析构
  /*virtual ~Animal()
  {
  cout<<"Aniaml析构函数调用"<<endl;
  }*/
};
//类外进行代码实现
Animal::~Animal()
{
  cout<<"Aniaml纯析构函数调用"<<endl;
}
class Cat:public Animal 
{
public:
  Cat(string name)
  {
  cout<<"Cat构造函数调用"<<endl;
  m_Name = new string(name);
  }
  virtual void speak()
  {
  cout<<*m_Name<<"小猫在说话"<<endl;
  }
  ~Cat()
  {
  if(m_Name != NULL)
  {
    cout<<"Cat虚构函数调用"<<endl;
    delete m_Name;  //释放内存
    m_Name = NULL;
  }
  }
  string *m_Name; //指针
};
void test01()
{
  Animal *animal = new Cat("Tom"); //父类指针在析构时不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏存在
  animal->speak();
  delete animal;
}
int main()
{
  test01();
  system("pause");
  return 0;
}


总结:


1.虚析构或纯虚析构就是用来解决通过父类指针释放类对象

2.如果子类中没有堆区数据,可以不写为虚析构式或纯虚析构

3.拥有纯虚析构函数的类也属于抽象类,无法进行实例化


相关文章
|
C语言 数据安全/隐私保护 C++
19 C++ - 类和对象的基本概念
19 C++ - 类和对象的基本概念
53 0
|
存储 设计模式 编译器
C++进阶之多态(下)
通过观察测试我们发现b对象是8bytes,除了_b成员,还多一个__vfptr放在对象的前面(注意有些平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫
|
2月前
|
编译器 C++
C++入门12——详解多态1
C++入门12——详解多态1
47 2
C++入门12——详解多态1
|
2月前
|
C++
C++入门13——详解多态2
C++入门13——详解多态2
87 1
|
6月前
|
Java 开发者
Java面向对象编程(OOP)的四个主要概念
【6月更文挑战第19天】Java OOP的基石是封装、抽象、继承和多态。封装是通过类隐藏数据细节;抽象简化复杂性,用接口和抽象类实现;继承让子类从父类继承属性和行为,促进代码重用;多态一个接口可有多种形态,分编译时和运行时。这些概念支持灵活且可扩展的软件设计。
53 1
|
5月前
|
存储 编译器 C++
C++基础知识(七:多态)
多态是面向对象编程的四大基本原则之一,它让程序能够以统一的接口处理不同的对象类型,从而实现了接口与实现分离,提高了代码的灵活性和复用性。多态主要体现在两个层面:静态多态(编译时多态,如函数重载)和动态多态(运行时多态,主要通过虚函数实现)。
|
7月前
|
C++
关于C++多态 的基本知识 与 底层原理
关于C++多态 的基本知识 与 底层原理
|
编译器 C++
C++进阶之多态(上)
多态的概念:通俗来说,去完成某个行为,当不同的对象去完成时会产生出不同的状态 。
|
程序员
面向对象编程的简述
面向对象编程的简述
50 0
面向对象程序设计(OOP)的基本概念
面向对象程序设计(OOP)的基本概念
202 0
下一篇
DataWorks