1:final, override的作用
1:final的作用:修饰类或成员函数
修饰类时: 表示本类禁止被继承;
修饰成员函数:
virtual成员函数:表示不允许子类重写覆盖,但可以重写隐藏
非virtual成员函数:表示不允许子类重写隐藏;
2:override:用于标示虚函数,明确说明这是重写覆盖父类的同名函数,避免造成重写隐藏的情况;
2: 示例代码
/ final的用法: 用于标识类或成员函数,禁止子类继承或重写
// 具体是禁止重写隐藏还是重写覆盖得看成员函数是否有virtual关键字修饰
// 1, final 修饰类
// 用final修饰,意味着继承该类会导致编译错误
class Hello final
{
...
}
/*
2, final 修饰成员方法,可以是虚函数,也可以是普通成员函数,
虚函数则表示不允许子类重写覆盖,但经测试证明仍允许重写隐藏,
普通成员函数则表示不允许子类重写隐藏:
*/
class A
{
public:
virtual void f(int) final {}
};
class B : public A
{
public:
/*
这样将会导致错误,因为父类声明了该虚函数为final,
即表示不允许子类重写覆盖;
*/
// virtual void f(int){}
/*
正确,这个函数与上面那个函数是互相独立的,不被认为是重写覆盖,
而是重写隐藏,这种情况要好好注意
*/
virtual void f(){}
virtual void f(double){} // 正确,原因同上
};
// 2 override的用法
// override只能用于标识虚函数,不能是普通成员函数, 比如:
class A
{
public:
virtual void f(int){}
void g(){};
};
class B : public A
{
public:
/*
明确表示这个函数为的是重写覆盖父类的f(int);虚函数的,
如果可能发生错误导致可能的重写隐藏的话编译时就报错
*/
virtual void f(int) override {}
/*
像这种形式就会导致编译时报错,因为这样不是重写覆盖,
而是重写隐藏,重写覆盖要求函数签名是一样的;
*/
// virtual void f() override {}
/*
错误,因为override只能用于修饰虚函数,g();不是虚函数,
不可以使用override;
*/
// void g() override {}
};
另一方面,如果需要一个函数永远不能被重写覆盖(顺着继承层次往下都不能被重写覆盖),可以把该函数标识为final,在基类中和派生类中都可以这么做。如果是在派生类中,我们可以同时使用override和final标识符。
class B
{
public:
virtual void f(int)
{
std::cout << "B::f" << std::endl;
}
};
class D : public B
{
public:
virtual void f(int) override final
{
std::cout << "D::f" << std::endl;
}
};
class F : public D
{
public:
virtual void f(int) override
{
std::cout << "F::f" << std::endl;
}
};
3:区分重载(overload)、重写隐藏、和重写覆盖(override) 注:
参考资料来源:http://blog.csdn.net/dazhong159/article/details/7844369
注: 原文单纯的区分重载、隐藏、和覆盖,有点混乱,这里我做了个人的注解:
成员函数中分为重载(overload
)和重写(override)
,
而重写包括重写隐藏
, 重写覆盖
(常说override更多指的是这个)
类成员函数的重载(overload)、重写隐藏、和重写覆盖(override)区别
a.成员函数被重载(overload)的特征:
(1)相同的范围(在同一个类中);
(2)函数名字相同;
(3)参数不同;
(4)virtual关键字可有可无。
(5)对(1)的补充:不同类中形成的两种重载(overload):
5.1 如下代码:
// 5.1
class B
{
public:
virtual void f(short)
{
std::cout << "B::f" << std::endl;
}
};
class D : public B
{
public:
virtual void f(int)
{
std::cout << "D::f" << std::endl;
}
};
// 5.2
class B
{
public:
virtual void f(int) const
{
std::cout << "B::f " << std::endl;
}
};
class D : public B
{
public:
virtual void f(int)
{
std::cout << "D::f" << std::endl;
}
};
b.重写覆盖(override)是指派生类函数覆盖基类函数,特征是:
(1)不同的范围(分别位于派生类与基类);
(2)函数名字相同;
(3)参数相同;整个函数签名一样;
(4)基类函数必须有virtual关键字。
// 重写覆盖示例
class A
{
public:
void f(int)
{
cout << "A" << endl;
}
};
class B : public A
{
public:
virtual void f(int){};
};
c.重写“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:
(1)如果派生类的函数与基类的函数同名,但是参数不同。
此时,不论有无virtual关键字,基类的函数将被隐藏
(注意别与重载混淆)。但注意如果加上virtual则
通过父类型指针或引用访问不用类名::的形式访问也可以访问;
如下例:
(2)如果派生类的函数与基类的函数同名,并且参数也相同,
但是基类函数没有virtual关键字。此时,
基类的函数被隐藏(注意别与覆盖混淆)
// 重写隐藏示例
#include <iostream>
#include <cstring>
using namespace std;
class A
{
public:
void hello(){cout<<"A hello"<<endl;}
virtual void hello(int i){cout<<"A hello int"<<endl;}
};
class B:public A
{
public:
// B中的hello()隐藏了A中的hello(int)
void hello() const{cout<<"B hello"<<endl;}
};
// const导致重载时的隐藏问题,解决办法,在B中重新定义hello(int)函数
int main()
{
B b;
b.hello();
// b.hello(1); // error
b.A::hello(1); // 这种情况得通过类名::的形式访问
A *p = new B;
// 通过父类指针类型来访问还是正确,这种情况不用类名::的形式访问
p->hello(2);
}
// 注意分清楚重载(overload)和重写隐藏以及重写覆盖(override)
// 重写隐藏:
class A
{
public:
void test(){};
}
class B : public A
{
public:
/*
因为不是虚函数,同名则会导致重写隐藏父类的,这是要调用子类中
要调用父类的test()必须通过A::test();的形式进行调用;
*/
void test(int i){}
};
/*
另外注意:
一个函数中是否有const修饰函数也会造成重载,但不属于重写隐藏
或重写覆盖,是两个不同的函数, 如下例:
*/
class B
{
public:
virtual void test()const{};
};
class D : public B
{
public:
virtual void test(){};
/*
这个函数没有const修饰,因此这个函数尽管有多态的性质,
但是是属于两个函数,这是因为成员函数的隐形参数this的原因:
一个是this,一个是const this的类型;
*/
};
小结:
1)分清楚overload, 和override;
2)注意重载与重写覆盖,重写隐藏的条件;
参考资料:
http://blog.jobbole.com/44015/
http://blog.csdn.net/luyafei_89430/article/details/38662491
http://blog.csdn.net/dazhong159/article/details/7844369