C++ 重载, 重定义, 重写

先来看看定义

重载 : 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.csdn.net/qq_41071068/article/details/102909116

虚表详解, 戳链接( ̄︶ ̄)↗ https://blog.csdn.net/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;
}

发布了223 篇原创文章 · 获赞 639 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_41071068/article/details/102983143
今日推荐