首页 > 其他分享 >【cplusplus教程翻译】多态(Polymorphism)

【cplusplus教程翻译】多态(Polymorphism)

时间:2023-05-25 15:14:52浏览次数:47  
标签:Polymorphism Polygon area int cplusplus 多态 height 基类 public

多态(Polymorphism)

学习本章之前,需要正确理解指针和继承,如果忘记下面表达式的含义,需要回顾之前的章节

基类指针(Pointers to base class)

继承的一个关键特性就是派生类的指针可以类型安全地转换成基类指针,多态就是利用这个简单通用特性的艺术

// pointers to base class
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b; }
};

class Rectangle: public Polygon {
  public:
    int area()
      { return width*height; }
};

class Triangle: public Polygon {
  public:
    int area()
      { return width*height/2; }
};

int main () {
  Rectangle rect;
  Triangle trgl;
  Polygon * ppoly1 = &rect;
  Polygon * ppoly2 = &trgl;
  ppoly1->set_values (4,5);
  ppoly2->set_values (4,5);
  cout << rect.area() << '\n';
  cout << trgl.area() << '\n';
  return 0;
}

main函数声明了两个基类指针,但是却被赋值成派生类对象的地址,这样的赋值是有效的,因为派生关系。
解引用也是有效的,获得的是指向的对象,上面的调用等效于ppoly1->set_values (4,5); rect.set_values (4,5);
但是由于指针的类型是基类类型(不是派生类类型),只有从基类继承的成员能够被访问,而不是派生类的成员,这也就是为什么上面的程序直接访问area,因为基类的指针访问不了area
如果area是基类的成员,而不是派生类的,那么就可以访问,但是问题是派生类的area有不同的实现所以没办法在基类里加一个公共版本。

虚成员

虚函数是指派生类可以重定义的成员函数,但是通过引用调用的属性还是保留?虚函数的声明语法就是在函数定义前加virtual关键字

// virtual members
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b; }
    virtual int area ()
      { return 0; }
};

class Rectangle: public Polygon {
  public:
    int area ()
      { return width * height; }
};

class Triangle: public Polygon {
  public:
    int area ()
      { return (width * height / 2); }
};

int main () {
  Rectangle rect;
  Triangle trgl;
  Polygon poly;
  Polygon * ppoly1 = &rect;
  Polygon * ppoly2 = &trgl;
  Polygon * ppoly3 = &poly;
  ppoly1->set_values (4,5);
  ppoly2->set_values (4,5);
  ppoly3->set_values (4,5);
  cout << ppoly1->area() << '\n';
  cout << ppoly2->area() << '\n';
  cout << ppoly3->area() << '\n';
  return 0;
}

在这个例子里,三个类都有同样的成员函数和成员变量
area成员函数在基类里被声明成虚函数,然后在每一个派生类里被重定义了,非虚成员函数也可以在派生类里被重定义,但是非虚函数不能通过基类的指针被访问到,例如:如果virtual关键字被移除,那么三个函数调用都会返回0,因为对于这个情况,调用的都是基类版本
因此,本质上来说,virtual关键字的作用是:允许派生类的成员和基类成员同名,但是可以通过基类的指针访问(注意C++都是通过类型进行调用的),更准确地说,就是上面这个例子提到的,指针的类型是基类,但是对象的类型是派生类
声明或者继承了一个虚函数的类就被称为多态类
尽管声明了一个虚函数成员,基类还是一个正常类,可以用来实例化对象

抽象基类(Abstract base classes)

抽象基类和上面例子里的基类很像,虚函数变成了纯虚函数,本质上是不提供函数定义,语法上是函数定义赋值为0

