c++中的函数重载、函数重写、函数重定义
目录
一、函数重载
二、函数重写
三、函数重定义
为了更加深刻的理解 函数重载、重写、重定义,我们可以带着如下这两个问题去思考:
1、子类中是否可以定义父类中的同名成员?为什么?
可以,因为子类与父类的命名空间不同;
2、子类中定义的函数是否可以重载父类中的同名函数?
不可以,因为函数重载必须在同一个作用域中。
https://blog.csdn.net/qq_37791134/article/details/81502017、https://blog.csdn.net/gogogo_sky/article/details/71189499、https://blog.csdn.net/fantian_/article/details/80719144
5、函数重载的结论
1. 函数重载的本质:多个不同的函数;
2. 函数名和参数列表是唯一的标识;
3. 函数重载必须发生在同一个作用域中;
4. c++编译器 和 c编译器 对函数重命名的规则不同;
5. 编译器决定符号表中函数名被编译后的最终目标名;
c++ 编译器 将函数名和参数列表编译成目标名;
c 编译器将函数名编译成目标名;
6. 函数重载是在编译期间根据参数类型和个数决定函数调用
7. 函数重载是一种静态多态;
(1)多态:用同一个东西表示不同的形态;
(2)多态分为:静态多态(编译时的多态)、动态多态(运行时的多态);
6、编译器调用函数重载的规则
1. 将所有同名函数作为候选者;
2. 尝试寻找可行的候选者函数
(1)精确匹配实参;
(2)通过默认参数能够匹配实参;
(3)通过默认类型转换匹配实参;
3. 匹配失败
(1)最终寻找的候选函数不唯一,则出现二义性,编译失败;
(2)无法匹配所有的候选函数,函数没定义,编译失败;
7、函数重载与默认参数
当函数重载遇到默认参数时,就会发生二义性;
代码如下:
1 #include函数重载的二义性案例2 using namespace std; 3 4 class A 5 { 6 void func(int a, int b, int c = 0) {} 7 void func(int a, int b) {} 8 }; 9 10 int main() 11 { 12 A a; 13 a.func(1, 2); // 二义性出现 14 15 return 0; 16 }
8、函数重载 与 函数指针
将重载函数名赋值给函数指针时,
1. 根据重载规则挑选与函数指针参数列表一致的候选者;
2. 严格匹配候选者的函数类型与函数指针的函数类型;
1 #include函数重载与函数指针2 #include <string.h> 3 4 int func(int x) 5 { 6 return x; 7 } 8 9 int func(int a, int b) 10 { 11 return a + b; 12 } 13 14 int func(const char* s) 15 { 16 return strlen(s); 17 } 18 19 typedef int(*PFUNC)(int a); 20 21 22 int main(int argc, char *argv[]) 23 { 24 int c = 0; 25 26 PFUNC p = func; 27 28 c = p(1); 29 30 printf("c = %d\n", c); // c = 1 31 32 return 0; 33 }
二、函数重写(也称为覆盖, Function override)
1、什么是函数重写
函数重写分为 虚函数重写(会发生多态) 与 非虚函数重写(重定义的一种形式);
函数重写:也叫做覆盖。子类重新定义父类中有相同返回值、名称和参数的虚函数。函数特征相同。但是具体实现不同,主要是在继承关系中出现的 。
注:一般而言,函数重写 就是 虚函数重写,为的是实现多态调用;
2、函数重写的条件
1. 函数的返回类型、方法名、参数列表完全相同;
2. 必须发生在不同的作用域中(基类与派生类中);
3. 基类中有 virtual 关键字声明,派生类中可有可无,不能有 static (虚函数重写);
3、函数重写的意义
在面向对象的继承关系中,我们了解到子类可以拥有父类中的所有属性与行为;但是,有时父类中提供的方法并不能满足现有的需求,所以,我们必须在子类中重写父类中已有的方法,来满足当前的需求。
三、函数重定义(也称为隐藏,Function redefining)
1、什么是函数重定义
子类重新定义父类中有相同名称的函数 ( 不包括虚函数重写 ) 。
2、重定义的表现形式
1. 必须发生在不同的作用域中(基类与派生类中);
2. 函数名相同;
3. 返回值可以不同;
4. 参数列表不同,此时,无论基类中的同名函数有无 virtual 关键字,基类中的同名函数都会被隐藏。
5. 参数列表相同,此时,基类中的同名函数没有 virtual 关键字,则基类中的同名函数将会被隐藏 --- 非虚函数重写 。
3、关于同名覆盖的结论(归纳:基类与派生类中存在同名成员;--- 同名覆盖)
1. 子类将隐藏父类中的同名成员;
2. 父类中的同名成员依然存在于子类中;
3. 可以通过作用域分辨符(::)访问被隐藏的父类中的同名成员;
4. 不可以直接通过子类对象访问父类成员;
注:同名覆盖规则适用于类的成员变量与成员函数;
相关代码展示:
1 #include同名成员变量案例2 #include <string> 3 4 using namespace std; 5 6 class Parent 7 { 8 public: 9 int mi; 10 11 Parent() 12 { 13 cout << "Parent() : " << "&mi = " << &mi << endl; 14 } 15 }; 16 17 class Child : public Parent 18 { 19 public: 20 int mi; 21 22 Child() 23 { 24 cout << "Child() : " << "&mi = " << &mi << endl; 25 } 26 }; 27 28 int main() 29 { 30 Child c; 31 32 c.mi = 100; 33 34 c.Parent::mi = 1000; 35 36 cout << "&c.mi = " << &c.mi << endl; 37 cout << "c.mi = " << c.mi << endl; 38 39 cout << "&c.Parent::mi = " << &c.Parent::mi << endl; 40 cout << "c.Parent::mi = " << c.Parent::mi << endl; 41 42 return 0; 43 } 44 45 /** 46 * Parent() : &mi = 0x7ffe98191450 47 * Child() : &mi = 0x7ffe98191454 48 * &c.mi = 0x7ffe98191454 49 * c.mi = 100 50 * &c.Parent::mi = 0x7ffe98191450 51 * c.Parent::mi = 1000 52 */
1 #include重定义案例2 #include <string> 3 4 using namespace std; 5 6 class Parent 7 { 8 public: 9 int mi; 10 11 void add(int v) 12 { 13 mi += v; 14 } 15 16 void add(int a, int b) 17 { 18 mi += (a + b); 19 } 20 }; 21 22 class Child : public Parent 23 { 24 public: 25 int mi; 26 27 void add(int v) 28 { 29 mi += v; 30 } 31 32 void add(int a, int b) 33 { 34 mi += (a + b); 35 } 36 37 void add(int x, int y, int z) 38 { 39 mi += (x + y + z); 40 } 41 }; 42 43 int main() 44 { 45 Child c; 46 47 c.mi = 100; 48 c.Parent::mi = 1000; 49 50 cout << "c.mi = " << c.mi << endl; 51 cout << "c.Parent::mi = " << c.Parent::mi << endl; 52 53 c.add(1); 54 c.add(2, 3); 55 c.add(4, 5, 6); 56 c.Parent::add(10); 57 c.Parent::add(11, 12); 58 59 cout << "c.mi = " << c.mi << endl; 60 cout << "c.Parent::mi = " << c.Parent::mi << endl; 61 62 return 0; 63 } 64 /** 65 * c.mi = 100 66 * c.Parent::mi = 1000 67 * c.mi = 121 68 * c.Parent::mi = 1033 69 */
1 #include重写案例2 #include <string> 3 4 using namespace std; 5 6 class Parent 7 { 8 public: 9 int mi; 10 11 virtual void add(int v) 12 { 13 mi += v; 14 } 15 }; 16 17 class Child : public Parent 18 { 19 public: 20 int mi; 21 22 virtual void add(int v) 23 { 24 mi += v; 25 } 26 27 void add(int a, int b) 28 { 29 mi += (a + b); 30 } 31 }; 32 33 int main() 34 { 35 Child c; 36 Parent &p = c; // 父类引用指向子类对象,多态发生 37 38 c.mi = 100; 39 c.Parent::mi = 1000; 40 41 cout << "c.mi = " << c.mi << endl; 42 cout << "c.Parent::mi = " << c.Parent::mi << endl; 43 44 c.add(1); 45 c.add(2, 3); 46 p.add(100); // 实际调用的是子类中 add(int v) 函数 47 c.Parent::add(10); 48 49 cout << "c.mi = " << c.mi << endl; // c.mi = 1 + 2 + 3 + 100 50 cout << "c.Parent::mi = " << c.Parent::mi << endl; // c.Parent::mi = 1000 + 10 51 52 return 0; 53 } 54 /** 55 * c.mi = 100 56 * c.Parent::mi = 1000 57 * c.mi = 206 58 * c.Parent::mi = 1010 59 */
本节总结:
1、 重载 必须在 一个类之间, 而 重写、重定义 是在 2个类 之间
2、 重载是在 编译期间 根据参数类型和个数决定函数调用; 多态(虚函数重写)是在 运行期间 根据具体对象的类型决定函数调用
3、 发生重写、重定义后,遵循 同名覆盖 规则;