C++ 函数重载、函数重写、函数重定义之辨析
文章目录
广告^ _ ^ 如果你是C++初学者,给你推荐一套C++视频,搬运自IT黑马,视频质量很高:https://www.bilibili.com/video/av54151140/
1.几个问题
- 什么是函数重载?
- 什么是函数重定义?
- 什么是函数重写?
2. 函数重载
函数重载
是指,在同一作用域内
几个·同名函数的参数列表的``参数个数
、参数顺序
、参数类型
不同。
2.1 函数重载的原理
//[A]
void test(int age,double salary,char gender)
{}
//[B]
void test(int age,double salary)
{}
在C++的底层实现中,重载函数,并非同名,而是编译器帮我们做了工作。例如:函数[A]的底层实现中,函数名为:
test_idc
,而函数[B]的底层实现函数名为:test_id
。基本规则是在函数名后添加参数列表各项的数据类型的首字母,组成新的函数名
。这个实现原理又被称之为name mangling
。
当调用test(20,5300.23,'m');
时,其实底部调用的是:test_idc(20,5300.23,'m');
当调用test(20,5300.24);
时,其实底部调用的是:test_id(20,5300.24);
2.2 函数重载需要注意的事项
仅返回值不同
不构成函数重载!
通过,2.1分析的函数重载的底层原理可以知道,返回值,并没有用来构成不同的函数名字,因此返回值不同,不构成函数的重载!
3. 函数重定义
3.1 函数重定义的概念
函数的重定义,发生在
类的继承关系中
即父子类
。
在子类中对父类的函数进行重新定义。
3.2 函数重定义背景引入
通过例子,我们先了解两个问题:
- 为什么要进行函数的重定义?
- 如果不重定义会发生什么现象?
#include<iostream>
#include<string>
using namespace std;
class Parent
{
public:
string name;
public:
Parent(string name)
{
cout<<"Parent(string)..."<<endl;
this->name = name;
}
Parent()
{
cout<<"Parent()..."<<endl;
this->name = "小头爸爸";
}
void printName()
{
cout << "name: " << this->name << endl;
}
};
class Child: public Parent
{
private:
string name;
public:
Child(string name)
{
cout<<"Child(string)..."<<endl;
this->name = name;
}
};
int main(int argc,char* argv[])
{
Child* c = new Child("大头儿子");
c->printName();
system("pause");
return 0;
}
/*
* 程序运行结果:
* Parent()...
* Child(string)...
* name: 小头爸爸
*/
程序运行结果分析:
- 程序在进行子类对象定义时,如果
子类构造函数没有在初始化列表调用父类构造函数
会自动调用父类默认构造函数,此时要求:父类必须存在无参构造函数!
。 - 接下来,调用子类构造函数
- 子类调用了printName()函数
通过结果我们发现,虽然是子类调用的printName()函数,打印的确是父类的中私有的数据成员name。好奇的同学可能会问这是为什么呢?
简单解释一下这个问题:
首先大家需要了解一个机制:
子类是位于父类作用域之内的!!!
子类对象调用printName()函数,当前内作用域没有该函数,故上外作用域(父类
)查找该函数,发现父类作用域有该函数,因此调用。而此时,该函数在外作用域(父类
)且当前作用域(父类
)有name属性,因此将此name输出,而不是子类的name。
问题来了:我想输子类的name属性该怎么办?
3.3 函数重定义
我们在子类中,对父类的同名函数进行重定义,即可以访问子类的属性。从作用域的角度讲,我们在内作用域(子类
)中定义一个同名函数,即可以覆盖父类作用域的该函数。
#include<iostream>
#include<string>
using namespace std;
class Parent
{
public:
string name;
public:
Parent(string name)
{
cout<<"Parent(string)..."<<endl;
this->name = name;
}
Parent()
{
cout<<"Parent()..."<<endl;
this->name = "小头爸爸";
}
void printName()
{
cout << "name: " << this->name << endl;
}
};
class Child: public Parent
{
private:
string name;
public:
Child(string name)
{
cout<<"Child(string)..."<<endl;
this->name = name;
}
void printName()
{
cout << "name: " << this->name << endl;
}
};
int main(int argc,char* argv[])
{
Child* c = new Child("大头儿子");
c->printName();
system("pause");
return 0;
}
/*
* 程序运行结果:
* Parent()...
* Child(string)...
* name: 大头儿子
*/
进行了函数重定义,访问的便是子类的数据成员了。此时,父类,子类都有printName()函数,因为作用域不同,所以可以同时存在。
看到,这里我们已经可以理解,上面提问的两个问题:
- 为什么要进行函数的重定义?
- 如果不重定义会发生什么现象?
好了,到这里,关于函数重定义的知识,已经讲解完毕。
稍等,还有一个知识知识点介绍^ _ ^
3.4 关于函数重定义的一点补充
继续思考一个问题:当子类中,出现了与父类同名,不同参
的函数时,会不会把父类的同名函数隐藏吗?
//父类printName()函数
void printName()
{
cout << "name: " << this->name << endl;
}
//子类printName(int)
void printName(int age)
{
cout << "name: " << this->name << endl;
}
int main(int argc,char* argv[])
{
Child* c = new Child("大头儿子");
//会调用子类还是父类的printName?
c->printName();
system("pause");
return 0;
}
**咳咳,这里会报错!!!**和你想的一样吗?为什么会报错呢?原因在于:当子类中只要出现了父类的同名函数,无论参数是否相同,父类的该同名函数都会被覆盖。
//正确的调用
c->printName(1);
4. 函数重写
4.1 函数重写的概念
函数重写,发生在父子类之间,函数重写又可称为
虚函数重写
,函数重写,是为了实现多态现象。
4.2 函数重写问题引入
在C++中,父类指针可以指向,子类的对象。我们的有的时候会需要,当父类指针指向子类对象时
,调用子类同名函数,当父类指针指向父类对象时
调用父类函数时调用父类同名函数,此即:江湖上大名鼎鼎的:多态
。
在上一节,我们知道,子类函数对父类函数进行重定义后,将会覆盖父类函数,如何实现我们的问题需求呢?
4.3 如何实现函数重写
需要重写的函数,需要在**
父类函数前添加virtual关键字
**将该函数声明为虚函数,这样在子类中即可进行重写。子类重写时virtual关键字可有可无
。
4.4 函数重写的列子
#include<iostream>
#include<string>
using namespace std;
class Parent
{
public:
string name;
public:
Parent(string name)
{
cout<<"Parent(string)..."<<endl;
this->name = name;
}
Parent()
{
cout<<"Parent()..."<<endl;
this->name = "小头爸爸";
}
virtual void printName()
{
cout << "name: " << this->name << endl;
}
};
class Child: public Parent
{
private:
string name;
public:
Child(string name)
{
cout<<"Child(string)..."<<endl;
this->name = name;
}
void printName()
{
cout << "name: " << this->name << endl;
}
};
int main(int argc,char* argv[])
{
Parent* p = new Parent("小头爸爸");
p->printName();
Child* c = new Child("大头儿子");
c->printName();
Parent* p2 = new Child("大头儿子");
p2->printName();
system("pause");
return 0;
}
/*
* 程序运行结果:
* Parent(string)...
* name: 小头爸爸
* Parent()...
* Child(string)...
* name: 大头儿子
* Parent()...
* Child(string)...
* name: 大头儿子
*/
5.总结
本文,总结了函数重载
、函数重定义
、函数重写
的相关知识。需要注意,函数重载一定发生在同一作用域中
、函数重定义和函数重写,很类似,区别在于:函数重写需要在父类函数名前加关键字virtual
。
由于本人水平有限,可能有一些点讲的不准确,希望路过的高手前辈,批评指正。如果您觉得文章还不错,点个赞鼓励一下> _ <。