3、c++编译系统为每个对象所占用存储空间只是该对象的数据部分所占用的存储空间,而不包括函数代码所占用的存储空间。
第三章构造函数与析构函数
★构造函数
建立一个对象时,对象的状态(数据成员的取值)是不确定的。
为了是对象的状态确定,必须对其进行正确的初始化。
(1)构造函数是成员函数,它可自动被编译系统调用,进行对象的初始化;
(2)构造函数调用并不是由用户来完成的;
(3)构造函数的名字和类名同名,在定义构造函数时,不能指定返回类型,即使void类型也不可以;
(4)构造函数的声明形式:
类名(形参1,形参2,……形参n);//可以没有形参
(5)构造函数可以在类体内声明时定义,也可以在类体外定义。
在类体外定义形式:
类名:
:
类名(形参1,形参2,……形参n)
{x1=形参1;
x2=形参2;
……
}
或类名:
:
类名(形参1,形参2……形参n):
x1(形参1),x2(形参2),……,xn(形参n){}
★构造函数的作用及性质
✓C++提供构造函数来处理对象的初始化
✓构造函数是特殊函数,在建立对象时自动执行
✓构造函数的名字必须与类名相同
✓构造函数不具有任何类型,没有返回值。
★说明:
(1)构造函数不需用户调用,也不能被用户调用;
(2)如果用户未定义构造函数,则系统会自动提供一个默认的构造函数;系统提供的默认构造函数函数体是空的,也没有参数无参的构造函数属于默认构造函数
(3)一旦程序定义了自己的构造函数,系统就不再提供这个默认构造函数;
(4)尽管一个类中可以包含多个构造函数,但是对于每一个对象来说,建立对象时,只能执行其中一个构造函数,并非每个构造函数都被执行;
★默认构造函数
※用户自己没有定义构造函数时,由系统自动生成。
※该构造函数没有函数体,也没有参数,不执行初始化操作。
※一个类只能有一个默认的构造函数。
例:
Time:
:
Time(){}
※需注意:
一旦程序定义了自己的构造函数,系统就不再提供默认构造函数。
★注意:
※尽管类中可以包含多个构造函数,但对于每个对象来说,只执行其中一个构造函数。
※如果在建立时选用无参构造函数,应注意定义对象的语句:
Boxbox1;√
而不能写成:
Boxbox1();×
★构造函数的默认参数
※构造函数中的参数可以通过实参传递,也可以指定为某些默认值。
※就是用户不指定实参值,编译系统使用形参的默认值。
★说明:
v在声明构造函数时指定默认值,而不能只在定义构造函数时指定默认值。
v构造函数声明时形参名可以省略。
v一个类中只能有一个默认构造函数
例:
Box();
Box(inth=10,intw=10,intl=10);
v一个类中定义了全部默认参数的构造函数后,就不能再重载构造函数。
★复制构造函数
v使用已有的对象来建立一个新对象。
v特点:
※该函数是一种构造函数。
※该函数只有一个参数,并且是对某个对象的引用。
※其定义格式:
类名:
:
复制构造函数名(类名&引用名)
类名:
:
复制构造函数名(const类名&引用名)
★说明:
(1)运算符new用于建立生存期可控的对象,new返回这个对象的指针;
(2)当使用new建立一个动态对象时,new首先分配足以保存Test类的一个对象所需要的内存,然后自动调用构造函数来初始化这块内存,在返回这个动态对象的地址。
★new运算符
✓new运算符动态分配内存
✓格式:
指针变量=newtype;
✓返回一个指向对象的指针的值,即所分配的内存空间的起始地址。
否则返回一个0指针。
✓例如:
Box*pt;
pt=newBox;//同时调用该类的构造函数
★delete运算符
v格式:
delete指针变量;
v在执行delete时,在释放内存空间之前,自动调用析构函数,完成善后清理工作。
★运算符new动态建立对象,delete撤销对象
★new和delete必须配对使用。
★运算符delete必须用于先前new分配的的有效指针
★new可以为数组分配内存,但当释放时,也可告诉delete数组有多少个元素。
★说明:
(1)应该在什么地方指定构造函数的默认参数?
应该在声明构造函数时指定默认值。
(2)如果构造函数的全部参数指定了默认值,则在定义对象时可以给一个或几个实参,也可以不给出实参。
(3)在一个类中定义了全部是默认参数的构造函数后,不能再定义重载构造函数。
例如:
在一个类中有以下构造函数的声明:
Box(int=10,int=10,int=10);//指定全部为默认构造函数
Box();//声明无参那的构造函数;是重载构造函数
Box(int,int);//声明有两个参数的构造函数,是重载
★复制初始化构造函数
(1)复制构造函数的形式:
X:
:
X(X&)
(2)说明:
※通常使用已有的对象来建立一个新对象;
※在使用该类的一个对象初始化该类的另一个对象时,调用这个函数;
例:
Testobj1(5,5.6);
Testobj2(obj2);
★析构函数
在对象消失时,使用析构函数释放由构造函数分配的内存。
1、定义:
(1)析构函数与类同名,为了与构造函数区分,在定义析构函数前加上“~”
(2)析构函数也不能指定返回类型,即使void返回类型也不行。
而且析构函数不能指定参数,但是可以显式地说明参数为void,即如X:
:
~X(void)Test:
:
~Test(){}
(3)一个类只能定义一个析构函数
(4)析构函数在对象的生存期结束时自动被调用
(5)如果在定义类时没有定义析构函数,编译系统要为这个类产生一个默认的析构函数
★析构函数和对象数组及运算符delete
※C++系统为对象数组的每个元素调用一次析构函数。
※当使用delete删除一个动态对象时,它首先为这个动态对象调用析构函数,然后释放这个动态对象占用的内存。
※每个对象都有一个析构函数,如果在定义一个类时没有定义一个析构函数,C++编译器要为这个类产生一个缺省的析构函数。
例如:
Test:
:
~Test()
{
}
★说明:
(1)析构函数的作用并不是删除对象,而是在撤销对象占用的内存之前完成一些清理工作。
(2)先构造的后析构,后构造的先析构。
它相当于一个栈,先进后出。
(3)static局部对象在函数调用结束时对象并不释放,因此也不调用析构函数。
(4)如果定义一个全局对象,则在程序的流程离开其作用域是(如main函数结束或调用exit函数)时,调用该全局对象的析构函数。
(5)如果用new运算符动态地建立一个对象,当用delete运算符释放该对象时,先调用该对象的析构函数。
★指向对象的指针:
对象空间的起始地址就是对象的指针。
★定义类对象的指针变量的一般形式:
v类名*对象指针名;
★指向对象成员的指针
v数据类型名*指针变量
例:
int*p;p=&t.hour;
★5、对象成员
※可以在一个类中说明具有类的类型的数据成员,这些成为对象成员。
※一般形式:
classX{
类名1成员名1
类名2成员名2
…….
类名n成员名n
};
※X类的构造函数要调用这些对象成员所在类的构造函数,X类的构造函数的定义形式:
X:
:
X(参数表0):
成员1(参数表1),成员2(参数表2),…,(参数表n)
{
//……
}
※对对象成员的构造函数的调用顺序取决于这些对象成员在类中的说明顺序,与它们在成员初始化列表中给出的顺序无关。
※在建立X类对象时,先调用对象成员的构造函数,初始化对象成员,然后才执行X类的构造函数,初始化X类的其他成员。
※析构函数的调用顺序与构造函数正好相反。
★对象的初始化:
※使用等号在说明语句进行初始化。
例如:
TestTry1=5;
TestTry2=Test(10);
※注意:
等号不是运算符,编译器对这种表示有特殊的解释→
TestTry1(5);
TestTry2(10);
★对象的赋值:
1、一个对象的值可以赋给另一个同类的对象。
2、对象之间赋值也是通过赋值运算符“=”进行的。
形如:
对象名1=对象名2;
3、对象的赋值只对其中的数据成员赋值,而不对成员函数赋值。
数据成员占存储空间,不同对象的成员函数占有不同的存储空间。
4、类的数据成员中不能包括动态分配的数据,否则在赋值式可能出现严重后果。
#include
classone
{public:
one(){cout<<1<};
classtwo:
publicone
{intk;
public:
two():
k(0){cout<<2<two(intn):
k(n){cout<<3<};
voidmain()
{twob(4),c;}
★this指针
v每一个成员函数中都包含一个特殊的指针,这个指针的名字是固定的----this
vThis指针是隐式使用的,它是作为参数被传递给成员函数。
v例:
intBox:
:
volume()
{retuanheight*width*length;}
C++把它处理为:
intBox:
:
volume(Box*this)
{retuanthis->height*this->width*this->length;}
第四章继承与派生
★继承
※就是在一个已存在的类的基础上建立一个新的类。
※已存在的类成为基类或父类。
※新建立的类称为派生类或子类。
★派生类的特点
Ø继承了基类的所有数据成员和成员函数。
Ø增加了新的成员(数据成员或成员函数)。
Ø重新定义了已有的成员函数。
Ø改变基类成员的访问权限。
※用公有继承方式建立的派生类称为公有派生类,其基类称为公有基类。
公有基类
在公有派生类中的访问属性
私有成员
不可访问
公有成员
公有
保护成员
保护
※用私有继承方式建立的派生类称为私有派生类,其基类称为私有基类。
私有基类
在私有派生类中的访问属性
私有成员
不可访问
公有成员
私有
保护成员
私有
※受保护的成员不能被类外访问
※保护成员可以被派生类的成员函数引用
基类成员
在保护派生类中的访问属性
私有成员
不可访问
公有成员
保护
保护成员
保护
★
(1)派生类构造函数必须负责该派生类所有基类构造函数的调用;
(2)派生类构造函数的执行顺序是先执行基类的构造函数,再执行派生类本身构造函数。
(3)处于同一层次的各基类构造函数的执行顺序取决于定义派生类所指定的各基类顺序。
(4)与派生类构造函数中定义的成员初始化列表的各项顺序无关。
★派生类的构造函数和析构函数
1、构造函数
Ø构造函数不能够被继承
Ø派生类的构造函数必须通过调用基类的构造函数来初始化基类的子对象。
Ø在定义派生类构造函数时除了对自己的数据成员进行初始化外,还必须负责调用基类构造函数使基类的数据成员得以初始化。
★派生类构造函数的一般格式:
派生类构造函数名(参数列表):
基类构造函数(参数列表)
{
派生类中数据成员初始化;
};
★派生类构造函数的调用顺序如下
Ø基类的构造函数
Ø子对象的构造函数(如果有的话)
Ø派生类构造函数
★析构函数
Ø析构函数不能被继承。
Ø执行顺序为,先执行派生类的析构,在执行基类的析构函数。
★赋值兼容
✓派生的对象可以赋给基类对象;
deriverd;
baseb;
b=d;
✓派生类的对象可以初始化基类的引用;
deriverd;
base&br=d;
✓派生类的对象的地址可以赋给指向基类的指针。
derivedd;
base*pb=&d;
★虚基类
※如果在多条继承路径上有一个公共的基类,那么在这些路径中的某几条路径的汇总处,这个公共的基类就会产生多个实例。
※如果想使这个公共的基类只产生一个实例,则可以将这个基类说明为虚基类。
※这要求从base类派生新类时,使用关键字virtual将base类说明为虚基类。
★
第五章多态性和虚函数
★静态联编和动态联编
1、联编:
确定调用具体对象的过程称为关联。
一般地说,联编指把一个函数名和一个类对象捆绑起来,建立关联。
2、静态联编:
指在编译时进行的联编,称为静态联编。
Ø即在编译时就解决了程序中的操作调用与执行该操作代码之间的关系。
3、动态联编:
只有在程序执行时才能确定调用的函数,这种在程序运行时进行的联编,称为动态联编。
4、多态性:
是指不同类型