继承:从已有的类派生出新类

基类和派生类

不同的类之间可能有很多共通的属性和行为,这些属性和行为允许在一个类中通用化并被其他类所共享。类C1从类C2扩展而来,则C1称为派生类,C2称为基类,基类也称为父类或者超类,派生类称为子类,一个子类继承了其父类所有可访问的数据域和函数。同时可以增加新的数据域和函数

例如:

定义基类geometric,在此基础上生成circle类:
基类Geometric:

geometric.h文件

#ifndef GEOMETRIC_H
#define GEOMETRIC_H#include <string>
using namespace std;class Geometric
{private:string color;bool filled;public:Geometric();Geometric(const string& color, bool filled);string getColor() const;void setColor(const string& color);bool isFilled() const;void setFilled(bool state);string toString() const;
};
#endif

geometric.cpp文件

#include <string>
#include <iostream>
#include "E:\back_up\code\c_plus_code\chapter15\external_file\geometric.h"using namespace std;Geometric::Geometric()
{color = "white";filled = true;
}Geometric::Geometric(const string& color, bool filled)
{this->color = color;this->filled = filled;
}string Geometric::getColor() const
{return color;
}void Geometric::setColor(const string& color)
{this->color = color;
}bool Geometric::isFilled() const
{return filled;
}void Geometric::setFilled(bool state)
{filled = state;
}string Geometric::toString() const
{return "Geometric object";
}

派生类Circle

circle.h文件:

