先来看看定义
重载 : C++允许同一作用域中有同名函数, 这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同(返回值类型无要求),在处
理实现功能类似数据类型不同的问题上保证了接口的统一性
重写 : 也叫覆盖. 在继承关系中, 派生类中有跟基类完全相同(返回值类型、函数名字、参数列表完全相同) 的虚函数,称派生类
的虚函数重写了基类的虚函数(注意: 重写有两个函数名不同的例外, 协变和析构重写)
重定义 : 一般叫隐藏, 在继承关系中, 在派生类中有跟基类同名的普通成员函数(只要函数名相同, 与返回值类型和参数列表无关),
则基类在派生类中被隐藏. 隐藏对成员变量也一样, 不管变量类型, 只要变量名相同, 基类成员变量在派生类中也会被隐藏.
其实定义已经说大概差不多了, 还是再详细来看
重载
名字修饰及重载原理
说到C++中的函数重载 , 就不得不提名字修饰
C/C++程序要运行, 需要经历以下几个阶段:预处理、编译、汇编、链接。
名字修饰 是一种在编译过程中,将函数、变量的名称重新改编的机制,简单来说就是编译器为了区分各个函数,将函数通过某种算法,重新修饰为一个全局唯一的名称。
C语言的名字修饰规则非常简单,只是在函数名字前面添加了下划线" _ "。比如,对于以下代码,在最后链接时就会出错:
.c文件
#include<stdio.h> int Add(int a, int b); int main() { Add(1, 2); return 0; }
在链接时编译器会报出以下错误 :
上述Add函数只给了声明没有给定义,在main函数中引用的Add函数找不到函数体, 因此在链接时就会报错 .从报错结果中可以看到,C语言只是简单的在函数名前添加下划线。因此当工程中存在相同函数名的函数时,就会产生冲突。
那么C++中支持函数重载 , 命名空间 . 也就意味着C++中有着与C不同的更加复杂的函数名修饰方式 . 需要注意的是不同平台, 不同编译器在底层实现的方式不尽相同 , 本文内容都是VS2017中测试的
我们来看这段代码 :
.cpp文件#include<iostream> using namespace std; int Add(int a, int b); double Add(double a, double b); int main() { Add(1, 2); Add(1.1, 2.2); system("pause"); return 0; }
我们看到在链接时出错, 也是只声明 , 未定义, 在链接时找不到函数实体, 所以报错 .
重点是编译器在底层使用的不是Add名字,而是被重新修饰过的一个比较复杂的名字,被重新修饰后的名字中包含了:函数的名字以及参数类型。这就是为什么函数重载中几个同名函数要求其参数列表不同的原因。只要参数列表不同,编译器在编译时通过对函数名字进行重新修饰,将参数类型包含在最终的名字中,就可保证名字在底层的全局唯一性。实际不止有名字和参数类型还有作用域和返回值, 名字修饰的准确映射机制为 : 作用域+返回类型+函数名+参数列表
举个栗子
#include<iostream>
using namespace std;
class A {
public:
void fun() {
cout << "A::void fun()\n";
}
int fun(int a) {
cout << "A::void fun(int)\n";
return 0;
}
};
char fun(char a, int b) {
cout << "char fun(char, int)\n";
return 0;
}
char fun(int a, char b) {
cout << "char fun(int, char)\n";
return 0;
}
int main() {
A a;
a.fun();
a.fun(1);
fun('a', 10);
fun(10, 'a');
system("pause");
return 0;
}
在上面的代码中, 类中的两个fun函数构成了重载关系. 类外的两个函数也构成了重载关系
重写及原理
C++为了实现多态定义了虚函数, 虚函数可以被重写, 但没有规定怎么去实现. 大多数编译器都选择了用虚表来实现. 在重写虚函数时, 可以很方便的修改虚表中的函数指针.
重写以及重写的两个例外, 戳链接( ̄︶ ̄)↗ https://blog.youkuaiyun.com/qq_41071068/article/details/102909116
虚表详解, 戳链接( ̄︶ ̄)↗ https://blog.youkuaiyun.com/qq_41071068/article/details/102943449
隐藏
我们知道, 类本身就是一个作用域, 那么当一个类继承了另一个类时, 作用域又将如何变化呢?
1. 继承关系发生后, 基类和派生类还是有各自独立的作用域
2. 当基类和子类有重名成员时, 派生类中将屏蔽掉基类的重名成员, 这种现象叫隐藏, 也叫重定义
解释:
1). 重名, 对变量来说, 只需要变量名重名, 与变量类型无关, 对函数来说, 只需要函数名重名, 与返回值与参数列表无关
2). 屏蔽, 指的是, 当在派生类中直接访问重名成员时, 访问的都是派生类的
3. 事实上基类的重名成员只是被隐藏了, 我们还是可以通过 基类类名::重名成员 这种方式来访问
4. 在实际继承关系中, 最好不要定义重名成员
来看个例子
#include<iostream>
using namespace std;
class Base {
public:
int m_data;
Base():m_data(10){}
int func() {
cout << "Base的func()" << endl;
return 0;
}
};
class Derive : public Base {
public:
double m_data;
Derive() :m_data(20.0) {}
void func() {
cout << "Derive的func()" << endl;
}
};
int main() {
Derive d;
cout << d.m_data << endl;
d.func();
cout << d.Base::m_data << endl;
d.Base::func();
system("pause");
return 0;
}