IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++继承与多态 -> 正文阅读

[C++知识库]C++继承与多态

? ? ? ? C++是一款面向对象的语言,对象是可以物化的实体,拥有自己的特性与行为,像车子有外形,内核,可以加速减速。而车子又分为保时捷与宝马、奔驰等等。我们想要描述一个车子对象的特征,就需要创建一个类,若为每一种车创建一个类,会使效率下降,而不同车子之间既有共性又有特性,因此,便产生了派生类。

一、派生类的继承方式

派生类(子类)继承了基类(父类)的特性,同时它可以添加自己的诸多特点。继承的方式有以下三种

?

?既:派生类对基类的继承小于等于基类的数据(包括成员变量与成员函数)

无论用那种方式继承,派生类对基类数据的访问都只限于public和protected区域,而不能访问private区域。如果要访问基类的private区域,可以在基类的public或protectd区域里面创建接口

#inlcude<iostream>
using namespace std;
class base
{
  public:
     int func()
        {return m_a}
private:
    int m_a;
};
class derived:public base
{
......
};

派生类derived正常情况下访问不到m_a,但添加了func这个接口,即可通过func来访问。

二、构造派生类对象

构造一个对象,首先调用的是构造函数,派生类继承了除构造函数和析构函数的其他非私有部分,但派生类仍是属于基类的,所以如果创建一个派生类对象,那么构造函数和析构函数的调用顺序如下

#include <iostream>
using namespace std;
class Base
{
public:
	Base(int a)
	{
		ma = a;
		cout<<"Base::base() ";
	}
	~Base()
	{
		cout<<"Base::~base() ";
	}
private:
	int ma;
	Test t;
};
 
class Derive : public Base
{
public:
	Derive(int b):Base(b)
	{
		mb = b;
		cout<<"Derive::derive() ";
	}
	~Derive()
	{
		cout<<"Derive::~derive() ";
	}
private:
	int mb;
};
 
int main()
{
	Derive d(2);
	 
	return 0;
}

Base::base() Derive::derive()?Derive::~derive() Base::~base()

可知,派生类的创建先调用了基类的构造函数和其部分成员变量,再调用派生类构造函数及其部分成员变量,如果基类中还有对象,则应先调用成员对象的构造函数,其中,析构函数的调用顺序与构造函数相反。?

三、多态的初步形成

如果派生类中有和基类中重命名的成员,则有三种可能性:重载,隐藏和覆盖。

1、重载

void Swap1(int* a, int* b);
void Swap2(float* a, float* b);
void Swap3(char* a, char* b);
void Swap4(double* a, double* b);

以上是重载的实现,C++允许在同一作用域中声明几个类似的同名函数,通过函数的参数个数,种类与排列顺序来区分。函数的返回值类型可相同可不同(函数的返回值类型不是区分函数的条件)。C++的函数重载是在编译时完成的,既C++编译器为根据参数的种类,数量或者排列顺序不同将函数区分开来。

2、函数隐藏?

函数隐藏,是指C++中派生类覆盖基类同名成员函数(不考虑参数列表)

class  Base
{
public:
	Base(int a) :ma(a){}
	 void Show()
	{
		std::cout << "Base::ma:" << ma << std::endl;
	}
protected:
	int ma;
};
class Derived :public Base
{
public:
	Derived(int b) :mb(b), Base(b){}
	void Show()
	{
		std::cout << "Derived::mb:" << mb << std::endl;
	}
protected:
	int mb;
};
int main()
{
	Derived a(2);
    a.Show();
    Base* pb = new Derived(10);
	pb->Show();
	return 0;
}

?运行结果:Derived::mb:2

Base::ma:10????????

?即:即使派生类继承了基类的show函数,但派生类中的show函数将基类中的show函数替换掉了,但如果用Base类的指针指向show函数,还是能找到Base类的原函数的,即使它指向的是一个派生类对象(派生类对象本就属于基类,基类指针优先在基类中寻找函数,找到所需函数即停止寻找)。

三、函数覆盖

class  Base
{
public:
	Base(int a) :ma(a){}
	 virtual void Show()
	{
		std::cout << "Base::ma:" << ma << std::endl;
	}
protected:
	int ma;
};
class Derived :public Base
{
public:
	Derived(int b) :mb(b), Base(b){}
	void Show()
	{
		std::cout << "Derived::mb:" << mb << std::endl;
	}
protected:
	int mb;
};
int main()
{
	Base* pb = new Derived(10);
	pb->Show();
	return 0;
}

运行结果:Derived::mb:10

