类继承
能够从已有的类派生出新的类,而派生类继承了原有类(称为基类)的特征,包括方法。
1.可以在已有类的基础上添加功能。
2.可以给类添加数据。
3.可以修改类方法的行为。
派生类需要自己的构造函数。
派生类可以根据需要添加额外的数据成员和成员函数。
构造函数必须给新成员(如果有的话)和继承的成员提供数据。
(继承类型
当一个类派生自基类,该基类可以被继承为 public、protected 或 private 几种类型。继承类型是通过上面讲解的访问修饰符 access-specifier 来指定的。
我们几乎不使用 protected 或 private 继承,通常使用 public 继承。当使用不同类型的继承时,遵循以下几个规则:
公有继承(public):当一个类派生自公有基类时,基类的公有成员也是派生类的公有成员,基类的保护成员也是派生类的保护成员,基类的私有成员不能直接被派生类访问,但是可以通过调用基类的公有和保护成员来访问。
保护继承(protected): 当一个类派生自保护基类时,基类的公有和保护成员将成为派生类的保护成员。
私有继承(private):当一个类派生自私有基类时,基类的公有和保护成员将成为派生类的私有成员。
)
无论什么方式继承,派生类都不能直接访问基类的私有成员,而必须通过基类方法进行访问。
有关派生类构造函数的要点如下:
1.首先创建基类对象。
2.派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数。
3.派生类构造函数应初始化派生类新增的数据成员。
创建派生类对象时,程序首先调用基类构造函数,然后再调用派生类构造函数。基类构造函数负责初始化继承的数据成员;派生类构造函数主要用于初始化新增的数据成员。派生类的构造函数总是调用一个基类的构造函数。可以使用初始化列表语法指明要使用的基类构造函数,否则将使用默认的基类构造函数。
派生类对象过期时,程序将首先调用派生类析构函数,然后在调用基类析构函数。
成员初始化列表
派生类构造函数可以使用初始化列表机制将值传递给基类构造函数
derived ::derived(type x,type y):base(x,y){
}
其中derived是派生类 base是基类名 x y基类是构造函数变量 。
同理 基类构造函数也可以这样做。
基类指针可以在不进行显式类型转换的情况下指向派生类对象;
基类引用可以在不进行显式类型转换的情况下指向派生类对象。
然而,基类指针或引用只能用于调用基类方法,不能调用派生类的方法。基类声明的虚函数除外。
通常,C++要求引用和指针类型与赋给的类型匹配,但这一规则对继承来说是例外。
然而,这种例外只是单向的,不可以将基类对象和地址赋给派生类引用和指针。因为派生类指针或引用为基类调用派生类函数是没有意义的。
继承 is-a关系
公有继承是最常用的方式,他建立一种is-a关系,即派生类对象也是一个基类对象,可以对基类对象进行的任何操作,也可以对派生类对象执行。
多态公有继承
有两种机制可用于实现多态公有继承:
1.在派生类中重新定义基类的方法。
2.使用虚方法。
如果父类子类存在函数名相同程序将使用对象类型来确定使用哪个版本。
如果方法是通过引用或指针而不是对象调用的,它将确定使哪一种方法。如果没有使用关键字virtual,程序将根据引用类型或指针类型选择方法;如果使用了virtual,程序将根据引用或指针指向的对象的类型来选择方法。
如果要在派生类中重新定义基类的方法,通常应将积累方法声明为虚的。这样,程序将根据对象类型而不是引用或指针的类型来选择方法。为基类声明一个虚析构函数也是一种惯例。
虚析构函数可以确保正确的析构函数被调用(由对象类型来选择方法而不是指针或引用决定)。
静态联编和动态联编
在编译过程中进行联编称为静态联编,又称早期联编。然而,虚函数使这项工作变得更困难,使用拿一个函数是不能在编译时确定的,因为编译器不知道用户将选择那种对象。所以编译器必须生成能够在程序运行时选择正确的虚方法的代码,这被称为动态联编,又称晚期编程。
将派生类引用或指针转化为基类引用或指针被称为向上强制转换,这使公有继承不需要进行显式类型转换。
相反的过程——将基类指针或引用转换为派生类指针或引用——成为向下强制转换。
如果不是显式类型转换,则向下强制转换是不允许的。原因是is-a关系通常是不可逆的。
隐式向上强制转换是基类指针或引用可以指向基类对象或派生类对象,因此需要动态联编。C++使用虚成员函数来满足这种需求。
静态联编的好处 1.效率更高 2.指出不需要重新定义该函数。这表明,金奖那些语气将被重新定义的方发生吗为虚的。
如果要在派生类中重新定义基类的方法,则将它设置为虚方法;否则,设置为非虚方法。
通常编译器处理虚函数的方法是:给每个对象添加一个隐藏成员。隐藏成员中保存了一个指向函数地址数组的指针。这个数组称为虚函数表。虚函数表中储存了为类对象进行声明的虚函数地址。
使用虚函数使,在内存和执行速度方面有一定的成本 包括:
1.每个对象都将增大,增大量为储存地址的空间;
2.对于每个类,编译器都创建一个虚函数地址表(数组);
3.对于每个函数调用,都需要执行意向额外的操作,即到表中查找地址。
1.构造函数不能是虚函数。创建派生类对象时,将调用派生类的构造函数,而不是基类的构造函数。
2.析构函数应是虚函数,除非类不用做基类。
否则 当创建基类指针并指向动态分配的派生类的内存时,当释放这个指针时,将会释放基类所占的内存而不是派生类的。
所以,通常应 给基类提供一个虚析构函数,即使他并不需要析构函数。
3.友元不能是虚函数,因为友元不是类成员,而只有成员才能是虚函数。可以换种方式使用友元调用虚成员函数解决。
4.重新定义将隐藏方法。
派生类和基类如果出现相同的函数名,对于派生类的对象来讲派生类的函数将会覆盖基类的函数。
第一 如果重新定义继承的方法,应确保与原来的原型完全相同,但如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针(例外)。这种特性被称为返回类型协变,因为允许返回类型随类类型的变化而变化。第二,如果基类声明被重载了,则应在派生类中重新定义所有的基类版本。
private和protected之间的区别只有在基类派生的类中才会表现出来。(派生类的成员可以直接访问基类的保护成员但不能直接访问基类的私有成员)因此,对于外部世界来说
保护成员的行为与私有成员相似;但对于派生类来说,保护成员的行为与公有成员相似。最好对类数据成员采用私有访问控制,不要使用保护访问控制;同时通过基类方法使派生类能够访问基类数据。
抽象基类
当类声明中包含纯虚函数时,则不能创建该类的对象。这里的理念是,包含纯虚函数的类只用作基类。
由含有纯虚函数的基类派生出来的类叫做具体类,这表示可以创建这些类型的对象。
在设计ABC之前,首先应开发一个模型——指出编程问题所需的类以及他们之间的相互关系。一种学院派思想认为,如果要设计类继承层次,则只能将那些不会被用作基类的类设计为具体的类。这种方法的设计更清晰,复杂程度更低。
ABC要求具体派生类覆盖其纯虚函数——破事派生类遵循ABC设置的接口规则。
继承和动态内存分配
第一种情况:派生类不使用new
基类声明中包含了构造函数使用new时需要的特殊方法:析构函数、复制构造函数、重载赋值运算符。那么派生类就不需要再次定义上述这些。
派生类的析构函数总是要进行一些操作:执行自身的代码后调用基类析构函数。
在复制类成员或继承的类组件时,则是使用该类的复制构造函数完成的。
类的默认赋值运算符将自动使用基类的复制运算符来对基类组件进行赋值。
第二种情况:派生类使用new
这种情况下,必须为派生类定义显式析构函数、赋值构造函数和赋值运算符。
派生类析构函数自动调用基类的析构函数,故其自身的职责是对派生类构造函数执行工作的进行清理。
类设计回顾...
(完)
网友评论