C++函数重载、函数重写、函数重定义之辨析

C++ 函数重载、函数重写、函数重定义之辨析



广告^ _ ^ 如果你是C++初学者,给你推荐一套C++视频,搬运自IT黑马,视频质量很高:https://www.bilibili.com/video/av54151140/


1.几个问题

  • 什么是函数重载?
  • 什么是函数重定义?
  • 什么是函数重写?

2. 函数重载

函数重载是指,在同一作用域内几个·同名函数的参数列表的``参数个数参数顺序参数类型不同。

2.1 函数重载的原理

 //[A]
 void test(int age,double salary,char gender)
 {}
 //[B]
 void test(int age,double salary)
 {}

在C++的底层实现中,重载函数,并非同名,而是编译器帮我们做了工作。例如:函数[A]的底层实现中,函数名为:test_idc,而函数[B]的底层实现函数名为:test_id。基本规则是在函数名后添加参数列表各项的数据类型的首字母,组成新的函数名。这个实现原理又被称之为name mangling

当调用test(20,5300.23,'m');时,其实底部调用的是:test_idc(20,5300.23,'m');
当调用test(20,5300.24);时,其实底部调用的是:test_id(20,5300.24);

2.2 函数重载需要注意的事项

仅返回值不同不构成函数重载!

  通过,2.1分析的函数重载的底层原理可以知道,返回值,并没有用来构成不同的函数名字,因此返回值不同,不构成函数的重载!


3. 函数重定义

3.1 函数重定义的概念

函数的重定义,发生在类的继承关系中父子类
在子类中对父类的函数进行重新定义。

3.2 函数重定义背景引入

通过例子,我们先了解两个问题:

  1. 为什么要进行函数的重定义?
  2. 如果不重定义会发生什么现象?
#include<iostream>
#include<string>
using namespace std;
class Parent
{
    public:
        string name;
    public:
        Parent(string name)
        {
            cout<<"Parent(string)..."<<endl;
            this->name = name;
        }
        Parent()
        {
            cout<<"Parent()..."<<endl;
            this->name = "小头爸爸";
        }
		void printName()
		{
			cout << "name: " << this->name << endl;
		}
        
};
class Child: public Parent
{
    private:
        string name;
    public:
        Child(string name)
        {
            cout<<"Child(string)..."<<endl;
            this->name = name;
        }	
};

int main(int argc,char* argv[])
{
    Child* c = new Child("大头儿子");
    c->printName();
	
	system("pause");
    return 0;
}

/*
 * 程序运行结果:
 * Parent()...
 * Child(string)...
 * name: 小头爸爸
 */

程序运行结果分析:

  • 程序在进行子类对象定义时,如果子类构造函数没有在初始化列表调用父类构造函数会自动调用父类默认构造函数,此时要求:父类必须存在无参构造函数!
  • 接下来,调用子类构造函数
  • 子类调用了printName()函数

  通过结果我们发现,虽然是子类调用的printName()函数,打印的确是父类的中私有的数据成员name。好奇的同学可能会问这是为什么呢?
简单解释一下这个问题:

首先大家需要了解一个机制:子类是位于父类作用域之内的!!!

  子类对象调用printName()函数,当前内作用域没有该函数,故上外作用域(父类)查找该函数,发现父类作用域有该函数,因此调用。而此时,该函数在外作用域(父类)且当前作用域(父类)有name属性,因此将此name输出,而不是子类的name。

  问题来了:我想输子类的name属性该怎么办?

3.3 函数重定义

  我们在子类中,对父类的同名函数进行重定义,即可以访问子类的属性。从作用域的角度讲,我们在内作用域(子类)中定义一个同名函数,即可以覆盖父类作用域的该函数。

#include<iostream>
#include<string>
using namespace std;
class Parent
{
    public:
        string name;
    public:
        Parent(string name)
        {
            cout<<"Parent(string)..."<<endl;
            this->name = name;
        }
        Parent()
        {
            cout<<"Parent()..."<<endl;
            this->name = "小头爸爸";
        }
		void printName()
		{
			cout << "name: " << this->name << endl;
		}
        
};

