一,继承
(1). 继承的基本语法
继承是面向对象的基本特征之一。简单的继承示例如下:
class
ExampleBase
{
private
:
int
x;
public
:
ExampleBase( ) : x(0)
{
cout <<
"ExampleBase 类构造函数"
<< endl;
}
void
SetX (
int
n
)
{
x =
n
;
}
int
GetNum()
{
return
x;
}
};
class
Example
:
public
ExampleBase
{
private
:
int
y;
public
:
Example( ):y(10)
{
cout <<
"Example 类构造函数"
<< endl;
}
int
GetNum()
{
return
y;
}
};
int
_tmain
(
int
argc,
_TCHAR*
argv
[])
{
Example
ex;
cout << ex.GetNum() << endl;
return
0;
};
// return:
// ExampleBase 类构造函数
// Example 类构造函数
// Example 类函数
// 10
总结:
- 创建派生类实例时,构造函数调用从最底层基类开始直至当前类。清理对象时顺序相反。
- 基类的访问修饰符对派生类依然有效。如 基类中的私有成员即使是派生类中依然无法访问。
- 派生类中只要有和基类同名的函数,即便是签名不同(参数或返回值不同),对于派生类对象,基类的同名函数将会被隐藏(和重载不同)。
你可以在派生类中通过作用域操作符调用基类的函数
Base
::GetNum();
(2). protected 修饰符
声明为 protected 的成员可以让从其派生的类访问。而在其他地方和声明为 private 一样外界无法访问(可参见下节示例)。
(3). 派生类中显式调用基类的构造函数
当基类中存在多个构造函数时,就有可能需要在派生类中调用指定的基类构造函数。方法如下:
class
ExampleBase
{
protected
:
int
x;
public
:
ExampleBase( ) : x(0)
{
cout <<
"ExampleBase 类无参数 构造函数"
<< endl;
}
ExampleBase(
int
n
) : x(
n
)
{
cout <<
"ExampleBase 类有参数 构造函数"
<< endl;
}
void
SetX (
int
n
)
{
cout <<
"ExampleBase 类函数"
<< endl;
x =
n
;
}
int
GetNum()
{
return
x;
}
};
class
Example
:
public
ExampleBase
{
private
:
int
y;
public
:
Example( ):y(10),ExampleBase (15)
{
cout <<
"Example 类构造函数"
<< endl;
}
int
GetNum()
{
cout <<
"Example 类函数"
<< endl;
return
y;
}
int
SumXy()
{
return
x + y;
}
};
(4). 多重继承
C++中允许多重继承,既 一个类可以有多个基类,
class
A
{
protected
:
int
x;
public
:
A(): x(1)
{
cout <<
"A 类构造函数"
<< endl;
}
};
class
B
{
protected
:
int
y;
public
:
B(): y(2)
{
cout <<
"B 类构造函数"
<< endl;
}
};
class
C
{
protected
:
int
z;
public
:
C(): z(3)
{
cout <<
"C 类构造函数"
<< endl;
}
};
class
Example
:
public A , public B, public C
{
public
:
Example( )
{
cout <<
"Example 类构造函数"
<< endl;
}
int
SumXyz()
{
return
x + y + z;
}
};
int
_tmain
(
int
argc,
_TCHAR*
argv
[])
{
Example
ex;
cout << ex.SumXyz();
};
//return:
//A 类构造函数
//B 类构造函数
//C 类构造函数
//Example 类构造函数
//6
后边还会继续讨论多重继承。
二,多态
(1). 多态的基本理念
多态就是 可以用基类对象表示其派生类对象,在调用该对象上的函数时能正确调用派生类对象的相应函数(动态绑定)的一种能力。
典型的应用场景如: 某个函数的形参为基类类型(指针或引用),当调用时可以为其传入该基类的派生类并能正确调用。很显然一般的函数覆盖是实现不了动态绑定的,于是 C++引入了虚函数来完成动态绑定。
(2). 虚函数 virtual
C++中使用关键字 virtual 声明虚函数,
class
Base
{
public
:
virtual void ShowInfo()
{
cout <<
"is Base class"
<< endl;
}
};
class
ExampleOne
:
public
Base
{
public
:
void ShowInfo()
{
cout <<
"is ExampleOne class"
<< endl;
}
};
class
ExampleTwo
:
public
Base
{
public
:
void
ShowInfo(
int
x)
{
cout <<
"is ExampleTwo class"
<< endl;
}
};
int
_tmain
(
int
argc,
_TCHAR*
argv
[])
{
Base *b = new ExampleOne ;
b->ShowInfo();
Base
*b2 =
new
ExampleTwo
;
b2->ShowInfo();
}
//return:
//is ExampleOne class
//is Base class
总结:
- 只有虚函数才具有多态(动态绑定)性。
- 只有派生类函数和基类中的虚函数签名完全相同时才会具有多态性,
- 用于覆盖基类虚函数的派生类函数也可以声明为虚函数。
- 析构函数也可以为多态的,所以一般的基类析构函数应为虚函数。
- 构造函数或析构函数中调用虚函数时,将只会调用其类型上的版本。
顺便提一句。即便是已经实现了动态绑定,也可以用如下语法显式的指定应该调用哪个类的函数
b->
Base::ShowInfo();
(3). 纯虚函数
在虚函数后加 "= 0" 时,该虚函数就为纯虚函数。因为纯虚函数没有方法体自然就没法调用执行。这样的类是不完全的,所以
拥有纯虚函数的类会变成了抽象类(不能被实例化的类)。
class
Base
{
public
:
virtual void ShowInfo() = 0;
};
当一个类继承自抽象类并且未覆盖其纯虚函数时,该类也会变成抽象类(显而易见)。
(4). 虚继承
当一个派生类间接继承多个相同基类时,该基类会在内存中有多个副本。
class
Base
{
public
:
int
num;
};
class
MidOne
:
public
Base
{ };
class
MidTwo
:
public
Base
{ };
class
Example
:
public
MidOne
,
public
MidTwo { };
int
_tmain
(
int
argc,
_TCHAR*
argv
[])
{
Example
*ex =
new
Example
;
cout << ex->num;
// error C2385: 对“num”的访问不明确, 因为有多个 Base的副本
}
可以通过 继承时添加 修饰符 virtual ,使内存只存储一个该基类的副本。
(5). 动态绑定原理
当类中有虚函数时(动态绑定基础),会生成一个保存该类所有虚函数(包括继承来
未被覆盖的)地址的虚表(vtable),然后用一个指针(vptr)指向虚表,并将(大多数情况)该指针放在类的首地址。当将派生对象赋给基类对象时实际只是改变了基类对象的引用(指向派生类对象地址),这样就可以调用正确的虚函数,从而实现动态绑定。
class
Base
{
public
:
virtual
void
ShowInfo()
{
cout <<
"is Base"
<< endl;
}
virtual
void
Say()
{
cout <<
"is Base say"
<< endl;
}
};
class
Example
:
virtual
public
Base
{
public
:
virtual
void
ShowInfo()
{
cout <<
"is Example"
<< endl;
}
};
typedef
void
(*
py)();
int
_tmain
(
int
argc,
_TCHAR*
argv
[])
{
Base
*b =
new
Example
;
void*
vptr
= (
void
*)*(
unsigned
long*)b;
unsigned
char
*p = (
unsigned
char
*)vptr;
p +=
sizeof(
void
*) * 0;
py
vtable = (
py
) (
void
*)*(
unsigned
long
*)p;
vtable();
p +=
sizeof(
void
*) * 1;
py
vtable2 = (
py
) (
void
*)*(
unsigned
long
*)p;
vtable2();
}
//return:
// is Example
// is Base say
三,动态绑定对象切割
(1). 切割
在某种情况下即便当前基类是一个指向派生类的引用(多态),也会将其转换为真正的基类,而派生类部分将被切掉。
(2). 值类型形参
当形参为值类型时,动态绑定类型将被切割,转换为纯基类类型。
class
Base
{
public
:
virtual
void
ShowInfo()
{
cout <<
"is Base"
<< endl;
}
};
class
Example
:
virtual
public
Base
{
public
:
virtual
void
ShowInfo()
{
cout <<
"is Example"
<< endl;
}
};
void
Test (
Base b
)
{
b.ShowInfo();
}
int
_tmain
(
int
argc,
_TCHAR*
argv
[])
{
Base *ex = new Example ;
Test(*ex);
}
//return:
//is Base
(3). 容器和数组
当将动态绑定的类型存入容器(对象而非指针)时,也会被切割,转换为纯基类类型。
int
_tmain
(
int
argc,
_TCHAR*
argv
[])
{
Base *ex = new Example ;
Base
baseArray[3] = {};
baseArray[0] = *ex;
baseArray[0].ShowInfo();
Base *ex2 = new Example ;
Vector<
Base
> vr;
vr.push_back(*ex2);
vr[0].ShowInfo();
}
//return:
// is Base
// is Base
-
<原创文章 转载请注明出处 http://blog.csdn.net/meiwm 谢谢>
出处:
http://blog.csdn.net/meiwm
本文为原创,本文版权归作者所有。欢迎转载,但请务必保留此段声明,且在文章页面明显位置给出原文连接,谢谢合作。
-
本文详细介绍了C++中的继承概念,包括基本语法、protected修饰符的使用、派生类如何显式调用基类构造函数以及多重继承。接着探讨了多态的本质,讲解了虚函数、纯虚函数、虚继承和动态绑定的原理。同时,讨论了动态绑定对象切割的问题,如切割现象、值类型形参和容器与数组的影响。

1838

被折叠的 条评论
为什么被折叠?



