C++中类成员函数的重载、覆盖、隐藏
重载
重载指的是多个同名函数,这些函数的参数列表都不同(可能是参数个数不同,或许是参数类型不同)
成员函数重载特征:
(1)同一个作用域范围(成员函数就是在同一个类中)
(2)成员函数名相同
(3)参数列表不同 (参数个数不同、参数类型不同或两个皆不同)
(4)通过参数区分函数,说明多个函数肯定不是一个地址空间
(5)返回值类型可不同(只有返回值不同是不能重载的)重载函数不能通过返回值来区别重载函数
重载的好处:
(1)不用因为参数类型或参数个数不同,而写多个函数。
(2)可以是一些函数,特定的运算符发具有多种功能(最常用的就是,运算符重载)
举例:赋值运算符重载
#include<iostream>
using namespace std;
class Person
{
public:
Person(int age)//有参构造
{
m_age = new int(age);//初始化成员变量
}
~Person()//析构函数
{
if (m_age!= NULL)
{
delete m_age;
m_age = NULL;
}
}
//赋值运算符的重载
Person& operator=(Person &p)//返回自身需要引用
{
//编译器提供是浅拷贝
//m_age = p.age;
//应该先判断是否有属性在堆区,如果有先释放干净
if (m_age != NULL)
{
delete m_age;
m_age = NULL;
}
//深拷贝
m_age = new int(*p.m_age);
//*m_age = *p.m_age;
//返回自身
return *this;
}
int* m_age;
};
void test()
{
Person P(18);
Person P1(20);
Person P2(30);
P2 = P1 = P;//赋值操作
cout << "P的年龄为:" << *P.m_age << endl;
cout << "P1的年龄为:" << *P1.m_age << endl;
cout << "P2的年龄为:" << *P2.m_age << endl;
}
int main()
{
test();
system("pause");
return 0;
}
运行结果
运行结果显示:如果我们使用普通的(=)运算符,实现的是浅拷贝,在类中有指针的情况下,是无法直接用一个类对象给另一个类对象赋值的,就需要我们进行深拷贝,当我们重载了(=)后,可进行深拷贝。
覆盖
覆盖是指派生类方法覆盖从基类继承过来的方法(覆盖也叫重写),当基类中的方法被覆盖后,可用super关键字调用
覆盖特征:
(1)不同的范围(分别位于派生类与基类)
(2)成员函数名相同,且参数相同,返回类型相同
(3)基类函数必须有virtual关键字(虚函数)
覆盖条件: 借助博客文章参考:点击打开链接
1、“三同一不低” 子类和父类的方法名称,参数列表,返回类型必须完全相同,而且子类方法的访问修饰符的权限不能比父类低。
2、子类方法不能抛出比父类方法更多的异常。即子类方法所抛出的异常必须和父类方法所抛出的异常一致,或者是其子类,或者什么也不抛出;
3、被覆盖的方法不能是final类型的。因为final修饰的方法是无法覆盖的。
4、被覆盖的方法不能为private。否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
5、被覆盖的方法不能为static。所以如果父类中的方法为静态的,而子类中的方法不是静态的,但是两个方法除了这一点外其他都满足覆盖条件,那么会发生编译错误。反之亦然。即使父类和子类中的方法都是静态的,并且满足覆盖条件,但是仍然不会发生覆盖,因为静态方法是在编译的时候把静态方法和类的引用类型进行匹配。
隐藏
隐藏:派生类中重新定义基类同名函数的方法,称为对基类函数的覆盖或改写,覆盖后基类同名函数在派生类中被隐藏。
隐藏特征:当基类中有多个重载函数并且派生类中有同名函数,则基类中的所有同名函数被隐藏,若想调用基类函数仍然要显示使用基类名
#include<iostream>
using namespace std;
class Animal //定义基类
{
public:
//定义speak函数
void speak() {
cout << "animal language" << endl;
}
};
class Cat :public Animal //定义派生类,且继承
{
public:
//定义speak函数
void speak() {
cout << "cat language miao miao" << endl;
}
};
int main()
{
Cat cat;//定义派生类对象
cat.speak();//通过派生类调用speak函数
cat.Animal::speak();//调用基类同名函数
system("pause");
return 0;
}
运行结果:
通过结果可知,第一次调用的时候并不是调用的基类中的函数,而是派生类中的函数,而调用
基类中的同名函数,需要显示的使用基类名。