初识C++之函数重载、重写、重定义的区别

在C++的学习中,慢慢接触了一些很容易混淆的名词,今天就来剖析几个容易混淆的名词。
1、函数重载
  重载函数是函数的一种特殊情况,为方便使用,C++允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同,也就是说用同一个运算符完成不同的运算功能。这就是重载函数。重载函数常用来实现功能类似而所处理的数据类型不同的问题。

想要构成重载函数必须要满足以下几个条件:
①作用域相同;
②函数名相同;
③参数列表不同(参数个数、参数类型不同或者参数顺序不同);

下面给出具体的实例:
①参数个数不同

class A
{
public:void Fun() {cout << "Fun()" << endl;}void Fun(int i){cout << "Fun(int i)" << endl;}
private:int data;
};

②参数类型不同

class A
{
public:void Fun(int i) {cout << "Fun(int i)" << endl;}void Fun(double d){cout << "Fun(double d)" << endl;}
private:int data;
};

③参数顺序不同

class A
{
public:void Fun(int i, double d) {cout << "Fun(int i, double d)" << endl;}void Fun(double d, int i){cout << "Fun(double d, int i)" << endl;}
private:int data;
};

注意:仅仅返回值不同是无法构成重载的

class A
{
public:void Fun(int i) {cout << "no return" << endl;}int Fun(int i){cout << "return i;" << endl;return i;}
private:int data;
};

上面的代码编译会报错:
这里写图片描述
上面的例子都放在类中是因为想要表明它们是在同一作用域,当然这个不是必须的,只要满足在同一作用域即可。
关于函数重载的调用机制可以参考:
http://blog.csdn.net/ljx_5489464/article/details/50962363
PS:运算符重载也属于函数重载。

2、函数重写
  函数重写其实就是函数覆盖,当你在派生类中声明了一个与基类函数的函数名、返回值、参数列表完全相同,函数体相同或不同的成员函数时,你就已经将基类函数重写了,当你在用基类对象或基类对象的指针调用该函数时,就是调用的派生类的函数了。

想要构成重写函数必须要满足以下几个条件:
①作用域不同(分别处于基类和派生类,也就是它是在继承关系中才会存在的);
②函数名相同、参数列表、返回值必须相同(协变除外,这个后面会讲到);
③基类必须有virtual关键字,也就是基类要被重写的函数必须是虚函数;
④访问修饰符可以不同(也就是是否构成重写与访问修饰符无关);

下面给出实例:
①简单的重写关系

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;class Base
{
private:virtual void Fun(int i){cout << "Base::Fun()" << endl;}
private:int data;
};class Derived : public Base
{
public:void Fun(int i){cout << "Derived::Fun()" << endl;}
private:int data;
};int main()
{Derived d;d.Fun(1);return 0;
}

这里写图片描述
可以看到在派生类中,对基类的Fun函数进行了重写,所以这儿会调用派生类的Fun函数。

②特殊的重写关系–>协变
  在基类中有个虚函数返回基类的指针,在派生类中有个和基类同名虚函数返回派生类的指针,这种情况称为协变

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;class Base
{
public:virtual Base* Fun(){cout << "Base:" << this << endl ;return this;}
private:int data;
};class Derived: public Base
{
public:virtual Derived* Fun(){cout << "Derived:" << this << endl;return this;}
private:int data;
};int main()
{Base b;Derived d;Base *pb = b.Fun();Derived *pd = d.Fun();cout << "pb = " << pb << endl;cout << "pd = " << pd << endl;return 0;
}

这里写图片描述

3、函数重定义
  这里的重定义跟我们平时遇到的错误列表中的重定义不一样,平时我们的我们遇到的错误列表里的重定义是指在同一作用域里定义了函数名相同、返回值相同、参数列表相同的函数(这样肯定会报错啊,因为我们在调用函数时,编译器不知道该为我们调用哪一个行函数,会产生二义性),我们今天要讲的函数重定义,是一种合法的重定义,它是在不同的作用域里的函数因为某种机制产生的原因。
  
想要构成重写函数必须要满足以下几个条件:
①作用域不同(分别处于基类和派生类,也就是它是在继承关系中才会存在的);
②函数名相同
③没有构成重写
其实简单讲,只要满足前两条,并且没有构成重写,就构成了函数重定义(感觉有点说废话的感觉)。

还是上实例:
①只是函数体不同