class Child: public Parent
{
    private:
        string name;
    public:
        Child(string name)
        {
            cout<<"Child(string)..."<<endl;
            this->name = name;
        }
		void printName()
		{
			cout << "name: " << this->name << endl;
		}
};

int main(int argc,char* argv[])
{
    Child* c = new Child("大头儿子");
    c->printName();
	
	system("pause");
    return 0;
}

/*
 * 程序运行结果:
 * Parent()...
 * Child(string)...
 * name: 大头儿子
 */

  进行了函数重定义,访问的便是子类的数据成员了。此时,父类,子类都有printName()函数,因为作用域不同,所以可以同时存在。
  看到,这里我们已经可以理解,上面提问的两个问题:

  • 为什么要进行函数的重定义?
  • 如果不重定义会发生什么现象?

  好了,到这里,关于函数重定义的知识,已经讲解完毕。
  稍等,还有一个知识知识点介绍^ _ ^

3.4 关于函数重定义的一点补充

  继续思考一个问题:当子类中,出现了与父类同名,不同参的函数时,会不会把父类的同名函数隐藏吗?

//父类printName()函数
void printName()
{
	cout << "name: " << this->name << endl;
}
//子类printName(int)
void printName(int age)
{
	cout << "name: " << this->name << endl;
}
int main(int argc,char* argv[])
{
    Child* c = new Child("大头儿子");
    //会调用子类还是父类的printName?
    c->printName();
	
	system("pause");
    return 0;
}

  **咳咳,这里会报错!!!**和你想的一样吗?为什么会报错呢?原因在于:当子类中只要出现了父类的同名函数,无论参数是否相同,父类的该同名函数都会被覆盖。

//正确的调用
c->printName(1);

4. 函数重写

4.1 函数重写的概念

函数重写,发生在父子类之间,函数重写又可称为虚函数重写,函数重写,是为了实现多态现象。

4.2 函数重写问题引入

  在C++中,父类指针可以指向,子类的对象。我们的有的时候会需要,当父类指针指向子类对象时,调用子类同名函数,当父类指针指向父类对象时调用父类函数时调用父类同名函数,此即:江湖上大名鼎鼎的:多态
  在上一节,我们知道,子类函数对父类函数进行重定义后,将会覆盖父类函数,如何实现我们的问题需求呢?

4.3 如何实现函数重写

需要重写的函数,需要在**父类函数前添加virtual关键字**将该函数声明为虚函数,这样在子类中即可进行重写。子类重写时virtual关键字可有可无

4.4 函数重写的列子

#include<iostream>
#include<string>
using namespace std;
class Parent
{
    public:
        string name;
    public:
        Parent(string name)
        {
            cout<<"Parent(string)..."<<endl;
            this->name = name;
        }
        Parent()
        {
            cout<<"Parent()..."<<endl;
            this->name = "小头爸爸";
        }
		virtual void printName()
		{
			cout << "name: " << this->name << endl;
		}
        
};

class Child: public Parent
{
    private:
        string name;
    public:
        Child(string name)
        {
            cout<<"Child(string)..."<<endl;
            this->name = name;
        }
		void printName()
		{
			cout << "name: " << this->name << endl;
		}
};

int main(int argc,char* argv[])
{
	Parent* p = new Parent("小头爸爸");
	p->printName();

	Child* c = new Child("大头儿子");
    c->printName();
	
	Parent* p2 = new Child("大头儿子");
	p2->printName();
	
	system("pause");
    return 0;
}

/*
 * 程序运行结果:
 * Parent(string)...
 * name: 小头爸爸
 * Parent()...
 * Child(string)...
 * name: 大头儿子
 * Parent()...
 * Child(string)...
 * name: 大头儿子
 */

5.总结

  本文,总结了函数重载函数重定义函数重写的相关知识。需要注意,函数重载一定发生在同一作用域中、函数重定义和函数重写,很类似,区别在于:函数重写需要在父类函数名前加关键字virtual
  由于本人水平有限,可能有一些点讲的不准确,希望路过的高手前辈,批评指正。如果您觉得文章还不错,点个赞鼓励一下> _ <

发布了30 篇原创文章 · 获赞 58 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/annjeff/article/details/91553467