在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关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。