<转>C++继承中虚函数的使用
转自:http://blog.csdn.net/itolfn/article/details/7412364
一:继承中的指针问题。
1. 指向基类的指针可以指向派生类对象,当基类指针指向派生类对象时,这种指针只能访问派生对象从基类继承
而来的那些成员,不能访问子类特有的元素 ,除非应用强类型转换,例如有基类B和从B派生的子类D,则B
*p;D dd; p=ⅆ是可以的,指针p只能访问从基类派生而来的成员,不能访问派生类D特有的成员.因为基类不
知道派生类中的这些成员。
2. 不能使派生类指针指向基类对象 .
3. 如果派生类中覆盖了基类中的成员变量或函数,则当声明一个基类指针指向派生类对象时,这个基类指针只能
访问基类中的成员变量或函数。例如:基类B和派生类D都定义了函数f,则B *p; D m; p=&m; m.f()将调用基类中
的函数f()而不会调用派生类中的函数f()。
4. 如果基类指针指向派生类对象,则当对其进行增减运算时,它将指向它所认为的基类的下一个对象,而不会指
向派生类的下一个对象,因此,应该认为对这种指针进行的增减操作是无效的.
二:虚函数
1. 为什么要使用虚函数:正如上面第1 和3 点所讲的,当声明一个基类指针指向派生类对象时,这个基类指针只
能访问基类中的成员函数,不能访问派生类中 特有的成员变量或函数 。如果使用虚函数就能使这个指向派生类对
象的基类指针访问派生类中的成员函数,而不是基类中的成员函数,基于这一点派生类中的这个成员函数就必须和
基类中的虚函数的形式完全相同,不然基类指针就找不到派生类中的这个成员函数。使用虚函数就实现了一个接口
多种方法。
2. 注意不能把成员变量声明为虚有的,也就是说virtual关见字不能用在成员变量前面。
3. 正如上面所介绍的,一般应使用基类指针来调用虚函数,如果用点运算符来调用虚函数就失去了它的意义.
4. 如果基类含有虚函数则当声明了一个基类的指针时 ,当基类指针指向不同的派生类时,它就会调用相应派生
类中定义的虚函数版本.这种调用方法是在运行时 决定的 ,例如在类B中声明了虚函数,C,D,E 都从B继承而
来且都实现了自已的虚函数版本,那么当定义了一个B类的指针P时,当P指向子类C时就会调用子类C中定义的
虚函数,当
P指向子类D时就会调用子类D中定义的虚函数 ,当P指向子类E时就会调用子类E中定义的虚函数 .
5. 虚函数须在基类中用virtual 关见字声明也可以在基类中定义虚函数,并在一个或多个 子类中重新定义 .重
定义虚函数时不需再使用virtual关见字,当然也可以继续标明virtual关见字,以便程序更好理解。
6. 包括虚函数的类被称为多态类.C++使用虚函数支持多态性.
7. 在子类中重定义 虚函数时 ,虚函数必须有与基类虚函数的声明完全相同的参数类型和数量,这和重载是不同
的.如果不相同,则是函数重载,就失去了虚函数的本质.
8. 虚函数不能是声明它的类的友元函数,必须是声明它的类的成员函数,不过虚函数可以是另一个类的友元.
9. 一旦将函数声明为虚函数,则不管它通过多少层继承,它都是虚函数,例如D和B继承,而E又从D继承,那
么在B中声明的虚函数,在类E中仍然是虚函数.
10.隐藏虚函数:如果基类定义了一个 虚函数 ,但派生类中却定义了一个虚函数的重载板本 ,则派生类的这个
版本就会把基类的虚函数隐藏掉,当使用基类指针调用该函数时只能调用基类的虚函数 ,而不能调用派生类的重
载版本,当用派生类的对象调用基类的 虚函数时就会出现错误了 ,因为基类的虚函数被派生类的重载版本隐藏了
。
11.带默认形参的虚函数:当基类的虚函数带有默认形参时,则派生类中对基类 虚函数的重定义也必须有相同数
量的形参,但形参可以有默认值也可以没有,如果派生类中的 形参数量和基类中的不一样多 ,则是对基类的虚函
数的重载 。
对虚函数的重定义也就意味着,当用指向派生类的基类指针调用该虚函数时就会调用基类中的虚函数版本。比如基
类定义virtual void f(int i=1, int j=2){}则派生类中必须定义带有两个形参的函数f才是对基类虚函数f的重定
义, 不然就是函数f的重载版本,比如派生类中定义的void f(),void f(int i),void f(int i=2)都是对函数f
的重载,不是对f的重定义。而void f(int i, int j),void f( int i, int j=3),void f(int i=4, int j=5)都
是对虚函数f的重定义。
12.如果虚函数形参有默认值,那么派生类中的虚数的形参不论有无默认值,当用指针调用派生类中的虚函数时就
会被基类的默认值覆盖,即派生类的默认值不起作用 。但用派生类的对象调用该函数时,就不会出现这种情况 。
13.当用指向派生类的基类指针调用虚函数时是以基类中的虚函数的形参为标准的,也就是只要调用的形式符合基
类中定义的虚函数的标准就行了。比如基类中定义virtual void f(int i=1,int j=2){}派生类中重定义为void f
(int i, int j=3){}这时如果用派生类的对象调用这个派生类中的 虚函数 f 时必须至少要有一个实参,但是用指
向派生类的基类指针调用该虚函数时就可以不用任何形参就能调用派生类中的这个函数f, 比如语句p->f()就会调
用派生类中的 虚函数版本 。当用指向派生类的基类指针调用虚函数时是以基类中的虚函数的形参为标准的,也就
是只要调用的 形式符合基类中定义的虚函数的标准就行了。
14.析构函数可以是虚函数,但构造函数不能.
15.纯虚函数声明形式为 virtual 类型 函数名(参数列表)=0;注意后面的等于0;
16.如果类至少有一个纯虚函数,则这个类就是抽象的。
17.如果基类只是声明虚函数而不定义虚函数则此虚函数是纯虚函数 . 任何派生类都必须实现纯虚函数的自已的
版本. 如果不实现纯虚函数那么该类也是抽象类。
18.抽象类不能有对象,抽象类只能用作其它类的基类,因为抽象类中的一个或多个函数没有定义,所以不能用抽
象类声明对象,
19.仍然可以用抽象类声明一个指针,这个指针指向派生类对象.
20.如果派生类中未定义虚函数 ,则会使用基类中定义的函数.
21.虚函数虚拟特性是以层次结构的方式来继承的,例如C从B派生而且C中重定义了B中的虚函数,而D又从C
派生且未重定义B中的虚函数,这时声明一个基类指针P,当P指向类D,并调用D中的虚函数时,由于D中未重
定义虚函数他会调用基类中的虚函数版本,这时他会调用类C中的虚函数而不是类B中的虚函数,因为类C比类B
更接近于类D.
#include <iostream>
using namespace std;
class A
{
public:
int b;
//virtual int b; //错误,不能把成员变量声明为虚有的
virtual void f()
{
cout<<"继续"<<endl;
}
virtual void h(int i=1,int j=2)
{
cout<<"继续H"<<endl;
}
~A(){
cout<<"析构A"<<endl;
}
};
class B:public A
{
public:
int b;
void f(int i)//重载虚函数f
{
cout<<"paif()"<<endl;
}
void f(){//在派生类中重定义虚函数f
cout<<"paixu"<<endl;
}
void h(){ //重载虚函数h的版本。注意这里不是对基类虚函数的重定义。
int b;
b=5;
cout<<"B的"<<b<<endl;
}
void h(int i,int j=3){
int b;
b=j;
cout<<"paixuH"<<b<<endl;
}//当基类中的虚函数有默认形参时,派生类中重定义基类中的虚函数的版本必须有相同数量的形参,
//形参可以有默认值,也可以没有。如果形参数量不一样多则是对虚函数的重载。
~B(){
cout<<"析构B"<<endl;
}
};
int main(){
B m;
A *p=&m;
//p->b=3/错误,指向派生类的基类指针不能调用派生类中的成员,只能调用基类中的成员,
//除非该成员是虚函数
p->f();W//调用派生类中的函数f
//p->f(4);错误,注意这里不是在调用派生类中带一个形参的f函数,因为带一个参数的f函数不是虚函数,
//用指向派生类的基类指针时不会调用派生类中的函数,除非这个函数是虚函数。这里基类中没有定义这种 //带一个形参的f函数,所以这时会出现错误。
p->A::f(); W//调用基类的虚函数f,可以用作用域运算符使用指向派生类的基类指针调用基类的虚函数
p->h();//调用派生类中的虚函数版本h,用指向派生类的基类指针调用虚函数时派生类中的虚函数的默认值在这里不起作用。虽然派生类中的虚函数需要一个参数,
//但这里不给参数仍是调用的派生类的带两个参数的虚函数h,而不是调用派生类中的不带参数的h函数
m.h(); //调用派生类中不带参数的h函数,如果要用对象调用派生类中带两个形参的h函数,在本例中必须使用一个实参值。
m.h(1);//调用派生类中带两个形参的h函数,用对象调用派生类中的虚函数时函数的默认值不受基类虚函数默认值的影响
m.A::h(1);// 调用基类中的虚函数h.
}
<转>C++继承中虚函数的使用的更多相关文章
- C++ 在继承中虚函数、纯虚函数、普通函数,三者的区别
1.虚函数(impure virtual) C++的虚函数主要作用是“运行时多态”,父类中提供虚函数的实现,为子类提供默认的函数实现. 子类可以重写父类的虚函数实现子类的特殊化. 如下就是一个父类中的 ...
- c++中虚函数
虽然很难找到一本不讨论多态性的C++书籍或杂志,但是,大多数这类讨论使多态性和C++虚函数的使用看起来很难.我打算在这篇文章中通过从几个方面和结合一些例子使读者理解在C++中的虚函数实现技术.说明一点 ...
- C++ Pirmer : 第十五章 : 面向对象程序设计之基类和派生的定义、类型转换与继承与虚函数
基类和派生类的定义以及虚函数 基类Quote的定义: classs Quote { public: Quote() = default; Quote(cosnt std::string& bo ...
- C++中虚函数的作用浅析
虚函数联系到多态,多态联系到继承.所以本文中都是在继承层次上做文章.没了继承,什么都没得谈. 下面是对C++的虚函数这玩意儿的理解. 一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你 ...
- C++中虚函数的作用是什么?它应该怎么用呢?(转)
虚函数联系到多态,多态联系到继承.所以本文中都是在继承层次上做文章.没了继承,什么都没得谈. 下面是对C++的虚函数这玩意儿的理解. 一, 什么是虚函数(如果不知道虚函数为何物,但有急切的想知道,那你 ...
- C++中虚函数功能的实现机制
要理解C++中虚函数是如何工作的,需要回答四个问题. 1. 什么是虚函数. 虚函数由于必须是在类中声明的函数,因此又称为虚方法.所有以virtual修饰符开始的成员函数都成为虚方法.此时注意是vir ...
- 从零开始学C++之虚继承和虚函数对C++对象内存模型造成的影响
首先重新回顾一下关于类/对象大小的计算原则: 类大小计算遵循结构体对齐原则 第一个数据成员放在offset为0的位置 其它成员对齐至min(sizeof(member),#pragma pack(n) ...
- c++虚继承与虚函数
学习继承与多态时看到这两个概念,记录整理. 虚继承与虚函数都是用virtual关键字实现,虚继承为了防止多重继承,而虚函数为了实现多态. 是几个例子. 虚继承: class A{}; class B: ...
- C++多重继承分析——《虚继承实现原理(虚继承和虚函数)》
博客转载:https://blog.csdn.net/longlovefilm/article/details/80558879 一.虚继承和虚函数概念区分 虚继承和虚函数是完全无相关的两个概念. 虚 ...
随机推荐
- 利用opencv进行简易的拍照并处理照片
今天用python写了一个调用摄像头拍照并对图片进行素描化或动漫化的小demo. 首先我的环境是:PyCharm+python3.8+opencv-python(4.4.0.42) 我们分析一下思路, ...
- Java8-JVM内存区域划分白话解读
前言 java作为一款能够自动管理内存的语言,与传统的c/c++语言相比有着自己独特的优势.虽然我们无需去管理内存,但为了防范可能发生的异常,我们需要对java内部数据如何存储有一定了解,已应对突发问 ...
- lilypond和弦及其转位的表示
在lilypond,如果要打和弦的话,有所谓的chordmode,命令就是\chordmode {} 要使用chordmode需要一些基本的和弦命名的知识,最好先补一下乐理 实际上lilypond的官 ...
- 前端---梳理 http 知识体系 2
为什么要有HTTPS HTTP 天生具有明文的特点,整个传输过程完全透明,任何人都能够在链路中截获.修改或者伪造请求 / 响应报文,数据不具有安全性.仅凭HTTP 自身是无法解决的,需要引入新的HTT ...
- Java设计模式之(十一)——享元模式
1.什么是享元模式? Use sharing to support large numbers of fine-grained objects efficiently. 享元模式(Flyweight ...
- Mysql-单个left join 计算逻辑(一对多问题)
BUG背景: 我们有一个订单表 和 一个 物流表 它们通过 订单ID 进行一对一的关系绑定.但是由于物流表在保存订单信息的时候没有做判断该订单是否已经有物流信息,这就变成同一个订单id在物流表中存在多 ...
- Dreamweaver 2019 软件安装教程
下载链接:https://www.sssam.com/1220.html#软件简介 Adobe Dreamweaver,简称"DW",DW是集网页制作和管理网站于一身的所见即所得网 ...
- JAVA中复制数组的方法
在JAVA里面,可以用复制语句"A=B"给基本类型的数据传递值,但是如果A,B是两个同类型的数组,复制就相当于将一个数组变量的引用传递给另一个数组;如果一个数组发生改变,那么 引用 ...
- javaSE高级篇1 — 异常与多线程基础
1.异常的体系结构 注:Throwable是一个类,不是一个接口,这个类里面是描述的一些Error和Exception的共性,如图所示: 异常 / 错误是什么意思? 定义:指的是程序运行过程中,可能 ...
- 学习java的第二十七天
一.今日收获 1.java完全学习手册第三章算法的3.2排序,比较了跟c语言排序上的不同 2.观看哔哩哔哩上的教学视频 二.今日问题 1.快速排序法的运行调试多次 2.哔哩哔哩教学视频的一些术语不太理 ...