对象的初始化
默认初始化
如Haveall one;Haveall arr1[2];
直接初始化
初始值在圆括号()中;
使用等号“=”。
拷贝初始化
用等号“=”初始化一个对象时,执行拷贝初始化,
如Haveall one = two;
Haveall arr1 = 4。
列表初始化
用花括号“{}”中的初始值构造对象,调用相应的构造函数,与直接初始化类似,如Haveall arr1[3] = {1,2}。
拷贝构造函数
如X one;X two(one)就是用one中的每个成员分别去初始化two的每个对应的成员。这种行为成为浅复制。
拷贝赋值运算符
重载赋值运算符
类X的赋值运算符要定义为类X的成员函数X& operator=(const X&){…};
运算符重载
重载的运算符:
定义重载运算符和定义普通函数类似
函数名由关键字operator 和其后要定义的运算符组成operator=, operator<<, operator+
返回类型:运算结果的类型
类的成员运算符函数:
this指向的对象呗作为运算符的第一个操作数;
一元运算符函数不需要提供参数;
二元运算符提供一个参数作为右操作数
非成员运算符函数:
一元运算符要提供一个类类型的参数;
二元运算符需要提供两个参数分别作为左右操作数,其中至少有一个参数必须是类类 型的
通常声明为类的友元,以便访问私有数据成员;
运算符函数只有在类类型的对象参与运算时才起作用
将运算符作用于类型正确的实参时,会引起重载运算符的调用,也可以像普通函数一样直接调用运算符函数;
如
include <iostream>
using namespace std;
class X
{
}
X date1,date2;
date1 +date2;//表达式中的调用
operator+(date1,date2);//全局函数直接调用
date1.operator+(date2);//成员函数直接调用
赋值(=)、下标([])、函数调用(())和成员函数访问箭头(->)运算符必须是成员函数
常用运算符的重载
- 赋值运算符只能用成员函数重载;
- 复合赋值运算符可以用成员或非成员重载;
- 对于类类型的参数,如果仅仅只是读参数的值,而不改变参数,应该作为const引用来传递;
- 所有赋值运算符均改变左值
- 逻辑运算符和关系运算符最好返回bool值
输入输出运算符的函数原型
istream& operator>>(istream&, type&);
ostream& operator<<(ostream&, const type&);
- 重载的赋值运算符必须定义为成员函数
返回左操作数的引用
- 复合赋值运算符不是必须用成员函数定义,但一般倾向于定义为成员函数,也返回左操作数的引用
组合与继承
组合:
将一个类的对象作为另一个类的成员,被称作组合或包含;
- 如果将嵌入的对象作为新类的公有成员,那么除了使用新类借口中提供的功能之外,还可以向包含的成员对象发送消息。
- 如果将嵌入对象作为新类的私有成员,这时它是新类内部实现的一部分:新类中的方法可以使用成员对象提供的功能,但新类值向外部提供自己的接口,隐含了包含的成员对象。
成员对象时组合对象的一部分,随着组合对象的创建而创建,随着组合对象的撤销而撤销
成员对象不作为独立元素对外部展现
成员初始化的次序和成员声明的次序相同,并不考虑它们在初始化列表中的排列顺序
继承:
在已有类的基础上创建新类的过程
在已有类的基础上继承得到新类型,这个新类型自动拥有已有类的特性,并可以修改继承到自己的特性或增加自己的新特性
一个B类继承A类,或称从类A派生类B
被继承的已有类称为基类;
继承得到的新类称为派生类;
派生类可以再被继承,这样构成的层次结构称为继承层次;
类继承关系的语法形式:
class 派生类名 : 基类名表
{
数据成员和成员函数声明
};
基类名表 构成
访问控制 基类名1,访问控制 基类名2,……访问控制 基类名n
访问控制表示派生类对基类的继承方式,使用关键字:
public 公有继承
private 私有继承
protected 保护继承
不管以何种方式继承基类,派生类都不能直接使用基类的私有成员。
派生类吸收基类中除构造函数和析构函数之外的全部成员。
- 通过在派生类中定义同名成员(包括成员函数和数据成员)来屏蔽(隐藏)在派生类中不起作用的部分基类成员。在派生类中使用基类的同名成员,显式地使用类名限定符:类名 :: 成员
基类的初始化:
在创建派生类对象时用指定参数调用基类的构造函数来初始化派生类继承基类的数据。
派生类构造函数声明为
派生类构造函数 ( 变元表 ) : 基类 ( 变元表 ) , 对象成员1( 变元表 )
… 对象成员n ( 变元表 ) ;
构造函数执行顺序:基类 à 对象成员à 派生类
在C++中,派生类构造函数的一般格式为:
派生类::派生类名(参数总表):基类名(参数表)
{
// 派生类新增成员的初始化语句
}
多继承
一个类有多个直接基类的继承关系称为多继承
多继承声明语法
class 派生类名 : 访问控制 基类名1 , 访问控制 基类名2 , … , 访问控制 基类名n {
数据成员和成员函数声
};
多继承的构造函数:
派生类名(参数总表):基类名1(参数表1),基类名2(参数表2),…,基类名n(参数表n)
{
// 派生类新增成员的初始化语句
}
组合与继承的选择:
如果多个类既共享数据也共享行为,应该让它们从一个共同基类继承而来,并在基类里定义共用的数据和操作
如果想由基类控制接口,使用继承;如果想自己控制接口,使用组合。
虚函数与多态:
- 多态性(Polymorphism)是指一个名字,多种语义;或界面相同,多种实现。
- 重载函数是多态性的一种简单形式。
- 虚函数允许函数调用与函数体的联系在运行时才进行,称为动态联编。
- 冠以关键字 virtual 的成员函数称为虚函数
- 实现运行时多态的关键首先是要说明虚函数,另外,必须用基类指针调用派生类的不同实现版本
注:
-
-
-
- 一个虚函数,在派生类层界面相同的重载函数都保持虚特性
- 虚函数必须是类的成员函数
- 虚函数可以是另一个类的友元
- 析构函数可以是虚函数,但构造函数不能是虚函数
- 在派生类中重载基类的虚函数要求函数名、返回类型、参数个数
- 参数类型和顺序完全相同
- 如果仅仅返回类型不同,C++认为是错误重载
- 如果函数原型不同,仅函数名相同,丢失虚特性
- 构造函数不能是虚函数,但析构函数可以是虚的(通常声明为虚函数),虚析构函数用于指引delete运算符正确析构动态对象。
- 派生类对基类中声明虚函数重新定义时,关键字virtual可以不写。
- 一般通过基类指针访问虚函数时才能体现多态性。
- 一个虚函数无论被继承多少次,保持其虚函数特性。
- 虚函数必须是其所在类的成员函数,而不能是友元函数,也不能是静态函数
- 构造函数、内联成员函数、静态成员函数不能是虚函数。(虚函数不能以内联的方式进行处理)
-
-
纯虚函数和抽象类:
- 纯虚函数是一种特殊的虚函数在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。
- 纯虚函数是一个在基类中说明的虚函数,在基类中没有定义, 要求任何派生类都定义自己的版本。
- 纯虚函数为各派生类提供一个公共界面
- 纯虚函数说明形式:virtual 类型 函数名(参数表)= 0 ;
- 一个具有纯虚函数的基类称为抽象类。