重载(Overload)
重载是比较容易弄明白的。
定义:重载是指不同的函数使用相同的函数名,但是函数的参数个数或类型不同(参数列表不同)。调用的时候根据函数的参数来区别不同的函数,函数重载跟返回值无关。
规则
- 函数名相同
- 必须具有不同的参数列表
- 可以有不同的访问修饰符
重载用来实现静态多态(函数名相同,功能不一样)。
重载是多个函数或者同一个类中方法之间的关系,是水平关系。
隐藏(Hiding)
定义
函数隐藏指不同作用域中定义的同名函数构成函数隐藏(不要求函数返回值和函数参数类型相同)。比如派生类成员函数屏蔽与其同名的基类成员函数、类成员函数屏蔽全局外部函数。
class Entity {
public:
Entity(){};
void Func(int a)
{
std::cout << "Entity" << std::endl;
}
};
class Person : public Entity {
public:
Person() {};
void Func(int a)
{
std::cout << "Person" << std::endl;
}
};
int main()
{
Entity* e = new Person();
e->Func(0); // 输出 Entity
std::cin.get();
}
Entity 类和 Person 类里面定义了两个一模一样的函数 Func,这两个类之间虽然是继承关系,但是这两个函数之间没有任何关系,它们只是属于不同类里面的方法,即Person类中的Func()实现了Entity类中Func()函数隐藏。
#include <iostream>
class Entity {
public:
Entity(){};
void Func(int a)
{
std::cout << "Entity" << std::endl;
}
};
class Person : public Entity {
public:
Person() {};
void Func(std::string a)
{
std::cout << "Person" << std::endl;
}
};
int main()
{
Entity* e = new Person();
e->Func(0); // 输出 Entity
e->Func("0"); // 编译报错:不能调用派生类成员函数
std::cin.get();
}
从函数签名的概念可知,基类 Entity 和派生类 Person 中分别定义了两个不同的函数,也就是说,这两个函数也毫无瓜葛,基类指针当然也就不能访问派生类中的函数了。
对比函数隐藏与函数重载的定义可知:
(1)派生类成员函数与基类成员函数同名但参数不同。此时基类成员函数将被隐藏(注意别与重载混淆,重载发生在同一个类中);
(2)函数重载发生在同一作用域,函数隐藏发生在不同作用域。
重写/覆盖(Override)
定义 :派生类中与基类同返回值类型、同名和同参数的虚函数重定义,构成虚函数覆盖,也叫虚函数重写。
规则:
- 方法声明必须完全与父类中被重写的方法相同
- 访问修饰符的权限要大于或者等于父类中被重写的方法的访问修饰符
- 子类重写的方法可以加virtual,也可以不加
重写用来实现动态多态(根据调用方法的对象的类型来执行不同的函数)。
重写是父类和子类之间的关系,是垂直关系。
#include <iostream>
class Entity {
public:
Entity(){};
virtual void Func(int a)
{
std::cout << "Entity" << std::endl;
}
};
class Person : public Entity {
public:
Person() {};
void Func(int a)
{
std::cout << "Person" << std::endl;
}
};
int main()
{
Entity* e = new Person();
e->Func(0); // 输出 Person
std::cin.get();
}
Entity 类和 Person 类里面定义了两个一模一样的函数 Func,唯一不同的是,基类中的函数被关键字 virtual
修饰了,很明显,这是一个虚函数。当定义一个指向派生类的基类对象的指针时,调用该函数时,实际指向的是派生类中的函数。即Func()在Person类中被重写了。
总结
函数覆盖存在于父类和子类的关系中,实际上对应的就是虚函数的概念。
在这里,牢记以下几点,就可区分函数重载、函数隐藏、函数覆盖和函数重写的区别:
(1)函数重载发生在相同作用域;
(2)函数隐藏发生在不同作用域;
(3)函数覆盖就是函数重写。准确地叫作虚函数覆盖和虚函数重写,也是函数隐藏的特例。
另外
- C++中重载的实现 采用命名倾轧(name mangling)技术,编译时会将同名的函数或方法根据某种规则生成不同的函数或方法名(因为函数或方法的特征标不一样)。
- C++中重写的实现 C++中重写可以用来实现动态多态,父类中需要重写的方法要加上 virtual 关键字。 虚函数实现的原理是采用虚函数表,多态中每个对象内存中都有一个指针,被称为虚函数指针,这个指针指向虚函数表,表中记录的是该类的所有虚函数的入口地址,所以对象能够根据它自身的类型调用不同的函数。