250 likes | 422 Views
c.R c.setr( ) c.getr( ) c.longs( ) c.area( ). d.R d.setr( ) d.getr( ) d.longs( ) d.area( ). 随机数. 随机数. 函数代码. 函数代码. 函数代码. 函数代码. 函数代码. 函数代码. 函数代码. 函数代码. 圆类的对象 c. 圆类的对象 d. 对于: circle c, d ;编译系统为 2 个对象分配空间的情况如下:.
E N D
c.R c.setr( ) c.getr( ) c.longs( ) c.area( ) d.R d.setr( ) d.getr( ) d.longs( ) d.area( ) 随机数 随机数 函数代码 函数代码 函数代码 函数代码 函数代码 函数代码 函数代码 函数代码 圆类的对象c 圆类的对象d 对于: circle c, d ;编译系统为2个对象分配空间的情况如下: 可以看出,两个对象在内存中各自占用不同的存储空间,可以说,两个对象都有类中所有成员的拷贝。
类实际上就是一种用户定义的数据类型,每当生成类的对象时,系统为每个对象分配空间存放类中的所有成员,每个对象中都有类中所有成员的拷贝,然而,在实际应用中,常常希望程序中所有同一类的对象共享某一成员,通常指数据成员,比如同一学校中所以学生对象中的学校名称成员,以保证数据的一致性及简化操作。解决办法就是将需要共享的成员说明成类中的静态成员。类实际上就是一种用户定义的数据类型,每当生成类的对象时,系统为每个对象分配空间存放类中的所有成员,每个对象中都有类中所有成员的拷贝,然而,在实际应用中,常常希望程序中所有同一类的对象共享某一成员,通常指数据成员,比如同一学校中所以学生对象中的学校名称成员,以保证数据的一致性及简化操作。解决办法就是将需要共享的成员说明成类中的静态成员。
3.5 静态成员 • 静态数据成员 静态数据成员在内存中仅有一个拷贝,为类的所有对象共享。 静态数据成员的使用方法: (1)静态数据成员的定义与一般数据成员的定义方法一样,但前面要加上关键字static (2)静态成员由于为所有的对象共享,所以必须要初始化,而且一般不在构造函数中初始化。初始化格式为: 类型 类名::静态数据成员名=初始值 (3)引用静态数据成员的格式为: 类名::静态数据成员名
静态数据成员是指在类中说明的、用关键字 static 修饰的数据成员。例: class X { private: int d; static int s;//定义静态数据成员 public: … … }; int X :: s=5; //初始化静态数据成员 该类中静态数据成员是私有类型,类外不能访问
定义静态数据成员,该数据成员为公有成员 初始化静态数据成员 引用静态数据成员 class sample { private: int n; float m; public: static int s; … … … }; int sample::s=0; void main() { … … … cout<<"s="<<sample::s<<endl; … … … } 该类中静态数据成员是公有类型,类外可以访问
s n n sample( ) sample( ) 函数代码 函数代码 display( ) display( ) 函数代码 函数代码 b对象 a对象 0 2 5 2 5 class sample { private: int n; static int s; public: sample( int i ) { n=i; s=i; } void display( ) { cout<<“n=”<<n<<‘\t’; cout<<“s=”<<s<<endl; } }; int sample::s=0; 静态数据成员由于为所有的对象共享,所以我们也把静态数据成员称为类变量。非静态数据成员属于一个具体的对象,我们称为对象变量(实例变量) void main() { sample a(2), b(5); a.display( ); b.display( ); } 输出: n=2 s=5 n=5 s=5
定义静态数据成员,由于n是私有成员,不能在类外访问定义静态数据成员,由于n是私有成员,不能在类外访问 初始化静态数据成员 定义了一个对象数组s,数组元素是四个对象。 #include <iostream.h> class sample { private: static int n; public: sample() { n=n+1;} void display() { cout<<"n="<<n<<endl; } }; int sample::n=0; void main() { sample *p=new sample,s[4]; s[0].display(); s[1].display(); s[2].display(); s[3].display(); p->display(); } 由于 n 为所有的对象共享,不属于没个对象,所以每创建一次对象n值加1。 输出为: n=5 n=5 n=5 n=5 n=5
静态成员函数 静态函数成员在内存中仅有一个拷贝,为类的所有对象共享。 静态函数的使用方法: (1)和静态数据成员相同,静态成员函数是用关键字static 修饰的成员函数。 (2)静态函数成员只能访问静态数据成员,不能访问该类中的非静态数据成员。 (3)引用静态函数成员的方式和引用静态数据成员的方式一样,格式为: 类名::静态函数成员名(<参数列表>)
静态函数成员的定义,只能访问静态数据成员n,不能访问数据成员m静态函数成员的定义,只能访问静态数据成员n,不能访问数据成员m 静态函数成员的调用 class sample { private: static int n; float m; public: void sub( ) { …… } static void add( ) { …… } ……… }; void main( ) { … … sample::add( ); … … }
静态函数成员访问静态数据成员 静态函数的调用 #include <iostream.h> int s; class sample { private: static int n; public: sample(int i) { n=i; } static void add( ) { s=s+n; } }; int sample::n=0; void main() { sample a(2),b(5); sample::add( ); cout<<"s="<<s<<endl; } 输出:s=5 为了节省内存,所有对象的函数成员在内存中共享一个副本,所以静态函数成员只有逻辑上的意义,称为类方法。非静态函数成员逻辑上属于一个具体的对象,我们称为对象方法(实例方法)
#include <iostream.h> class sample { public: static int n; static void add( ) { n=n+1; cout<<"n="<<n<<endl; } }; int sample::n=0; void main( ) { sample::n=5; sample::add( ); } 输出: n=6 该程序说明静态数据成员和静态函数成员不依赖于对象的存在,但是非静态成员只有在对象存在的时候才有意义。所以我们把静态数据成员称为类变量,把静态函数成员称为类方法。 该程序没有创建对象
q对象首址 p对象首址 x x 3 5 demo( int ) demo( int ) 函数代码 函数代码 函数代码 函数代码 demo( ) demo( ) add( ) add( ) 函数代码 函数代码 dis( ) dis( ) 函数代码 函数代码 q对象 p对象 到底调用的哪一个对象的add()成员,编译器怎么知道的? 3.6 this指针 #include <iostream.h> class demo { private: int x; public: demo(int m) { x=m; } demo( ) { } void add(int n) { x=x+n; } void dis( ) { cout<<x<<endl; } }; void main() { demo p(5),q(3); p.add(4); p.dis( ); q.add(2); q.dis( ); }
每个函数都有一个指向该类对象的一个指针变量作为第一个形参,该指针变量是系统隐含的,我们称为this指针。每个函数都有一个指向该类对象的一个指针变量作为第一个形参,该指针变量是系统隐含的,我们称为this指针。 this-> this-> 在成员函数体里,我们可以访问this这个指针变量。 &p, &q, #include <iostream.h> class demo { private: int x; public: demo(int m) { x=m; } demo() {} void add( int n) { x= x + n; } void display() { cout<<x<<endl; } }; void main() { demo p(5),q(3); p.add( 4); p.display(); q.add( 2); q.display(); } demo *this, 每个对象在调用函数成员时,都把自己的地址作为第一个参数传递给函数成员,不过该参数是系统隐含的。
系统隐含的,不需要用户定义。 #include <iostream.h> class demo { private: int x; public: demo( demo *this, int m) { this->x = m; } demo() {} void add( demo *this, int n) { this->x = this-> x+n; } void display( demo *this) { cout<<this->x<<endl; } }; void main() { demo p( &p , 5),q( &q , 3); p.add( &p , 4); p.display( &p ); q.add( &q , 2); q.display( &q ); } 注意: 类的每一个成员函数都有一个隐 含定义的,指向该类对象的一个指针 变量,我们称为this指针。 每当一个对象调用成员函数时,this指针被初始化为调用该成员函数的对象的地址。
this指针在每一个成员函数中系统都隐含定义并初始化了,所以我们可以在每一个成员函数中都可以使用this指针this指针在每一个成员函数中系统都隐含定义并初始化了,所以我们可以在每一个成员函数中都可以使用this指针 #include <iostream.h> class demo { private: int x; public: demo(int m) { this->x = m; } demo( ) { } void add(int n) { this->x = this-> x+n; } void display( ) { cout<<this->x<<endl; } }; void main() { demo p(5),q(3); p.add(4); p.display( ); q.add(2); q.display( ); } 输出: 9 5 注意:静态函数成员由于为类中所有的对象共享,逻辑上为类所有,所以没有this指针。
对象s3调用add( )函数成员,所以this指针指向对象s3。 该语句相当于:s3.n=s1.n+s2.n 返回了对象本身即s3 #include <iostream.h> class sample { private: int n; public: sample() { } sample(int m) { n=m; } sample add( sample s1,sample s2 ) { this->n = s1.n+s2.n; return(*this); } void display() { cout<<"n="<<n<<endl; } }; void main() { sample s1(10),s2(5),s3; cout<<"s1::"; s1.display(); cout<<"s2::"; s2.display(); s3.add(s1,s2); cout<<"s3::"; s3.display(); } 输出: S1::n=10 S2::n=5 S3::n=15
静态数据成员,存放国籍 初始化有参对象的构造函数。 例:Person p1(“Li”,25,’f’) 初始化无参对象的构造函数。 例:Person p2; 用对象初始化对象的构造函数 例:Person p3( p1 ); 析构函数,撤销对象 对无参对象赋值的成员函数 例:p2.Register( “Hu”,23,’f’); 静态函数成员,修改国籍 静态函数成员,输出国籍 class Person { private: char Name[20]; int Age; char Sex; static char Country[20]; public: Person ( char *, int, char ); Person ( ); Person ( Person &obj ); ~Person( ); void Register( char *, int, char ); void GetName( char * ); int GetAge( ); char GetSex( ); static void ChangeCountry( char * ); static void GetCountry( char * ); } ; char Person::Country[20]=“China” ; person类的定义
3.6 对象的引用 引用就是别名。当建立引用时,必须用一个已经定义的变量或者对象的名字来定义它。在引用被定义后,引用就作为原变量或者原对象的别名来使用,对引用的改动就是对原目标的改动。 定义格式: 类名 &引用对象名= 已定义对象名; 例如: peason p1(“zhang” , 25 , ’f’); peason &p2 = p1; 为对象p1定义了一个引用类型p2,换句话说,为对象p1 起了一个别名p2,即p2和p1都是对同一个对象的引用。
对象a b.R b.circle(int) b.ciecle( ) b.serr( ) b.getr( ) a.R a.circle(int) a.circle( ) a.setr( ) a.getr( ) 函数代码 函数代码 函数代码 函数代码 … … … 对象b 5 10 void main() { circle a(5); circle &b = a; b.setr(10); cout<<a.getr( )<<endl; cout<<b.getr( )<<endl; } 输出: 10 10
函数原型 a对象 b对象 值传递 R 5 5 R circle(int) 函数代码 circle( ) 函数代码 getr( ) 函数代码 … … … void main() { void f (circle); circle a(5); cout<<a.getr( )<<endl; f(a); } void f (circle b) { cout<<b.getr( )<<endl; } 输出: 5 5 注意:当对象的数据成员比较多时,使用对象直接作为函数参数会在赋值的操作上浪费大量系统资源,造成效率不高。
a对象 R circle(int) 函数代码 circle( ) 函数代码 setr( ) 函数代码 getr( ) 函数代码 … … … void main() { void f (circle &); circle a(5); cout<<a.getr( )<<endl; f(a); } void f (circle &b) { cout<<b.getr( )<<endl; } b对象 15 5 b.setr(15); 输出: 5 5 输出: 5 15 注意:使用对象的引用类型作为函数参数,优点是没有值传递的过程,也不需要另外分配内存空间,只是一个起别名的过程,效率高。缺点是通过引用名可能对原目标改动。
a对象 R circle(int) 函数代码 circle( ) 函数代码 setr( ) 函数代码 getr( ) 函数代码 … … … void main() { void f ( circle &); circle a(5); cout<<a.getr( )<<endl; f(a); } void f ( circle &b) { b.setr(15); cout<<b.getr( )<<endl; } b对象 5 const const //编译错误 //编译错误 注意:利用关键词const修饰的对象是常对象,常对象只能引用原对象的值,不能去更新原对象的值。
p1是常对象,其值不能更改 p2是常对象,只能引用p1的值,不能更改p1的值,我们也称p2为常引用 b是常对象,只能引用a的值,不能更改a的值,我们也称b为常引用 常对象和常成员函数 • 常对象:利用关键词const修饰的对象是常对象,常对象只能引用原对象的值,不能更新原对象的值。 const peason p1(“zhang” , 25 , ’f’); peason p1(“zhang” , 25 , ’f’); const peason &p2 = p1; void f (const circle &b) { … … …} void main() { circle a(5); ……… f(a); }
a对象 R circle(int) 函数代码 circle( ) 函数代码 setr( ) 函数代码 getr( ) 函数代码 … … … 由于b是常对象,常对象不能调用普通函数成员 • 常成员函数:利用关键词const修饰的函数成员我们称为常成员函数。常对象只能访问常成员函数,不能访问一般函数成员。 b对象 void main() { void f (const circle &); circle a(5); cout<<a.getr( )<<endl; f(a); } void f (const circle &b) { cout<<b.getr( )<<endl; } 5
常对象b调用常成员函数getr( ) const 常对象b也可以调用常成员函数longs( )和area( )。 const #include <iostream.h> class circle { private: double R; public: circle(double r) { R=r; } circle( ) { } void setr(double r ) { R=r; } double getr( ) { return(R); } double longs( ) { return( 2*3.14*R); } double area( ) { return(3.14*R*R); } }; void main() { void f(const circle &); circle a(5); cout<<a.getr( )<<endl; f(a); } void f(const circle &b) { cout<<b.getr( )<<endl; } const