// abstract class CPolygon
class Polygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b; }
    virtual int area () =0;
};
请注意,现在area没有任何定义,也就是纯虚函数,包含一个以上纯虚函数的类被称为抽象基类
抽象基类不能被用来实例化对象(**没有函数定义,没办法分配内存**),因此这个用法是不对的`Polygon mypolygon;   // not working if Polygon is abstract base class`
但是抽象基类也不是完全没用,可以用来定义指针类型发挥多态功能```c++
Polygon * ppoly1;
Polygon * ppoly2

当然也可以用来解引用,显然指针的对象一定是派生类,因为基类没办法实例化对象

// abstract base class
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b; }
    virtual int area (void) =0;
};

class Rectangle: public Polygon {
  public:
    int area (void)
      { return (width * height); }
};

class Triangle: public Polygon {
  public:
    int area (void)
      { return (width * height / 2); }
};

int main () {
  Rectangle rect;
  Triangle trgl;
  Polygon * ppoly1 = &rect;  // 本质上这里发生了类型转换,&rect是由无名对象返回值的
  Polygon * ppoly2 = &trgl;
  ppoly1->set_values (4,5);
  ppoly2->set_values (4,5);
  cout << ppoly1->area() << '\n';
  cout << ppoly2->area() << '\n';
  return 0;
}

这个例子里,类型不同但是相关的对象通过统一的指针类型被引用,由于虚函数的关系,对应的成员函数被调用,这个特性在某些情况下非常有用,例如抽象基类还可以使用this指针

// pure virtual members can be called
// from the abstract base class
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    void set_values (int a, int b)
      { width=a; height=b; }
    virtual int area() =0;
    void printarea()
      { cout << this->area() << '\n'; }
};

class Rectangle: public Polygon {
  public:
    int area (void)
      { return (width * height); }
};

class Triangle: public Polygon {
  public:
    int area (void)
      { return (width * height / 2); }
};

int main () {
  Rectangle rect;
  Triangle trgl;
  Polygon * ppoly1 = &rect;
  Polygon * ppoly2 = &trgl;
  ppoly1->set_values (4,5);
  ppoly2->set_values (4,5);
  ppoly1->printarea();
  ppoly2->printarea();
  return 0;
}

多态对面向对象编程十分有用,当然,上面这个例子很简单,不一定要是局部对象,对象数组和new出来的对象也可以
下面是一个非常完善的例子

// dynamic allocation and polymorphism
#include <iostream>
using namespace std;

class Polygon {
  protected:
    int width, height;
  public:
    Polygon (int a, int b) : width(a), height(b) {}
    virtual int area (void) =0;
    void printarea()
      { cout << this->area() << '\n'; }
};

class Rectangle: public Polygon {
  public:
    Rectangle(int a,int b) : Polygon(a,b) {}
    int area()
      { return width*height; }
};

class Triangle: public Polygon {
  public:
    Triangle(int a,int b) : Polygon(a,b) {}
    int area()
      { return width*height/2; }
};

int main () {
  Polygon * ppoly1 = new Rectangle (4,5);
  Polygon * ppoly2 = new Triangle (4,5);
  ppoly1->printarea();
  ppoly2->printarea();
  delete ppoly1;
  delete ppoly2;
  return 0;
}

注意new那段代码,虽然声明的是基类指针,但是对象是派生类类型

总结

OOP三大特点:封装、继承和多态,靠的是private protect public virtaul关键字
封装通过private public实现,类外只能访问public成员,不能访问private和protected,
继承通过protected丰富了访问权限控制,考虑访问点位置:类内、派生类内、类外,考虑派生权限:private、protected、public,在考虑基类数据成员属性private、protected、public,结合多次派生,就能明白为什么需要三个层级
多态可以避免编译器只通过类型进行访问

标签:Polymorphism,Polygon,area,int,cplusplus,多态,height,基类,public
From: https://www.cnblogs.com/xiaoweing/p/17431171.html

相关文章

  • Java核心之多态
    多态解析:最早学一个变量------>内存空间(小容器) 只有一个后来学一个数组------>内存空间(小容器) 存储一组一样的数据类型 好处是在于堆内存中存储的地址连续 便于循环遍历 数组创建时必须指定长度  频繁的添加或删除元素 个数固定就很不方便再后来学习如何描述类---......
  • 2023.5.21学习内容 多态、接口、泛型、反射
    下午1.了解CSS响应式布局和兼容性问题2.浏览IDEA使用手册并修改Maven仓库设置3.复习强化JavaSE的多态、接口、泛型、反射知识importorg.junit.Test;importtest.Hello;importjava.lang.reflect.Field;importjava.util.ArrayList;importjava.util.LinkedList;import......
  • //#ifdef __cplusplus extern "C" 含义
    #ifdef__cplusplusextern"C"{#endif#import<UIKit/UIKit.h>@interfaceViewController:UIViewController@property(nonatomic,readonly)UIView*headView;@end//#ifdef__cplusplusextern"C"{#endif//一段代码#ifdef__cplu......
  • 方法的重写和多态
    方法的重写静态方法无法重写子类和父类方法名、参数相同,在子类里生成方法重写(override)方法的调用只和左边的类型有关Fatherf1=newSon();f1.run();//此处调用的是Son中的run方法方法的多态只出现在继承关系中子类的方法要重写,本质是父类的引用指向子类父类实例的对象无......
  • Scala:一门灵活多态的编程语言
    基本语法和数据类型Scala的语法有很多共性与Java,它们都是基于C语言的语法规则。Scala中的数据类型也与Java类似,包括整数、浮点数、布尔值和字符串。你可以像Java一样声明和使用这些数据类型。与Java不同的是,Scala中的数值类型默认是基于对象的,因此你可以像调用对象方法一样调用数值......
  • 多态
    表现形式:父类类型对象名称=子类对象多态前提:有继承关系;有父类引用指向子类对象;有方法重写;Fuf=newzi();测试类里要用静态方法,否则无法被main接受......
  • 多态、虚函数表、底层实现、多重继承的问题及处理
    本文代码摘自 http://dwz.date/PST;视频解析:十分钟带你搞明白虚函数、虚表、多态的原理以及多重继承带来的问题_哔哩哔哩_bilibili1、多态:基类指针只能调用基类的成员函数,不能调用派生类的成员函数;如果在基类的成员函数前加上virtual关键字,把它声明为虚函数;基类指针就可以......
  • 【cplusplus教程翻译】名字可见性(Name visibility)
    作用域(Scopes)命名实体,如变量、函数和复合类型,在C++中使用之前需要声明。程序中发生此声明的点会影响其可见性:在任何块外部声明的实体都具有全局作用域,这意味着其名称在代码中的任何位置都是有效的。而在块内声明的实体,如函数或选择性语句,具有块作用域,并且只能在声明它的特定块内......
  • 【C++】多态(下)
    @TOC1.单继承中的虚函数表整体代码#include<iostream>usingnamespacestd;classBase{public:virtualvoidFunc1(){cout<<"Base::Func1()"<<endl;}virtualvoidFunc2(){cout<<"Base::Func......
  • [MAUI程序设计]界面多态与实现
    目录需求一:在不同设备上使用不同UI外观定义控件UI外观基于平台自定义配置需求二:在不同数据类别中使用不同的UI外观定义视图UI外观创建数据模板创建选择器定义数据需求三:在不同数据状态中使用不同的UI外观使用绑定模型更改控件的外观使用视觉状态更改控件的外观项目地址......