1、1,C+与C,2,第三章 基于对象的程序设计 (续),3,Student * ps1 = new Student (“lisi”, 3, 17); delete ps1;,/ps1-name并没有删除,3.6 析构函数,析构函数是构造函数的互补,为生命期即将结束的类对象自动释放资源。析构函数也是隐式自动调用的。,注意:析构函数的名字是在类名前加上波浪线 ,它不返回任何值也没有任何参数。尽管我们可以为一个类定义多个构造函数,但是我们只能提供一个析构函数,它将被应用在类的所有对象上。,class Student private:char * name;int age;int id;,4,class
2、 Student public:Student(const char * str, int i, int a=18);Student ();Student (const Student ,Student:Student() delete name; ,void main() Student st1(“lisi”, 1, 17 );Student *st2 = new Student(“zhangsan”, 2, 19);delete st2; ,5,class Student public:int getAge() return age; int setAge(int a) age = a;
3、private:char * name;int id;int age; ,const Student st1(“lisi”, 1, 18); int age = st1.getAge(); st1.setAge(20); ?,3.7 const成员函数,只有被声明为const 的成员函数才能被一个const 类对象调用,关键字const 被放在成员函数的参数表和函数体之间,对于在类体之外定义的const 成员函数,我们必须在它的定义和声明中同时指定关键字const,6,class Student public:int getAge() const return age; int setAge(
4、int a) age = a; int getId () const; private:char * name;int id;int age; ,int Student:getId() const return id; ,const Student st1(“lisi”, 1, 18); int age = st1.getAge(); int id = st1.getId(); ,int setAge(int a ) const age=a;,把一个修改类数据成员的函数声明为const 是非法的,7,为了允许修改一个const对象的数据成员,可把相应的 数据成员声明为mutable,class
5、 Student public:int getAge() const return age; int setAge(int a) const age = a; int getId () const; private:char * name;int id;mutable int age; ,8,统计创建的所有学生对象的个数?,学生人数不是任何一个具体学生的属性,具有全局性,若用一个全局变量记录学生人数,缺点: 容易被误修改,不安全。 容易和其它全局变量重名。,3.8 static成员,9,静态数据成员被当作类的全局对象,对于非静态数据成员,每个类对象都有自己的拷贝,而静态数据成员对每个类只有一个
6、拷贝,由该类的所有对象共享访问。,student.h class Student public:Student();Student(); private:char * name;int id;mutable int age;static int count; ,student.cpp #include “student.h” int Student:count = 0; Student:Student() count+; Student:Student() count-; ,friend int getCount();,int getCount() return Student:count;
7、,10,class Student public:static int getCount() return count; private:char * name;int id;mutable int age;static int count; ,int count = Student:getCount();,在静态成员函数中只能访问静态成员。,11,class Student public: private:char * name;int id;mutable int age;static int count;static const int class = 3306;,只有静态整型常量才能在
8、头文件里初始化,static const char depart20 = “CSE”;,12,3.9 操作符重载,int a, b, c; a=1; b=1; c=1;,a=b=c=1;,class String public: private:int _size;char * _string; ,String s1,s2,s3,s4; s1=s2=s3=s4=“hello”;,?,13,重载操作符使得类对象可以与操作符一起被使用,使得对类对象的操纵与内置类型的对象一样直观。,重载的操作符在类体中被声明,声明方式同普通成员函数一样, 只不过它的名字包含关键字operator,以及紧随其后的一个
9、预定义操作符:,3.9 操作符重载,14,class String public:String,String ,String,String s1,s2,s3; s1=s2=s3=“hello”;,15,class String public:String,class String friend String ,String ,16,注意: 对于内置类型的操作符,它的预定义意义不能被改变;/ 错误: 不能为int 重新定义内置的操作符int operator+( int, int );不能为内置数据类型定义其他的操作符; 预定义的操作符优先级不能被改变。,17,3.10 类的组合,问题:定义一个
10、类,描述一条线段,线段具有长度、两个端点等属性线段具有求长度等行为,端点可以用另外一个表示点的类表示,线段类Line包含两个点类Point的对象,18,类的组合描述的是一个类内嵌其他类的对象作为成员的情况,它们之间的关系是一种包含与被包含的关系。,class Point public:Point( int xx=0,int yy=0)X=xx;Y=yy;Point(Point ,class Line public:Line(Point xp1,Point xp2);Line(Line ,19,当创建类的对象时,如果这个类具有内嵌对象成员,那么各个内嵌对象将首先被自动创建。因此,在创建对象时,既
11、要对本类的基本类型数据成员进行初始化,又要对内嵌对象成员进行初始化。,Point:Point(Point ,Line:Line(Point xp1,Point xp2):p1(xp1),p2(xp2) double x=(double)(p1.GetX()-p2.GetX();double y=(double)(p1.GetY()-p2.GetY();len=sqrt(x*x+y*y); Line:Line(Line ,20,int main() Point myp1(1,1), myp2(4,5);Line line(myp1,myp2);Line line2(line);cout“The
12、length of the line is:”;coutline.GetLen()endl;cout“The length of the line2 is:”;coutline2.GetLen()endl; ,如何调用Line和Point的构造函数?调用顺序?,21,在创建一个组合类的对象时,不仅它自身的构造函数将被调用,而且还将调用其内嵌对象的构造函数,调用顺序为: 1、调用内嵌对象的构造函数,调用顺序按照内嵌对象在组合类的声明中出现的次序。 2、执行本类的构造函数。,析构函数的调用顺序与构造函数刚好相反。,22,Point:Point(Point ,Line:Line(Point xp1,
13、Point xp2):p1(xp1),p2(xp2) double x=(double)(p1.GetX()-p2.GetX();double y=(double)(p1.GetY()-p2.GetY();len=sqrt(x*x+y*y); Line:Line(Line ,int main() Point myp1(1,1), myp2(4,5);Line line(myp1,myp2);Line line2(line);cout“The length of the line is:”;coutline.GetLen()endl;cout“The length of the line2 is
14、:”;coutline2.GetLen()endl; ,调用次数和顺序?,Point:Point(Point &)先被调用4次; Line:Line(Point xp1,Point xp2)被调用一次,Point:Point(Point &)先被调用2次; Line:Line(Line &)被调用一次,23,class A public:void f(B b); ; class B public:void g(A a); ,两个类互相引用的情况,称为循环依赖,3.11 前向引用声明,24,前向引用声明:在引用未声明的类之前,将该类的名字告诉编译器,使编译器知道这是一个类名。,class B;
15、class A public:void f(B b); ; class B public:void g(A a); ,25,class B; class A public:void method()x-bMethod(); private:B *x;B y; ; class B public:void bMethod(); private:A *z; ,前向引用声明虽然可以解决一些问题,但它不是万能的。尽管使用了前向引用声明,但在提供一个完整的类声明之前,不能声明该类的对象,也不能在内联成员函数中使用该类的对象。,26,class MyString public: MyString( const char * = 0 ); MyString( const MyString ,作业(3月28日之前提交):修改上次作业MyString类的定义和实现,如下:,27,class MyString public:MyString( const char * str= 0 ); MyString( const MyString ,