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

浅析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…

嵌入式面试准备

题目都摘于网上 嵌入式系统中经常要用到无限循环&#xff0c;如何用C编写死循环 while(1){}或者for(;&#x1f609; 内存分区 代码区&#xff0c;全局区&#xff08;全局变量&#xff0c;静态变量&#xff0c;以及常量&#xff09;&#xff0c;栈区&#xff0c;堆区 const关键…

C语言extern与static修饰变量

extern和static在C语言里面的作用这里就不做过多的阐述了&#xff0c;下面直接通过一个小程序来看一看他们修饰的变量的特性。 #include <stdio.h>int count 3;int main() {int i 0, count 2, sum 0;for (i 0; i < count; i 2, count){static int count 4;cou…

函数值的交换

《函数值的交换》 交换函数的几种方式&#xff1a; (1) //error int Swap1(int a,int b) { int tmp; tmp a; a b; b tmp; return 0; } 在函数Swap1中&#xff0c;a和b的地址的值并没有交换。只是把10和20赋给了a和b&#xff0c;a和b原本的值并没有改变。 (2) #…

数组的下标越界

《数组下标越界》 数组定义的一般形式为&#xff1a;类型说明符 数组名[常量表达式] [常量表达式]这两个常量表达式分别指定了二维数组的行数和列数&#xff0c;程序编译时据此向内存申请空间。 引用二维数组的格式为&#xff1a;数组名[行下标] [列下标]&#xff0c;下标都是从…

指针的加减法计算

(1) 指针&#xff1a;&#xff08;p&#xff09;表示加一个单元格&#xff08;单元格的字节随类型而定&#xff09;#include<stdio.h>int main(){int arr[10]{1,2,3,4,5,6};int *p arr;p;printf("%d %d\n",arr[0],arr[1]);return 0;}(2) 指针加数字&#xff1…

字符串在指针和数组上赋值的区别

1 #include<stdio.h> 2 int main() 3 { 4 char *str1 "abcde";// 字符串常量 5 char str2[] "abcde";// 字符数组 6 str1[0] x;//error 7 str2[0] x; 8 return 0; 9 } 注&#xff1a;代码运行到第6行崩溃 *str1是一个指…

继承与多态(一)

目录 一、继承的概念&#xff1a; 二、公有继承 三、私有继承 四、保护继承 五、保护继承与保护成员的访问 一、继承的概念&#xff1a; 在C中可以用已有的类来定义新的类&#xff0c;新类将继承原有类的全部特性&#xff0c;原有类称为基类&#xff08;父类&#xff09;&…

对象的使用

目录 一、对象指针 二、对象引用 三、对象数组 四、动态对象 五、this指针 六、组合对象 一、对象指针 定义&#xff1a;占用一块连续的内存区域&#xff0c;由此可以使用一个指向对象的指针来访问对象。它指向存放该对象的地址。 优点&#xff1a; &#xff08;1&a…

友元

概念&#xff1a;在C中&#xff0c;类具有封装性和隐蔽性&#xff0c;只有类的函数成员才能访问类的私有成员&#xff0c;程序中的其他函数是无法访问类的私有成员&#xff0c;友元为类的封装隐藏开了一个小孔&#xff0c;外界可以访问类内部的一些属性。如果某个对象说明为某个…

常对象与常成员

一、常对象 概念&#xff1a;如果某个对象不允许修改&#xff0c;则该对象称为常对象。 PS&#xff1a; &#xff08;1&#xff09;不允许常对象调用任何类的成员函数&#xff0c;而且常对象一旦定义&#xff0c;在其生存期内不允许修改&#xff0c;否则导致编译错误。 &am…

指针在数组里的加法

在C语言中&#xff0c;指针与数组经常放在一起使用&#xff0c;很多情况下数组和指针可以互相转换。 //伪代码 int arr[10] {0,1,2,3,4,5,6,7,8,9};int * p arr;arr[i] * (pi);//i是大于0小于数组长度-1的任何正整数*(arr1) arr[i]; 数组和指针的不同&#xff1a; &…

单链表(带头节点)

带头结点单链表的内存分布情况 头文件 #pragma once //带头节点的单链表 //单链表尾节点的next为NULL //List为一条链表&#xff1b;Node* 一个节点的地址 typedef struct Node {int data;//数据Node *next;//下一个节点的地址 }Node ,*List ;//List Node *//初始化 void Ini…