首先来看一下派生类和基类成员同名事的处理规则:

  1. 派生类内定义了一个与基类同名的成员,该现象称为同名覆盖,此时,无论派生类内部成员函数还是派生类的对象访问同名成员,如果未加任何特殊标识,则访问派生类中重新定义的同名成员。
  2. 如果派生类内部成员或派生类的对象需要访问基类继承来的同名函数,则必须在同名函数前加上"基类名::"进行类名限定。
  3. 如果基类内部成员函数或基类对象访问同名成员,访问的一定是基类的同名成员。
  4. 用基类的指针,无论是否指向基类对象,都只能访问基类的同名成员。
  5. 用基类的引用,无论是否是基类对象的别名,都只能访问基类的同名成员。

从4、5两条规则可以看到,无论基类指针指向基类对象还是派生类对象,无论引用是基类对象还是派生类对象的别名,始终调用基类成员。那么,怎样才能使基类指针指向基类对象时调用基类同名成员,指向派生类对象时调用派生类同名成员呢?这就是动态联编所能达到的效果。

为了实现动态联编,首先要将同名函数声明为虚函数

虚函数可以通过基类指针或引用访问基类和派生类中被声明为虚函数的同名函数。存在继承关系是首要条件,而且派生类一定是以公有方式继承了基类。同名虚函数在基类和派生类中其函数原型必须完全一致,否则无法通过虚函数实现动态多态性。

第一:为什么派生类必须以公有方式继承基类呢?这是赋值兼容规则的使用前提,以下规则反向均不成立。

  1. 基类对象=公有派生类对象。赋值后的基类对象只能获得基类成员部分,而在派生类中新增加的成员不能被基类对象访问。
  2. 指向基类对象的指针=公有派生类对象的地址。利用赋值后的指针可以间接访问基类的成员,若要间接访问派生类成员,必须强制转换为派生类类型指针访问。
  3. 指向基类对象的指针=指向公有派生类对象的指针。
  4. 基类的引用=公有派生类对象,即派生类对象可以初始化基类的引用。赋值后的引用只可以访问基类成员,无法访问派生类新增成员,因为无法对引用进行强制类型转换。

一般来说,可以将类层次中具有共性的成员函数声明为虚函数,而个性的函数没有必要声明为虚函数。但以下情况是特例:

  1. 静态成员函数不能声明为虚函数。因为静态成员函数不属于某一个对象,没有多态性。
  2. 内联函数不恩能够声明为虚函数。因为内联函数的执行代码是明确的,没有多态性的特征。
  3. 构造函数不能是虚函数。因为构造函数声明为虚函数是完全没有意义的。
  4. 析构函数往往被声明为虚函数。

如果基类的析构函数声明为虚函数,则该类所有派生类的析构函数也自动成为虚函数而无需显式声明。在这种情况下,由于实施多态性是将基类的指针指向派生类的对象,若果删除该指针,则会调用该指针指向的派生类的析构函数,而派生类的析构函数又自动调用基类的析构函数,这样整个派生类的对象被完全释放。如果析构函数不是虚函数,则编译器实施静态绑定,在删除基类指针时,只调用指针所属基类的析构函数而不调用派生类的析构函数,这会导致析构不完全。所以当派生类中存在指针数据成员,又通过该指针进行了动态空间申请时,将析构函数声明为虚函数是非常有必要的。

第二:为什么同名虚函数在基类和派生类中其函数原型必须完全一致?一个函数在基类和公有派生类中拥有相同的函数名,但函数返回值类型不同,或者是形式参数表不同,即使在基类中被声明为虚函数,也不具备多态性。这种情况下,基类中的函数无虚函数特性,当做普通函数使用,而在派生类中存在同名函数就是本文开始讲到的同名覆盖现象,无法通过基类的指针或引用实现动态多态性。

下面的几条面试题可以巩固本文的知识。

1.题目是问下面的C++代码输出是什么?(阿里巴巴)

class Base
{
public:
	int Bar(char x) { return (int)x;}
	virtual int Bar(int x) { return (2*x);}
};

class Derived : public Base
{
public:
	int Bar(char x) { return (int)(-x);}
	int Bar(int x) { return (x/2);}
};

void main()
{
	Derived Obj;
	Base* pObj=&Obj;
	printf("%d,",pObj->Bar((char)(100)));
	printf("%d,",pObj->Bar(100));
}

  答案是:100,50

2. What's the output of the following code?(微软)

class A
{
public:
	virtual void f()
	{
		cout << "A::f()" << endl;

	}
	void f() const
	{
		cout << "A::f() const" << endl;
	}
};

class B : public A
{
public:
	void f()
	{
		cout << "B::f()" << endl;
	}
	void f() const
	{
		cout << "B::f() const" << endl;
	}
};
void g(const A * a)
{
	a->f();
}

int main()
{
	A * a = new B();
	a->f();
	g(a);
	delete a;
}

  答案是:B::f()
                  A::f() const

转载请注明出处:http://www.cnblogs.com/Rosanna/p/3346046.html