class Base
{
private:void Fun(int i){cout << "Base::Fun()" << endl;}
private:int data;
};class Derived : public Base
{
public:void Fun(int i){cout << "Derived::Fun()" << endl;}
private:int data;
};

②函数参数列表不同

⑴
class Base
{
private:void Fun(){cout << "Base::Fun()" << endl;}
private:int data;
};class Derived : public Base
{
public:void Fun(int i){cout << "Derived::Fun()" << endl;}
private:int data;
};⑵
class Base
{
private:virtual void Fun()    //即使有virtual关键字,也不能构成重写,因为另个函数的参数列表不同,所以同样是重定义{cout << "Base::Fun()" << endl;}
private:int data;
};class Derived : public Base
{
public:void Fun(int i){cout << "Derived::Fun()" << endl;}
private:int data;
};

这里都只是列举了一些简单的常见的例子,可以举一反三。

PS:重载与覆盖
  这里的重载与覆盖是说派生类的函数与继承于基类的同名函数的关系。本来区分重载与覆盖并不算困难,但是C++的隐藏规则使问题复杂性陡然增加。这里“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
   (1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual 关键字,基类的函数将被隐藏(注意别与重载混淆)。
   (2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/526169.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

初识C++之封装

学习C不得不说到C的三大特性&#xff1a;封装、继承、多态&#xff0c;今天就先来剖析一下他的封装性。 1、什么是封装   封装就是将抽象得到的数据和行为&#xff08;或功能&#xff09;相结合&#xff0c;形成一个有机的整体&#xff0c;也就是将数据与操作数据的源代码进…

初识C++之虚函数

1、什么是虚函数   在基类中用virtual关键字修饰&#xff0c;并在一个或多个派生类中被重新定义的成员函数&#xff0c;用法格式为&#xff1a;   virtual 函数返回类型 函数名&#xff08;参数表&#xff09;    {     函数体    }    虚函数是实现多态性…

初识C++之多态

多态性是将接口与实现进行分离&#xff1b;用形象的语言来解释就是实现以共同的方法&#xff0c;但因个体差异&#xff0c;而采用不同的策略。 1、什么是多态   多态&#xff08;Polymorphism&#xff09;按字面的意思就是“多种状态”。在面向对象语言中&#xff0c;接口的…

从尾到头打印单链表(C语言)

void PrintListTailToHead(PSListNode pHead) {if (NULL ! pHead){//递归实现PrintListTailToHead(pHead->pNextNode);printf("%d ", pHead->data);} } 递归方式很容易就能实现。

删除一个无头单链表的非尾节点(C语言)

void DelNotTailNode(PSListNode pos) {PSListNode pNode NULL;assert(pos);if (NULL pos->pNextNode){return;}else{DataType temp 0;//交换pos和pos->pNextNode的数据&#xff08;相当于交换了两个结点的位置&#xff09;&#xff0c;使问题转换为删除pos指向的结点…

在无头单链表的一个非头节点前插入一个节点(C语言)

①时间复杂度为N&#xff1a; void InsertNotHead(PSListNode pHead, PSListNode pos, DataType data) {PSListNode pPreNode pHead;PSListNode pPreNode pHead;if (NULL pos){return;}else{//先找到pos结点前面的结点while(pCurNode ! pos){pPreNode pCurNode;pCurNode …

单链表实现约瑟夫环(JosephCircle)(C语言)

//使链表形成一个环 void FormCyc(PSListNode *pHead) {if (NULL pHead){return;}else{PSListNode pNode *pHead;while (NULL ! (pNode->pNextNode)){pNode pNode->pNextNode;}pNode->pNextNode *pHead;} }PSListNode JosephCircle(PSListNode pHead, int M) {if …

逆置/反转单链表(C语言)

1、逆置链表&#xff1a;前插法 void ReverseList(PSListNode* pHead) {if (NULL *pHead){return;}else{//创建一个新的空链表&#xff0c;遍历pHead指向的链表里的所有节点&#xff0c;每找到一个&#xff0c;就前插到新链表里PSListNode pNewHead *pHead;*pHead (*pHead)…

单链表排序(冒泡排序)(C语言)

优化版&#xff1a; void SortList(PSListNode pHead) {if (NULL pHead){return;}else{int flag 0;PSListNode pTailNode NULL;//当设置的尾节点与头结点指向同一个节点时&#xff0c;说明只有一个元素为排序&#xff0c;那么冒泡完成while (pTailNode ! pHead){PSListNode…

合并两个有序链表,合并后依然有序(C语言)

PSListNode MergeList(PSListNode pL1, PSListNode pL2) {PSListNode pNewNode NULL;PSListNode pListNode1 pL1;PSListNode pListNode2 pL2;PSListNode pNode NULL;if (NULL pListNode1){return pListNode2;}else if (NULL pListNode2){return pListNode1;}else{//先把新…

查找单链表的中间节点,要求只能遍历一次链表(C语言)

PSListNode FindMidNode(PSListNode pHead) {if (NULL pHead){return NULL;}else{//快慢指针&#xff0c;快指针一次循环走两步&#xff0c;慢指针一次循环走一步PSListNode pSlow pHead;PSListNode pFast pHead;//注意结束条件得加上NULL ! pFast->pNextNode&#xff0c…

查找单链表的倒数第k个节点,要求只能遍历一次链表(C语言)

PSListNode FindLastKNode(PSListNode pHead, int K) {if ((NULL pHead) || (K < 0)){return NULL;}else{PSListNode pFast pHead;PSListNode pSlow pHead;//利用快慢指针&#xff0c;让快指针先走K-1步&#xff0c;然后两指针同时走&#xff0c;直到快指针指向的下一个结…

判断单链表是否带环?若带环,求环的长度?求环的入口点?(C语言)

PSListNode HasCycle(PSListNode pHead) {if ((NULL pHead) || (NULL pHead->pNextNode)){return NULL;}else{PSListNode pFast pHead->pNextNode->pNextNode;PSListNode pSlow pHead->pNextNode;//利用快慢指针&#xff0c;让快指针每次走两步&#xff0c;慢指…

判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】(C语言)

int IsListCroseWithCycle(PSListNode pL1, PSListNode pL2) {PSListNode pMeetNode1 HasCycle(pL1);PSListNode pMeetNode2 HasCycle(pL2);if ((NULL pL1) || (NULL pL2)){return 0;}//两个链表都没环的情况else if ((NULL pMeetNode1) && (NULLpMeetNode2)){PSL…

求链表相交时的交点(C语言)

//链表相交时的交点 PSListNode IntersectionNode(PSListNode pL1, PSListNode pL2) {int count1 0;int count2 0;PSListNode PSList1 pL1;PSListNode PSList2 pL2;PSListNode pMeetNode1 HasCycle(pL1);PSListNode pMeetNode2 HasCycle(pL2);if ((NULL pL1) || (NULL …

求两个已排序单链表中相同的数据(C语言)

PSListNode ByeNode(DataType data) {PSListNode pNewNode (PSListNode)malloc(sizeof(struct SListNode));if (NULL ! pNewNode){pNewNode->data data;//注意使开辟的新节点的指向为空pNewNode->pNextNode NULL;}return pNewNode; }PSListNode UnionSet(PSListNode p…

浅析Linux开发工具之gcc/g++

在windows开发平台&#xff0c;我们用惯了vc、vs等IDE&#xff08;集成开发环境&#xff09;&#xff0c;在编译好源代码之后&#xff0c;按下相应按钮&#xff0c;IDE就会为我们完成编译&#xff0c;链接的过程。然而在Linux平台下&#xff0c;却没有这么方便的开发环境&#…

Linux权限的简单剖析

一、权限是什么 权限&#xff08;privilege&#xff09;是指某个特定的用户具有特定的系统资源使用权力。举个简单的例子&#xff0c;夏日炎炎&#xff0c;你看到路边有卖西瓜的&#xff0c;你想要吃西瓜&#xff0c;你就得买它&#xff0c;买它其实就是获取你对西瓜的使用权限…

Linux文件的三种时间属性

一、Linux文件时间属性的分类 我们在用windows系统时&#xff0c;在查看磁盘文件时&#xff0c;经常会看到文件或目录的后面有一个时间信息&#xff0c;这个是文件在磁盘上别创建的时间。其实&#xff0c;在windows系统中&#xff0c;文件还有文件的修改时间、访问时间两个时间…

浅析Linux开发工具之Makefile

一、什么是Makefile 在windows平台下&#xff0c;有很多的IDE供我们使用&#xff0c;我们不会去考虑怎么把一个很大的工程编译链接为一个可执行程序&#xff0c;因为这些事IDE都为我们做了&#xff0c;而在Linux平台下&#xff0c;我们并没有这么高端的IDE供我们使用&#xff…