- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章C++中的重载、覆盖、隐藏介绍由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
前几天面试时被问及C++中的覆盖、隐藏,概念基本答不上来,只答了怎么用指针实现多态,也还有遗漏。最终不欢而散。回来后在网上查找学习了一番,做了这个总结。其中部分文字借用了别人的博客,望不要见怪.
•概念 。
1、重载(overload) 指函数名相同,但是它的参数表列个数或顺序,类型不同。但是不能靠返回类型来判断。 (1)相同的范围(在同一个作用域中) ; (2)函数名字相同; (3)参数不同; (4)virtual 关键字可有可无。 (5)返回值可以不同; 。
2、重写(也称为覆盖 override) 是指派生类重新定义基类的虚函数,特征是: (1)不在同一个作用域(分别位于派生类与基类) ; (2)函数名字相同; (3)参数相同; (4)基类函数必须有 virtual 关键字,不能有 static 。 (5)返回值相同(或是协变),否则报错;<—-协变这个概念我也是第一次才知道… 。
(6)重写函数的访问修饰符可以不同。尽管 virtual 是 private 的,派生类中重写改写为 public,protected 也是可以的 。
3、重定义(也成隐藏) (1)不在同一个作用域(分别位于派生类与基类) ; (2)函数名字相同; (3)返回值可以不同; (4)参数不同。此时,不论有无 virtual 关键字,基类的函数将被隐藏(注意别与重载以及覆盖混淆) 。 (5)参数相同,但是基类函数没有 virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆) .
•例子 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
|
#include <iostream>
using
namespace
std;
class
SParent
{
public
:
SParent( ){};
SParent(
const
SParent &p )
{
cout <<
"parent copy construct"
<< endl;
}
int
add(
int
a,
int
b )
{
cout <<
"parent int add"
<< endl;
return
a + b;
}
double
add(
double
a,
double
b )
{
cout <<
"parent double add"
<< endl;
return
a + b;
}
virtual
int
dec(
int
a,
int
b )
{
cout <<
"parent int dec"
<< endl;
return
a - b;
}
};
class
SChild :
public
SParent
{
public
:
//using SParent::add;
float
add(
float
a,
float
b )
{
cout <<
"child float add"
<< endl;
return
a + b;
}
int
dec(
int
a,
int
b)
{
cout <<
"child int dec"
<< endl;
return
a - b;
}
};
int
main()
{
/* 测试重载 */
SParent parent;
parent.add( 3,5 );
parent.add( (
double
)3,(
double
)5 );
cout << endl;
/* 测试覆盖 */
SChild *pchild = (SChild *)
new
SParent();
/* 基类强转为子类...危险...,用dynamic_cast转换也不行 */
pchild->dec( 10,3 );
SParent *pparent =
new
SChild();
pparent->dec( 11,3 );
cout << endl;
/* 测试隐藏 */
SChild child;
child.add( (
int
)3,(
int
)5 );
cout << endl;
/* 测试函数表 */
((SParent *)NULL)->add( 4,6 );
((SChild *)NULL)->add( 4,6 );
int
a = 0;
((SChild *)&a)->add( 4,6 );
cout << endl;
/* 测试函数地址 */
((SParent)child).add( (
int
)4,(
int
)8 );
child.SParent::add( 3,5 );
return
0;
}
|
输出结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
parent
int
add
parent
double
add
parent
int
dec
child
int
dec
child
float
add
parent
int
add
child
float
add
child
float
add
parent copy construct
parent
int
add
parent
int
add
按 <RETURN> 来关闭窗口...
|
•理解 int SParent::add(int a,int b)与double SParent::add( double a,double b )是重载 。
int SParent::add(int a,int b)与double SParent::add( double a,double b )都被子类SChild中的float SChild::add( float a,float b )隐藏 。
int SParent::dec( int a,int b )被子类SChild中的int SChild::dec( int a,int b )覆盖 。
•测试 。
1.重载测试,简单易懂,略过。 2.覆盖测试。dec函数在基类、子类中同名同参,为虚函数,故称覆盖.
SChild *pchild = (SChild *)new SParent()创建的是一个基类对象,其函数表应该为 。
SParent *pparent = new SChild();创建一个子类对象,其函数表应该为 。
由上面的函数表可见,当发生覆盖时,子类的函数名会把基类的同名函数覆盖(这也就是为什么叫覆盖的原因吧)。这样我们可以利用一个指向子类的基类指针实现多态。但重点只有一 个,就是函数表里到底指向谁(不管这个指针经过转换后是什么类型的).故输出分别为父类、子类。这是一个运行时多态.
3.隐藏测试 。
int SParent::add(int a,int b)与double SParent::add( double a,double b )都被子类SChild中的float SChild::add( float a,float b )覆盖,是因为他们同名,而且在不同的作用域中(基类、子类作用域是不同的)。child.add( (int)3,(int)5 );这行代码中,编译器在子类中查找add函数,只找到了一个(基类的add(int a,int b)会被编译根据隐藏规则略过),再根据隐式类型转换发现该函数适用。如果无法隐式转换,则编译不过。隐藏的原因:防止隐式类型转换造成错误。比如int也是可以转换成char的,假如基类有一函数add(char a,char b),子类也有一函数add(double a,double b)。程序员想着在子类隐式把int转换为double,但编译器可能调的是基类的。这也防止了一些库或封装好的基类对程序员造成困扰.
像上面的代码,如果你确实需要基类的函数,可以用using SParent:add。则把基类的add函数域扩大到了子类,构成重载.
4.函数表测试 。
上面我们说到函数表,这个是在编译时定好的,程序运行时加载到内存中。这意味着我们可以直接通过地址去调用函数。所以((SChild *)NULL)->add( 4,6 );这种代码也是能运行通过的。网上还有人通过计算直接取到了函数表的地址直接调用了。但这种代码不安全不规范不说,还有个更大的问题。当成员函数里需要调用成员变量时,通过这种假的对象指针肯定找不到成员变量表,直接访问了非法内存.
5.函数地址测试 。
有了隐藏、覆盖,哪么我们要怎么调用被隐藏、覆盖的函数呢。下面有两种方法:
((SParent)child).add( (int)4,(int)8 ); child.SParent::add( 3,5 ),
第一种是比较低效的方法。事实上它是通过拷贝构造函数生成一个临时的基类变量去调用基类的add函数。 第二种通过::指定域去访问。这种方法是编译器根据域直接找到了基类的函数地址,跟函数表没有多大关系.
最后此篇关于C++中的重载、覆盖、隐藏介绍的文章就讲到这里了,如果你想了解更多关于C++中的重载、覆盖、隐藏介绍的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
假设我有一个类,我在其中重载了运算符 == : Class A { ... public: bool operator== (const A &rhs) const; ... };
我知道你不应该使用 std::find(some_map.begin(), some_map.end()) 或 std::lower_bound,因为它会采用线性时间而不是 some_map.lowe
我正在尝试在 Haskell 中定义 Vector3 数据类型,并允许在其上使用 (+) 运算符。我尝试了以下方法: data Vector3 = Vector3 Double Double Doub
我已经为我的类图将运算符重载为“-”。它的用途并不完全直观(糟糕的编码 - 我知道)但是如果我做 graph3 = graph2-graph1 那么图 3 是应该只接收图 2 和图 1 中的那些顶点。
这个问题在这里已经有了答案: 关闭 11 年前。 Possible Duplicate: Operator overloading 我想重载 以按字母顺序排列字符串,但我不确定该怎么做。 如何再次
下面的代码给我一个编译错误。谁能告诉我为什么? class mytype { public: int value; mytype(int a) { value = a;
这有什么问题吗? class Vec2 attr_accessor :x, :y # ... def += (v) @x += v.x @y += v.y retu
是否可以重载 [] 运算符两次?允许这样的事情:function[3][3](就像在二维数组中一样)。 如果可能的话,我想看看一些示例代码。 最佳答案 您可以重载 operator[] 以返回一个对象
我的团队目前正在与 Lua 合作,创建一个 android 游戏。我们遇到的一件事是表面上无法创建重载构造函数。 我习惯于使用默认值设置一个对象,然后在需要时使其过载。 前任: apples() {
我有一个网页,在某个时候显示一个导航栏,它只不过是一个 a 元素的列表 (ul)。所述 a 元素的大多数样式规则都是通用的。唯一应该改变的部分是要显示的图像,可以从列表中每个 li 元素的 id 标签
我对使用/重载“范围步长”运算符(.. ..)很感兴趣,但我终其一生都无法了解如何使用它。 在文档中它说 // Usage: start .. step .. finish 但是在 F# shell
Java 11(可能无关紧要): public static String toString(Object obj) { return ReflectionToStringBuilder.to
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引起辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the he
我无法理解以下代码(针对行号进行注释) class Base { void m1(Object o) { } void m2(String o) { } } publi
我有以下代码片段: #include using namespace std; struct Integer{ int x; Integer(const int val) : x(v
class myclass{ //definitions here }; myclass e; int myarray[10]; /* Do something... */ e = myarray;
为什么不能将下标运算符(operator [])作为 friend 函数重载? 最佳答案 正如Bjarne Stroustrup在D&E book中所说: However, even in the o
我有以下代码片段: #include using namespace std; struct Integer{ int x; Integer(const int val) : x(v
因此,我有一个问题是我最近尝试重载 namespace Eng { /** * A structure to represent pixels */ typedef
如何重载onResume()以正确的方式工作?我想从 activity 返回到 MainActivity ,我希望在其中具有与应用程序启动后相同的状态。我想使用 recreate() 但它循环了或者类
我是一名优秀的程序员,十分优秀!