[C++]虚函数-同名访问的更多相关文章

  1. C++之虚函数和多态

    干货较多-需要自己深思理解: C++支持两种多态性: 1.编译时多态性(静态绑定-早绑定) 在程序编译阶段即可以确定下来的多态性 通过使用 重载机制(重载函数)实现 (模板)http://blog.c ...

  2. C++中虚函数功能的实现机制

    要理解C++中虚函数是如何工作的,需要回答四个问题. 1.  什么是虚函数. 虚函数由于必须是在类中声明的函数,因此又称为虚方法.所有以virtual修饰符开始的成员函数都成为虚方法.此时注意是vir ...

  3. C++纯虚函数

    本文较为深入的分析了C++中虚函数与纯虚函数的用法,对于学习和掌握面向对象程序设计来说是至关重要的.具体内容如下: 首先,面向对象程序设计(object-oriented programming)的核 ...

  4. C++虚函数继承的bug

    闲来无事想测试一下:如果在派生类中重写基类的虚函数,那么允不允许改变虚函数的访问权限,结果颠覆了三观..... 基类Base,拥有public方法test(),test()为虚函数 派生类Derive ...

  5. C++ 虚函数 、纯虚函数、接口的实用方法和意义

    也许之前我很少写代码,更很少写面向对象的代码,即使有写多半也很容易写回到面向过程的老路上去.在写面向过程的代码的时候,根本不管什么函数重载和覆盖,想到要什么功能就变得法子的换个函数名字,心里想想:反正 ...

  6. 为何JAVA虚函数(虚方法)会造成父类可以&quot;访问&quot;子类的假象?

      首先,来看一个简单的JAVA类,Base. 1 public class Base { 2 String str = "Base string"; 3 protected vo ...

  7. 【C++】多态性(函数重载与虚函数)

    多态性就是同一符号或名字在不同情况下具有不同解释的现象.多态性有两种表现形式: 编译时多态性:同一对象收到相同的消息却产生不同的函数调用,一般通过函数重载来实现,在编译时就实现了绑定,属于静态绑定. ...

  8. C++ - 虚基类、虚函数与纯虚函数

    虚基类       在说明其作用前先看一段代码 class A{public:    int iValue;}; class B:public A{public:    void bPrintf(){ ...

  9. C++之虚函数的作用和使用方法

    在同一类中是不能定义两个名字相同.参数个数和类型都相同的函数的,否则就是“重复定义”.但是在类的继承层次结构中,在不同的层次中可以出现名字相同.参数个数和类型都相同而功能不同的函数.例如在例12.1( ...

随机推荐

  1. List Set Map

    List Set 都是接口,都继承了Collection接口 ArrayList LinkList 直接实现了List接口 HashSet 实现了Set接口  TreeSet继承父类AbstractS ...

  2. 2-sql基本操作

    sql基本操作 一.Sqlplus常用命令 1.查看oracle数据库的进程 2.查看oracle数据库运行状态 3.显示实例名(数据库名) 4.用sys账户登陆到数据库 5.解锁账户scott,并登 ...

  3. 设置Distribution clean up 每次删除Command的数量

    Replication Job “Distribution clean up: distribution” 默认设置是,每10minutes运行一次,每次删除2000个Command.这对于有1.9亿 ...

  4. js 判断各种数据类型

    了解js的都知道, 有个typeof  用来判断各种数据类型,有两种写法:typeof   xxx   ,typeof(xxx) 如下实例: typeof   2      输出   number   ...

  5. C的数值取反操作

    今儿在代码中发现一句"return x? ~0 : 0;"对~0这个取反操作相关的知识又还给老师了.一查,查到下面一道题,弄过来贴上. //-------------------- ...

  6. MyBatis学习总结(二)&mdash;&mdash;使用MyBatis对表执行CRUD操作

    一.使用MyBatis对表执行CRUD操作--基于XML的实现 1.定义sql映射xml文件 userMapper.xml文件的内容如下: 1 <?xml version="1.0&q ...

  7. python字典概述

    字典 1.    概述 字典是一个无序的数据集合,序列类型用有序的数字键做索引将数据以数组的形式存储. 在字典中能获得的有序集合只能是键的集合或者是值得集合,方法keys()或者value()返回一个 ...

  8. Can&#39;t obtain the input stream from /docProps/app.xml

    今天在做poi修改样式时,报了以下错误: Exception in thread "main" org.apache.poi.POIXMLException: java.io.IO ...

  9. 【转】win7与VMware ubuntu虚拟机实现文件共享(最后一定要装open-vm-dkms插件)

    原文网址:http://blog.sina.com.cn/s/blog_453b9efb01019hpl.html 一般来说,由于一些特殊的需要,会在Win7系统中利用虚拟机(VMware)安装ubu ...

  10. mysql创建自定义函数与存储过程

    mysql创建自定义函数与存储过程 一 创建自定义函数 在使用mysql的过程中,mysql自带的函数可能不能完成我们的业务需求,这时就需要自定义函数,例如笔者在开发过程中遇到下面这个问题 mysql ...