C++之函数重载和函数重写

函数重载定义

  • 函数重载:重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它们的参数列表和定义(实现)不相同。

    说明:当调用一个重载函数时,编译器通过把您所使用的参数类型与定义中的参数类型进行比较,决定选用最合适的定义。选择最合适的重载函数过程,称为重载决策

  • 函数重载好处:函数重载通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。

  • 函数重载特征是
    (1)相同的范围(在同一个作用域中);
    (2)函数名字相同;
    (3)参数不同;
    (4)返回值可以不同;

程序解释:

在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。不能仅通过返回类型的不同来重载函数。
例1:

class A
{
public:
	A() 
	{
		std::cout << "构造函数" << std::endl;
	}
	~A()
	{
		std::cout << "析构函数" << std::endl;
	}

	int sum(int a, int b)
	{
		int c;
		c = a + b;
		return c;
	}
	int sum(int a)
	{
		int c;
		c = a + a;
		return c;
	}

};
int main()
{
	A p;
	int sum1 = p.sum(1);   //程序根据参数个数调用一个参数的函数
	int sum2 = p.sum(1,88); //程序根据参数个数调用两个参数的函数
	std::cout << "sum1:" << sum1 << std::endl;
	std::cout << "sum2:" << sum2 << std::endl;
	return 0;
}

运行结果:

构造函数
sum1:2
sum2:89
析构函数

函数重写定义

  • 函数重写(也称为覆盖 override)函数重写是指子类重新定义基类的虚函数。
  • 特征是:
    (1)不在同一个作用域(分别位于派生类与基类);
    (2)函数名字相同;
    (3)参数相同;
    (4)基类函数必须有 virtual 关键字,不能有 static 。
    (5)返回值相同,否则报错;
    (6)重写函数的访问修饰符可以不同;

函数重写是子类和父类之间的关系,是垂直关系;而重载是同一个类中不同方法之间的关系,是水平关系;

程序解释:

例1:

#include 

using namespace std;

class Farther
{
public:
	Farther();
	~Farther();

	virtual void print();

private:

};

Farther::Farther()
{
	cout << "I am farther 构造!" << endl;
}

Farther::~Farther()
{
	cout << "I am farther 析构!" << endl;
}

void Farther::print()
{
	cout << "I am farther!" << endl;
}

class Son : public Farther
{
public:
	Son();
	~Son();

	void print();  // 重写父类中的虚函数

private:

};

Son::Son()
{
	cout << "I am son 构造!" << endl;
}
Son::~Son()
{
	cout << "I am son 析构!" << endl;
}
void Son::print()
{
	cout << "I am son !" << endl;
}

int main()
{
	Farther p1;    //定义父类的对象
	p1.print();    //调用父类的虚函数
	//Son p2;     //定义子类的对象
	//p2.print();   //调用子类的虚函数
	return 0;
}

运行结果:

I am farther 构造!
I am farther!
I am farther 析构!

例子2:
例子2中main函数改为以下样例,其余和例子2中一样

int main()
{
	//Farther p1;    //定义父类的对象
	//p1.print();    //调用父类的虚函数
	Son p2;     //定义子类的对象
	p2.print();   //调用子类的虚函数
	return 0;
}

运行结果:

I am farther 构造!
I am son 构造!
I am son !
I am son 析构!
I am farther 析构!

多态:

说到这了不得不提一下C++中的多态。在 C++ 中,多态的实现是在基类的函数前加上 virtual 关键字使其成为虚函数,并在派生类中重写该函数;该函数运行时会根据引用或指针绑定的对象的真实类型来决定要执行的版本。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。

也就是说,我们所说的多态其实就是通过对派生类中虚函数的重写来实现的(我自己的理解,如有错误欢迎指正)。

虚函数

说明: 对于某些函数,基类希望它的派生类各自定义适合自身的版本,此时基类就将这些函数声明成虚函数

定义:基类通过在这些成员函数的声明语句之前加上关键字 virtual 使得该函数执行动态绑定,这些函数就是虚函数。关键字 virtual 只能出现在类内部的声明语句之前而不能用于类外部的函数定义。

注意:通常情况下,如果我们不使用某个函数,则无须为该函数提供定义。但是我们必须为每一个虚函数都提供定义,而不管它是否被用到了,这是因为连编译器也无法确定到底会使用哪个虚函数。(即无论虚函数用不用都必须定义该函数)

纯虚函数

定义:我们在虚函数后面加上 =0 就可以将一个虚函数说明为纯虚函数

说明:和普通的虚函数不一样,一个纯虚函数无需定义。含有(或者未经覆盖直接继承)纯虚函数的类是抽象基类。抽象基类负责定义接口,而后续的其他类可以覆盖该接口。我们不能(直接)创建一个抽象基类的对象。

虚函数列表

当一个类中,有纯虚函数或虚函数时,派生类对他们进行重写之后都可以完成自己的功能,当父类的指针指向子类时就可以调用子类的同名函数,这就是c++多态的体现,而真正的原理就是虚函数列表来实现的。

更详细的解释请参考博客:C++ 之多态性、虚函数和纯虚函数

重载与覆盖的区别:

  • 覆盖是子类和父类之间的关系,是垂直关系;重载是同一个类中不同方法之间的关系,是水平关系;

  • 覆盖要求参数列表相同,重载要求参数列表不同;覆盖要求返回类型相同,重载则不要求;

  • 覆盖关系中,调用方法体是根据对象的类型(基类类型还是派生类类型)来决定的,重载关系是根据调用时的实参表与形参表来选择方法体的。

你可能感兴趣的