#ifndef CIRCLE_H
#define CIRCLE_H
#include "E:\back_up\code\c_plus_code\chapter15\external_file\geometric.h"
#include <string>class Circle: public Geometric  // 表明circle类都是从Geomerric类中派生出来的 Circle继承了所有的公有成员
{private:double radius;public:Circle();Circle(double radius);Circle(double radius, const string& color, bool filled);double getRadius() const;void setRadius(double radius);double getArea() const;   // 获取面积 double getPerimeter() const;  // 获取周长double getDiameter() const;   // 直径string toString() const; };
#endif

circle.cpp文件

#include <string>
#include <iostream>
#include "E:\back_up\code\c_plus_code\chapter15\external_file\circle.h"using namespace std;Circle::Circle()
{radius = 1;
}Circle::Circle(double radius)
{this->radius = radius;
}Circle::Circle(double radius, const string& color, bool filled)
{//this->radius = radius;//this->color = color;      // 这种写法是错误的 //this->filled = filled;    // 这种写法是错误的// 除了在Geomertic类中,它的私有成员color和filled无法在其他任何类中被访问。// 唯一的方法是通过调用get()和set()进行访问 setRadius(radius);setColor(color);     // 继承的成员函数 setFilled(filled);   // 继承的成员函数 }double Circle::getRadius() const
{//return radius;return (radius>0)?radius:0;
}void Circle::setRadius(double radius)
{this->radius = radius;
}double Circle::getArea() const
{return 3.14*radius*radius;
}double Circle::getDiameter() const
{return 2*radius;
}double Circle::getPerimeter() const
{return 2*3.14*radius;
}string Circle::toString() const
{return "Circle object";
}

main.cpp文件

int main(int argc, char *argv[])
{Circle circle1(4.5, "black", true);cout << "Circle1 radius is " << circle1.getRadius() << " and color is " << circle1.getColor() << " and area is " << circle1.getArea() << endl; if(circle1.isFilled()){circle1.setColor(false);} return 0;
}

总结:

1.基类中的私有数据域不能在基类以外被访问,因此在派生类中不能直接使用,但可以通过基类中定义的accessor和mutator来访问和修改它们。

2.继承用来建模is-a关系:

3.c++允许同时扩展多个类来得到一个派生类,该功能被称为多重继承。

泛型程序设计

当程序中需要一个基类对象时,向其提供一个这个基类的派生类对象是允许的。这种特性使得一个函数可以适用于较大范围的对象实参,变得更加通用,称之为泛型程序设计

例如:

void displayGeometric(const Geometric& shape)

{

cout << shape.getColor() << endl;

}

函数displayGeometric()的参数类型是Geometric,但是我们可以向它传递Geometric类的派生类作为参数也可以。

例如:

displayGeometric(Circle(5));

派生类的构造函数和析构函数:

派生类的构造函数在执行自身代码之前,首先调用它的基类的构造函数(派生类不继承基类ide构造函数,仅仅是调用而已)。派生类的析构函数首先执行其自身的代码,然后自动调用其基类的析构函数(不继承,同样是调用)。

1.调用基类构造函数

派生类中的构造函数总是显式或者隐式的调用基类中的构造函数。如果没有被显式调用,则会默认调用基类中无参的构造函数

例如:

Circle::Circle(double radius, const string& color, bool filled):Geometric(color, filled)
{setRadius(radius);
}

构造函数链和析构函数链:

这里用a->b表示b继承a:

假设a->b->c->d, 则在实例化d对象时,d的构造函数执行前,会调用a的构造函数,而a的构造函数执行前,会调用b的构造函数,以此类推,就会按照继承关系形成一个链,称为构造函数链,而析构函数链与构造函数链正好相反。

tips: 如果一个类可能被继承,则最好为它设计一个无参的构造函数

tips: 如果基类中有自定义的拷贝构造函数和赋值操作,因该在派生类中自定义这些来保证基类中的数据域被正确拷贝。

函数重定义

在基类中定义的函数能够在派生类中可以被重新定义。需要在派生类的头文件中添加函数原型,并在派生类的实现文件中提供函数新的实现。

例如:toStrng()函数

如果在子类中任然需要调用父类中的函数,则需要基类名和作用域解析运算符

例如:

cout << circle1.Geometric::toString();  // 在circle对象中调用基类中的toString()函数 

函数重载和函数重定义的区别:

函数重载提供多个名字相同,但签名不同的函数。

函数重定义:必须在派生类中定义一个与基类中函数具有相同签名和返回类型的函数。

多态

多态意味着一个超类型的变量可以引用一个子类型的对象!(对比泛型编程的概念)

OOP三支柱: 封装, 继承性, 多态

一个类定义一种类型:基类称为超类型(subtype),派生类称为子类型(supertype)。

函数dislpayGeometric()的参数类型为Geometric, 但是在调用的函数的时候可以传入circle类型的对象。 这就是多态的概念。

#include <iostream>
#include <string>
//#include "E:\back_up\code\c_plus_code\chapter15\external_file\geometric.h"
#include "E:\back_up\code\c_plus_code\chapter15\external_file\circle.h"
using namespace std;void displayGeometric(const Geometric& g)
{cout << g.toString() << endl;
}
int main(int argc, char *argv[])
{Circle circle1(4.5, "black", true);displayGeometric(circle1);    //  超类型的变量引用子类型的对象 return 0;
}

虚函数和动态绑定

一个函数可以在沿着继承关系链的多个类中实现,虚函数使得系统能够基于对象的实际类型决定在运行时调用哪一个函数。

#include <iostream>
#include <string>
//#include "E:\back_up\code\c_plus_code\chapter15\external_file\geometric.h"
#include "E:\back_up\code\c_plus_code\chapter15\external_file\circle.h"
using namespace std;void displayGeometric(const Geometric& g)
{cout << g.toString() << endl;
}
int main(int argc, char *argv[])
{Circle circle1(4.5, "black", true);Geometric g1;displayGeometric(g1);displayGeometric(circle1);    //  超类型的变量引用子类型的对象 return 0;
}

上面的代码的输出结果如图所示:

在Geometric类和circle类总都定义了toString()函数,但是在语句:

displayGeometric(g1);
displayGeometric(circle1);    //  超类型的变量引用子类型的对象 

被执行时,调用的都是Geometric类中的toString()函数

如何使得在传入相应类型的参数时,使得相应的类中的toString()函数被调用,而不是都调用Geometric类中的toString()函数?

虚拟函数就可以实现这一需求:

virtual string toString() const;   

在基类中,函数toString()被定义成 virtual 类型,c++在运行时动态决定调用哪一个函数。这种功能叫做动态绑定。

实例:

如果不使用虚函数,调用Geometric, circle, Rectangle三个类对象的toString()方法,输出的都是“Geometric  object”

将基类的toString()函数定义为虚函数后,实现了动态绑定,输出的结果为:

总结:
1.要实现动态绑定,函数在基类中要声明为虚函数。而在派生类中不必再声明为虚函数

2. 引用对象的变量必须以引用或者指针的形式传递

3.动态绑定与匹配函数签名是两个独立的问题:编译器根据参数类型,参数个数以及参数顺序在编译时寻找匹配的函数,这是静态绑定,用于匹配函数签名。 动态绑定是由变量所引用的对象的实际类型所决定的,这是动态绑定,一个虚函数可以在多个派生类中实现,C++在运行时动态绑定这些函数的实现。

-------------------------------------------分割线------------------------------------------------------

关键字protected

基类中定义的数据域和函数经常需要允许派生类访问而不允许非派生类访问,为此可以使用关键字protected。

抽象类和纯虚函数

在类的继承层次中,基类到派生类,类的内容越来越具体和明确,从派生类到基类,则越来越一般化和不具体。

在设计类时应该确保基类包含其派生类的共同特性。

例如:

Gemometric类定义几何图形的基类,描述几何对象的公共属性,例如派生类 circle,rectangle类都会包含getArea()函数和getPerimeter()函数,因为所有的几何对象都可以计算面积和周长,因此按照类的设计原则应该在基类Gemometric中声明getArea()和getPerimeter()函数。但是这个函数不能在Gemometric类中实现,因为其实现和具体的几何对象有关。

这样的函数就成为抽象函数,基类Gemometric称为抽象类

纯虚函数的声明方法:

virtual double getArea() const = 0;   // 纯虚函数
virtual double getPerimeter() const = 0;  // 纯虚函数  

注(一个需要注意的地方)

当定义了基类中toString()函数为纯虚函数后,此时的Geometric类为一个抽象类,不能再声明Geometric对象!!!!!

在基类中定义了纯虚函数,就可以实现两个不同派生类之间的一些操作了

例如:

需要比较circle和rectangle之间的面积大小:
定义如下函数即可:

bool equalArea(const Geometric& g1, const Geometric& g2)
{return g1.getArea()==g2.getArea();
}

关于纯虚函数的实现部分,这里代码没有报错,但是执行结果总是不对,贴上来代码:

基类:Geometric

Geometric.h文件

#ifndef GEOMETRIC_H
#define GEOMETRIC_H#include <string>
using namespace std;class Geometric
{private:string color;bool filled;public:Geometric();Geometric(const string& color, bool filled);string getColor() const;void setColor(const string& color);bool isFilled() const;void setFilled(bool state);virtual string toString() const;   // 虚函数,实现动态绑定virtual double getArea() const = 0;   // 纯虚函数 virtual double getPerimeter() const = 0;  // 纯虚函数
};
#endif

Geometric.cpp文件

#include <string>
#include <iostream>
#include "E:\back_up\code\c_plus_code\chapter15\external_file\geometric.h"using namespace std;
Geometric::Geometric()
{color = "white";filled = true;
}Geometric::Geometric(const string& color, bool filled)
{this->color = color;this->filled = filled;
}string Geometric::getColor() const
{return color;
}void Geometric::setColor(const string& color)
{this->color = color;
}bool Geometric::isFilled() const
{return filled;
}void Geometric::setFilled(bool state)
{filled = state;
}string Geometric::toString() const
{return "Geometric object";
} 

派生类Circle

circle.h

#ifndef CIRCLE_H
#define CIRCLE_H
#include "E:\back_up\code\c_plus_code\chapter15\external_file\geometric.h"
#include <string>using namespace std;
class Circle: public Geometric  // 表明circle类都是从Geomerric类中派生出来的 Circle继承了所有的公有成员
{private:double radius;public:Circle();Circle(double radius);Circle(double radius, const string& color, bool filled);double getRadius() const;void setRadius(double radius);double getArea() const;   // 获取面积 double getPerimeter() const;  // 获取周长double getDiameter() const;   // 直径string toString() const;    // 将派生类中的函数重定义 };
#endif

circle.cpp

#include <string>
#include <iostream>
#include "E:\back_up\code\c_plus_code\chapter15\external_file\circle.h"using namespace std;Circle::Circle()
{radius = 1;
}Circle::Circle(double radius)
{this->radius = radius;
}Circle::Circle(double radius, const string& color, bool filled)
{//this->radius = radius;//this->color = color;      // 这种写法是错误的 //this->filled = filled;    // 这种写法是错误的// 除了在Geomertic类中,它的私有成员color和filled无法在其他任何类中被访问。// 唯一的方法是通过调用get()和set()进行访问 setRadius(radius);setColor(color);     // 继承的成员函数 setFilled(filled);   // 继承的成员函数 }double Circle::getRadius() const
{//return radius;return (radius>0)?radius:0;
}void Circle::setRadius(double radius)
{this->radius = radius;
}double Circle::getArea() const
{return 3.14*radius*radius;
}double Circle::getDiameter() const
{return 2*radius;
}double Circle::getPerimeter() const
{return 2*3.14*radius;
}string Circle::toString() const   // 提供重定义函数的实现
{return "Circle object";
}

派生类rectangle

rectangle.h

#ifndef RECTANGLE_H
#define RECTANGLE_H
#include <iostream>
#include <string>
#include "E:\back_up\code\c_plus_code\chapter15\external_file\geometric.h"
using namespace std;class Rectangle: public Geometric
{private:double width;double height;public:Rectangle();    //无参构造函数Rectangle(double width, double height);Rectangle(double width, double height, const string& color, bool filled);// accessordouble getWidth() const;double getHeight() const;double getArea() const;double getPerimeter() const;// mutatorvoid setWidth(double width);void setHeight(double height);string toString() const;//void setFilled(bool filled);//void setColor(const string& color);
};
#endif

rectangle.cpp

#include <iostream>
#include <string>
#include "E:\back_up\code\c_plus_code\chapter15\external_file\rectangle.h"using namespace std;Rectangle::Rectangle()
{width = 1;height = 1;
}Rectangle::Rectangle(double width, double height)
{setWidth(width);setHeight(height);
}Rectangle::Rectangle(double width, double height, const string& color, bool filled)
{setWidth(width);setHeight(height);setColor(color);setFilled(filled);
}double Rectangle::getWidth() const
{return width;
}double Rectangle::getHeight() const
{return height;
}double Rectangle::getArea() const
{return width*height;
}double Rectangle::getPerimeter() const
{return 2*(width+height);
}void Rectangle::setWidth(double width)
{this->width = width;
}void Rectangle::setHeight(double height)
{this->height = height;
}string Rectangle::toString() const
{return "Rectangle object";
}

主函数

main.cpp

#include <iostream>
#include <string>
#include "E:\back_up\code\c_plus_code\chapter15\external_file\geometric.h"
#include "E:\back_up\code\c_plus_code\chapter15\external_file\circle.h"
#include "E:\back_up\code\c_plus_code\chapter15\external_file\rectangle.h"using namespace std;void displayGeometric(const Geometric& g)
{cout << g.toString() << endl;
} bool equalArea(const Geometric& g1, const Geometric& g2)
{return g1.getArea()==g2.getArea();
}int main(int argc, char *argv[])
{Circle circle1(4.5, "black", true);//Geometric g1;Rectangle rec1;//displayGeometric(g1);displayGeometric(circle1);    //  超类型的变量引用子类型的对象 displayGeometric(rec1);cout << "rec1 area is " << rec1.getArea() << endl;cout << equalArea(circle1, rec1);cout << "circle area is " << circle1.getArea() << endl;//cout << "The circle and rectangle area is equal? " << ((equalArea(circle1, rec1))?"Yes":"No") << endl;return 0;}

遇到的问题是代码不能执行

cout << equalArea(circle1, rec1);

这一句之后的代码都执行不了,到这一句直接显示“按任意键结束”???

c++学习笔记(13) 继承和多态相关推荐

  1. c++学习笔记之继承和多态

    1.public 继承 class A : public B (1)友元函数不能被继承 (2)protected成员可以被派生类和友元访问 2.派生类 (1)派生类构造函数如果未显式调用基类构造函数, ...

  2. Spring攻略学习笔记(13)------继承Bean配置

    一:知识点 在Spring IoC容器中配置Bean时,可能拥有一个以上的共享某些公用配置的Bean,比如属性和<bean>元素中的属性.你常常需要为多个Bean重复这些配置. Sprin ...

  3. C# 学习笔记(6) 多态

    C# 学习笔记(6) 多态 面向对象三大特性,封装.继承和多态,前两个很容易理解,但是多态就不好描述了,每个人对多态的看法可能都不一样,个人认为多态就是通过继承实现的不同对象调用相同方法,表现出不同行 ...

  4. C++学习笔记_04抽象类、多态 2021-04-15

    //C++学习笔记_04抽象类.多态 (多重继承的歧义性问题 和 virtual虚继承) #include<cstring> #include<cstdio> #include ...

  5. mybatis学习笔记(13)-延迟加载

    2019独角兽企业重金招聘Python工程师标准>>> mybatis学习笔记(13)-延迟加载 标签: mybatis [TOC] resultMap可以实现高级映射(使用asso ...

  6. opencv进阶学习笔记13:图像形态学操作大全(膨胀,腐蚀,开闭,黑帽,顶帽,梯度)python版

    基础版学习笔记: python3+opencv学习笔记汇总目录(适合基础入门学习) 进阶版笔记目录链接: python+opencv进阶版学习笔记目录(适合有一定基础) 基础版形态学: opencv学 ...

  7. Linux学习笔记13

    Linux学习笔记13 Linux学习笔记13 配置Nagios 基本介绍 Nagios安装 - 服务端 Nagios安装 - 客户端 监控中心添加被监控主机 配置文件的简单说明 继续添加需要服务端通 ...

  8. Hadoop学习笔记—13.分布式集群中节点的动态添加与下架

    Hadoop学习笔记-13.分布式集群中节点的动态添加与下架 开篇:在本笔记系列的第一篇中,我们介绍了如何搭建伪分布与分布模式的Hadoop集群.现在,我们来了解一下在一个Hadoop分布式集群中,如 ...

  9. 台大李宏毅Machine Learning 2017Fall学习笔记 (13)Semi-supervised Learning

    台大李宏毅Machine Learning 2017Fall学习笔记 (13)Semi-supervised Learning 本博客参考整理自: http://blog.csdn.net/xzy_t ...

最新文章

  1. python画动态图代码-Python使用matplotlib画动态图
  2. 44 Wild card Matching
  3. bufferedreader接收不到数据_FreeRTOS例程3-串口中断接收不定长的数据与二值信号量的使用
  4. Elasticsearch系列「」学习路线
  5. asp.core api 通过socket和服务器通信发送udp_python socket之TCP/UDP
  6. 优化器--牛顿法总结
  7. c++ 图的连通分量是什么_学习数据结构第五章:图(图的遍历操作)
  8. php 文字水印如何居中,php文字水印和php图片水印实现代码(二种加水印方法)
  9. 十九、MySQL常用命令总结
  10. 【OpenCV 例程200篇】94. 算术平均滤波器
  11. php教育网站设计案例_南广东区优秀网站设计案例集锦第四期
  12. 玩生死狙击找不到服务器怎么办,生死狙击手游进不了游戏解决方法 生死狙击手游进游戏诀窍...
  13. 数字图像处理入门(冈萨雷斯第三版)
  14. Microsoft Forms产品分析报告
  15. 计算机系统三员试题,2013年计算机软考程序员试题及答案3
  16. 游戏开发新手快速入门指南
  17. Java常用的集成开发工具Eclipse和IDEA
  18. 50万粉丝单场带货破200万!这些快手美妆黑马主播是如何在双十一前夕涨粉又爆单的?
  19. 第五天学习--存储结构与磁盘划分
  20. 【C语言】冒泡排序学习笔记

热门文章

  1. STM32工作笔记005---STM32芯片解读
  2. VS2013 调用的目标发生了异常
  3. 杭电1872稳定排序
  4. shell 删除simatic_安装西门子软件时提示重启电脑的解决办法
  5. 随想录(功能安全和软件开发)
  6. mysql存bitset_用bitSet做百万级ip去重
  7. angular语言前端开发_2020年前端Angular招聘-前端Angular招聘求职信息-拉勾招聘
  8. win10 linux uefi启动不了系统安装教程,win10更新失败,提示硬盘布局不受UEFI固件支持如图,怎么解决?...
  9. MySQL concat函数使用详解
  10. Python来处理数独游戏(含世界最难数独示例)