? ? ? ? 当父类函数show()为virtual函数时,调用它的对象类型为指针类型指向的类型,这是一个动态的过程,当类中出现virtual函数时,在编译时期会为对象产生一个虚函数表,将virtual的成员放在里面,我们已经知道,基类的构造早于派生类,所以在创建虚函数表时,将基类的Show函数先放入改表中,到派生类时,派生类中的Show函数(若基类存在virtual函数类型,则系统自认派生类函数为虚函数)将基类的Show函数覆盖,因此,我们用Base虚指针创建Derived对象,再调用其Show函数时,看到的是派生类中的Show函数。

在次,我对基类和派生类中一些指针和引用情况进行简述。

派生类对象可以赋值给基类对象,但基类对象就不可以赋值给派生类对象。好比保时捷肯定是一个车,但你不能见到一个车就说它是保时捷。

基类指针(引用)可以指向派生类对象,但只能访问派生类中基类部分的方法,不能访问派生类部分的方法,派生类指针(引用)不可以指向基类对象,解引用可能出错,因为派生类的一些方法可能基类没有。

?四、虚函数

?下面我们借助一段代码来说明虚函数的一些问题

class A
{
public:
    virtual void foo() { cout << "A::foo() is called" << endl;}
};

class B: public A
{
public:
    virtual void foo() { cout << "B::foo() is called" << endl;}
};
int main()
{A * a = new B();
a->foo(); }

?我们运行程序,得到的会是B的foo函数,如果基类中foo函数不加virtual,那么A的指针将无法访问B中的foo,在将A中的foo定义为虚函数后,如果想用B创建的对象来访问A中的foo函数,在foo函数的前面加上A::作用域即可。

#include?<iostream.h>??
#include?<string.h>??
class?Thing??
{?public:??
virtual?void?what_Am_I(?)?{cout?<<?"My name is Wangyibo./n";}??
 ~Thing(){cout<<"Thing?destructor"<<endl;}??
};??
class?Person?:?public?Thing??
{????
public:??
virtual?void?what_Am_I(?)?{cout?<<?"I?am?Wangyibo./n";}??
~Person(){cout<<"Person?destructor"<<endl;}??
};??
void?main(?)??
{??
???Thing?*t?=new?Thing;????????
???Person*x?=?new?Person;??
???Thing*?a[2];??
???a[0]?=?t;??????????????????????????????????
???a[1]?=?x;?????????????????
????for?(int?i=0;?i<2;?i++)??array->what_Am_I(?)?;??
???delete?a[0];??
???delete?a[1];??
???return?;??
}??

该段代码中,派生类没有定义虚析构函数,会造成delete父类的指针时只调用父类的析构函数,而不调用子类的析构函数。(前面提到过,基类的构造函数和析构函数是不被派生类继承的,基类的指针也就无法指向派生类的析构函数),此时delete a[0],就不会调用虚析构函数,如果在派生类构造函数中存在内存释放,不被调用,就会造成内存泄漏。所以,我们需要在基类中的析构函数前加上virtual时,基类的指针就会先调用派生类的析构函数(存在于虚函数表中),再返回自己的基类中调用基类析构函数。也就是说虚函数的指针和引用可以找到对应函数,而不仅是执行定义类的函数。但,构造函数不能声明为虚函数,因为在执行构造函数前对象没有创建,因此编译时产生的虚函数表里没有构造函数,我们也无法得知调用那个构造函数了。

纯虚函数

纯虚函数的简单模板如下:

class base
{  public:
    virtual base()=0;
};
base::base()
{}

纯虚函数可以是类中的任意函数,只要类中出现纯虚函数,则该类被称为抽象类,即不可以创建对象,只能作为基类模板使用。但即使是抽象类,编译器仍会编译改类,所以我们需要在类外对纯虚函数进行声明。

虚继承

虚继承主要解决的是一个派生类如果继承多个基类,同时基类中又有几类继承同一类的话,在创建对象时,会产生二义性。

class A{
protected:
    int m_a;}
class B:virtual public A{
protected:
    int m_b;
};
class C:virtual public A{
protected:
    int m_c;
};
class D: public B, public C{
public:
    void seta(int a){ m_a = a; }  
    void setb(int b){ m_b = b; }  
    void setc(int c){ m_c = c; }  
    void setd(int d){ m_d = d; }  
private:
    int m_d;
};
int main(){
    D d;
    return 0;
}

?如本段代码,如果我们不在类B和类C继承A时加上virtual? seta函数在运行时,将发生错误,因为它有两个可选的途径,这会造成二义性。添加virtual之后,D类创建对象时,则会选择最近的A类来创建,并且只产生一份m_a的数据。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-19 18:23:00  更:2021-12-19 18:23:16 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 13:58:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码