二、缺省构造函数与 缺省析构函数 1、缺省构造函数 C++规定,每个类必须有构造函数,没有构造函数就不能创建对象。 如果在类的定义中,程序员没有设计任何构造函数,那么C++会自动提供一个默认的构造函数,该默认构造函数是一个没有参数的构造函数,它除了分配对象的实体空间外,其它什么也不做。此时,如果创建的对象是静态的或全局的,则对象中的数据初始化为0,否则将会是随机的。 缺省构造函数的形式是: 类名::类名() { } 只要在类的定义中提供了任意一个构造函数,那么C++就不再自动提供默认构造函数。 2、缺省析构函数 如果在类的定义中,程序员没有设计析构函数,C++也会自动提供一个默认的析构函数,该缺省析构函数的形式是: 类名::~类名() { } 只要在类的定义中提供了析构函数,那么C++就不再自动提供默认析构函数。 *********************************** 三、拷贝构造函数 拷贝构造函数是一种特殊的构造函数。它的参数是本类对象的(常)引用。 拷贝构造函数的功能是,将一个已经存在的对象的数据成员拷贝给正在创建的一个新的同类对象。 #include<iostream.h> class Point { public: Point(int x,int y){X=x;Y=y;} Point(Point &p); ~Point(){cout<< "DestructorCalled."<<endl;} intgetX(){return X;} intgetY(){return Y;} private: intX,Y; }; Point::Point(Point &p) { X=p.X; Y=p.Y; cout<<"Copy Constructor called."<<endl; } void main() { PointP1(5,7); PointP2(P1); cout<<P2.getX()<<endl; cout<<P2.getY()<<endl; } 5.4 成员函数的特性 一、内联函数与外联函数 类的成员函数分为内联函数和外联函数。内联函数是指函数的定义在类体内的情况;而外联函数是指函数的说明在类体内、函数的定义在类体外的情况。 内联函数的运行效率高。 二、重载性 除析构函数外,成员函数都可以重载,如:构造函数与拷贝构造函数。 三、设置参数的缺省值 例题: #include<iostream.h> class N { public: N(inta=3,int b=5,int c=7); intgetA(){return A;} intgetB(){return B;} intgetC(){return C;} private: intA,B,C; }; N::N(int a,int b,int c) { A=a; B=b; C=c; } void main() { NX,Y(9,11),Z(13,15,17); cout<<X.getA()<<","<<X.getB() <<","<<X.getC()<<endl; cout<<Y.getA()<<","<<Y.getB() <<","<<Y.getC()<<endl; cout<<Z.getA()<<","<<Z.getB() <<","<<Z.getC()<<endl; } 答案: 3,5,7 9,11,7 13,15,17 (获得全部隐含值:3,5,7) (第三个值从隐含获得) 5.5 静态成员 一、静态数据成员 类的普通数据成员在类的每一个对象中都拥有一个拷贝,即每个对象的同名数据成员分别存储了各自的数据,这也是每个对象拥有自身特征的保证。 静态数据成员是类的数据成员的一个特例。是该类的所有对象共同维护和使用,通过它,实现了同一个类的不同对象之间的数据共享。 使用静态数据成员可以节省内存,因为它是所有对象所公有的,因此对多个对象来说,静态数据成员只存储一处,供所有对象共用。当静态数据成员被更新时,所有的对象都可以获得这个更新后的值。 1、静态数据成员的说明 静态数据成员在定义或说明时前面加关键字:static。 2、静态数据成员的初始化 A、静态数据成员是静态存储的,必须对它进行初始化。 B、静态数据成员的初始化方法与一般数据成员不同,静态数据成员的初始化在类体外进行,此时,前面不加static,也不加访问控制标识符public、private等。 C、由于静态数据成员是类的成员,而不是对象的成员,因此,初始化静态数据成员时,使用作用域运算符来标识其所属的类。 D、静态数据成员的初始化采用以下格式进行: 数据类型类名::静态数据成员名=值; 3、静态数据成员的使用 类名::静态数据成员名 例题:(p153) #include <iostream.h> class Myclass { public: Myclass(int a,int b,int c); voidprintNumber(); voidprintSum(); private: intA,B,C; staticint Sum; }; int Myclass::Sum=0; Myclass::Myclass(int a,int b,int c) { A=a; B=b; C=c; Sum+=A+B+C; } void Myclass::printNumber() { cout<<"Number="<<A<<"," <<B<<","<<C<<endl; } void Myclass::printSum() { cout<<"Sum="<<Sum<<endl; } void main() { Myclass M(3,7,10); M.printNumber(); M.printSum(); Myclass N(14,9,11); N.printNumber(); N.printSum(); } 运行结果如下,请分析之。 Number=3,7,10 Sum=20 Number=14,9,11 Sum=54 Press any key continue 二、静态成员函数 与静态数据成员一样,静态成员函数也属于类的静态成员,而不是对象的成员,使用时,通过类名(而不是对象名)进行引用。 在静态成员的实现中可以直接引用类中说明的静态成员,和通过对象来引用类中的非静态成员。 与静态数据成员一样,静态成员函数也属于整个类,由该类的所有对象共同维护和共享。 例题: #include <iostream.h> class M { public: M(inta){ A=a; B+=a; } //构造函数,内联 staticvoid f1(M m); //静态成员函数 private: intA; staticint B; //静态数据成员 }; void M::f1(M m) { cout<<"A="<<m.A<<endl; //通过对象引用一般成员 cout<<"B="<<B<<endl; //直接引用静态成员 } int M::B=0; //静态成员初始化 void main() { M P(5),Q(10); M::f1(P); //通过类引用静态成员函数。 M::f1(Q); } 5.6 友元 一、友元函数 对于类的私有数据,只有该类的成员函数或经特殊说明的函数才可以引用它们。这个所谓的特殊说明的函数就是友元函数。 对于一个定义在类的外部的普通函数,当在某个类中用关键字FRIEND进行说明时,该函数就成为了这个类的友元函数。友元函数不是类的成员函数,但可以访问类中的私有成员。 友元函数可以提高程序的运行效率(直接访问私有数据),但也破坏了类的封装性和隐藏性。 例题: #include <iostream.h> #include <math.h> class Point { public: Point(double xx,double yy) { x=xx; y=yy; } void printXY(); friend double Distance (Point &a,Point &b); private: double x,y; }; void Point::printXY() { cout<<"("<<x<<","<<y<<")"<<endl; } double Distance(Point &a,Point&b) { double dx=a.x-b.x; double dy=a.y-b.y; return sqrt(dx*dx+dy*dy); } void main() { Point p1(3.0,4.0),p2(6.0,8.0); p1.printXY(); p2.printXY(); double d=Distance(p1,p2); cout<<"Distance is"<<d<<endl; } 二、友元类 当一个类作为另一个类的友元时,这个类的所有成员函数都将成为另一个类的友元函数,称之为友元类。 例句:friend class 类名; 5.7 类的作用域(自学) 5.8 局部类和嵌套类(自学) 5.9 对象的生存期(自学) P164:练习题1-16 作业题:一、二、三、四 要求:手写作业,作业题三可不抄题。 时间:争取10.31交。 关于类的总结 一、“变量”的发展体现了编程理念的飞跃 从简单变量、数组、结构、类,从面向过程的程序设计到面向对象的程序设计。 二、类与对象的概念是面向对象程序设计的基础。 一、什么是类 类是一种复杂的数据类型,它将各种不同类型的彼此相关的数据和与这些数据相关的操作(函数)封装在一起的集合体。 二、如何定义类类型 C++的类类型是程序员自己“创造”出来的。 1、类的一般定义格式如下: class类名 { Public: 数据成员或成员函数的说明 Private: 数据成员或成员函数的说明 Protected 数据成员或成员函数的说明 }; 各个成员函数的实现 2、定义类时应注意的事项 A、class是定义类类型的关键字,“类名”是标识符,通常用大写字母开始的字符串作为类名。 B、花括号内是类的说明部分,说明该类包含哪些数据成员和哪些成员函数。 C、类中的数据成员的类型可以是任意的,包含整型、浮点型、字符型、数组、指针和引用等,也可以是对象。 D、在类体中不允许对所定义的数据成员进行初始化,因为类的定义只是在创造一个类型而已,而不是在说明“变量”。 3、类成员的访问控制 A、从访问权限上来分,类的成员又分为:公有的(public)、私有的(private)和保护的(protected)三类。 B、公有成员用public来说明,公有部分往往是一些操作(成员函数),它是提供给用户的接口。这部分成员可以在程序中引用。 C、私有成员用private来说明,私有部分通常是一些数据成员,这些成员是用来描述该类中的对象的属性的,用户是无法访问它们的,只有成员函数或经特殊说明的函数才可以引用它们,它们是被用来隐藏的部分。 D、保护成员用protected来说明,在大多数情况下,类的保护成员具有私有成员的性质,但在派生类的成员函数而言,它具有公有成员的性质。 关键字public、private和protected被称为访问权限修饰符或访问控制修饰符,用它们来说明类成员的访问权限。它们在类体内出现的先后顺序无关,并且允许多次出现。 一般地,在类体内先说明公有成员,它们是用户所关心的,后说明私有成员,它们是用户不感兴趣的。在说明数据成员时,一般按数据成员的类型大小,由小至大说明,这样可提高时空利用率。 经常习惯地将类定义的说明部分或者整个定义部分(包含实现部分)放到一个头文件中。 4、类成员函数的实现 在类类型的定义中,“各个成员函数的实现”是类定义中的实现部分,这部分包含所有在类体内说明的函数的定义。 返回值的类型 类名::成员函数名(参数表) { 函数体 } 其中“::”叫作用域运算符。 如果一个成员函数的类体内定义了,实现部分将不出现。如果所有的成员函数都在类体内定义,则实现部分可以省略。 三、如何使用类:创建对象 1、类似于用基本数据类型来说明变量的方法,我们可以用自己创建的类类型来说明“变量”,此时的“变量”叫对象。 2、访问对象的公有成员: 当用自己创建的类类型来说明“对象”以后,程序中就可以用运算符“.”来引用其公有成员,引用的方式如下: 对象名.公有数据成员名 对象名.公有成员函数名(实参表) 对“对象”中数据成员的赋值和数据传递一般都是通过成员函数来进行的。 四、关于成员函数 (一)成员函数的定义可以放在类的说明部分进行,但函数体后的花括号后面不能有分号。 (二)在类的说明部分定义的函数体,默认为具有内联性质。这种内联性质只对简单的函数体有效。 (三)调用成员函数有三种方式: 1)对象名.成员函数名(参数表); 2)对象名—>成员函数名(参数表); 3)(﹡对象指针).成员函数名(参数表) (四)关于常成员函数 1)常成员函数的申明和定义上都要求:参数表后加const。 2)常成员函数不能修改对象值。 3)只对对象进行读操作的函数可以设置为常函数。类的设计者限少数人,使用者无数,常成员函数理念可避免不必要的错误。 (五)特殊成员函数 1、构造函数的特点: 1)构造函数是类的一个成员函数,但有其特殊性。 2)构造函数的函数名与类名相同。 3)构造函数不能有返回值。 4)构造函数的主要功能是完成对象的初始化工作。 5)构造函数可以重载。 在使用类名创建对象的时候,系统会自动调用构造函数,实现其“完成对象的初始化工作”的功能。 构造函数的说明: 1、C++规定,每个类必须有构造函数,没有构造函数就不能创建对象。 2、在类的定义中,若程序员没有设计任何构造函数,那么C++会自动提供一个默认的构造函数,该默认构造函数是一个没有参数的构造函数,它仅仅负责创建对象而不做任何赋值操作。此时,如果创建的对象是静态的或全局的,则对象中的数据初始化为0,否则将会是随机的。 3、只要在类的定义中提供了任意一个构造函数,那么C++就不再自动提供默认构造函数。 2、构造函数的重载 构造函数不但可以重载,还可以设置默认值,这样可以灵活地创建对象。 3、拷贝构造函数 拷贝构造函数也一种特殊的构造函数。它的参数是本类对象的(常)引用。 在用该类的一个对象去初始化另一个对象时,将参数对象的数据逐个拷贝到新创建的对象中。 4、析构函数 析构函数也是特殊的类成员函数,它没有返回类型,没有参数,也没有重载,不能随意调用,只有在类对象的生命期结束的时候,由系统自动调用。 1)析够函数是类的一个特殊成员函数。 2)析够函数的函数名取“~类名”。 3)析够函数不能有返回值,也不能有函数参数。 4)析够函数的主要功能是在对象消失时,执行如释放内存等清理工作。 5)在对象消失时,析够函数会被自动调用 二、缺省构造函数与 缺省析构函数 1、缺省构造函数 C++规定,每个类必须有构造函数,没有构造函数就不能创建对象。 如果在类的定义中,程序员没有设计任何构造函数,那么C++会自动提供一个默认的构造函数,该默认构造函数是一个没有参数的构造函数,它除了分配对象的实体空间外,其它什么也不做。此时,如果创建的对象是静态的或全局的,则对象中的数据初始化为0,否则将会是随机的。 缺省构造函数的形式是: 类名::类名() { } 只要在类的定义中提供了任意一个构造函数,那么C++就不再自动提供默认构造函数。 2、缺省析构函数 如果在类的定义中,程序员没有设计析构函数,C++也会自动提供一个默认的析构函数,该缺省析构函数的形式是: 类名::~类名() { } 只要在类的定义中提供了析构函数,那么C++就不再自动提供默认析构函数。 *********************************** 三、拷贝构造函数 拷贝构造函数是一种特殊的构造函数。它的参数是本类对象的(常)引用。 拷贝构造函数的功能是,将一个已经存在的对象的数据成员拷贝给正在创建的一个新的同类对象。 #include<iostream.h> class Point { public: Point(int x,int y){X=x;Y=y;} Point(Point &p); ~Point(){cout<< "DestructorCalled."<<endl;} intgetX(){return X;} intgetY(){return Y;} private: intX,Y; }; Point::Point(Point &p) { X=p.X; Y=p.Y; cout<<"Copy Constructor called."<<endl; } void main() { PointP1(5,7); PointP2(P1); cout<<P2.getX()<<endl; cout<<P2.getY()<<endl; } 5.4 成员函数的特性 一、内联函数与外联函数 类的成员函数分为内联函数和外联函数。内联函数是指函数的定义在类体内的情况;而外联函数是指函数的说明在类体内、函数的定义在类体外的情况。 内联函数的运行效率高。 二、重载性 除析构函数外,成员函数都可以重载,如:构造函数与拷贝构造函数。 三、设置参数的缺省值 例题: #include<iostream.h> class N { public: N(inta=3,int b=5,int c=7); intgetA(){return A;} intgetB(){return B;} intgetC(){return C;} private: intA,B,C; }; N::N(int a,int b,int c) { A=a; B=b; C=c; } void main() { NX,Y(9,11),Z(13,15,17); cout<<X.getA()<<","<<X.getB() <<","<<X.getC()<<endl; cout<<Y.getA()<<","<<Y.getB() <<","<<Y.getC()<<endl; cout<<Z.getA()<<","<<Z.getB() <<","<<Z.getC()<<endl; } 答案: 3,5,7 9,11,7 13,15,17 (获得全部隐含值:3,5,7) (第三个值从隐含获得) 5.5 静态成员 一、静态数据成员 类的普通数据成员在类的每一个对象中都拥有一个拷贝,即每个对象的同名数据成员分别存储了各自的数据,这也是每个对象拥有自身特征的保证。 静态数据成员是类的数据成员的一个特例。是该类的所有对象共同维护和使用,通过它,实现了同一个类的不同对象之间的数据共享。 使用静态数据成员可以节省内存,因为它是所有对象所公有的,因此对多个对象来说,静态数据成员只存储一处,供所有对象共用。当静态数据成员被更新时,所有的对象都可以获得这个更新后的值。 1、静态数据成员的说明 静态数据成员在定义或说明时前面加关键字:static。 2、静态数据成员的初始化 A、静态数据成员是静态存储的,必须对它进行初始化。 B、静态数据成员的初始化方法与一般数据成员不同,静态数据成员的初始化在类体外进行,此时,前面不加static,也不加访问控制标识符public、private等。 C、由于静态数据成员是类的成员,而不是对象的成员,因此,初始化静态数据成员时,使用作用域运算符来标识其所属的类。 D、静态数据成员的初始化采用以下格式进行: 数据类型类名::静态数据成员名=值; 3、静态数据成员的使用 类名::静态数据成员名 例题:(p153) #include <iostream.h> class Myclass { public: Myclass(int a,int b,int c); voidprintNumber(); voidprintSum(); private: intA,B,C; staticint Sum; }; int Myclass::Sum=0; Myclass::Myclass(int a,int b,int c) { A=a; B=b; C=c; Sum+=A+B+C; } void Myclass::printNumber() { cout<<"Number="<<A<<"," <<B<<","<<C<<endl; } void Myclass::printSum() { cout<<"Sum="<<Sum<<endl; } void main() { Myclass M(3,7,10); M.printNumber(); M.printSum(); Myclass N(14,9,11); N.printNumber(); N.printSum(); } 运行结果如下,请分析之。 Number=3,7,10 Sum=20 Number=14,9,11 Sum=54 Press any key continue 二、静态成员函数 与静态数据成员一样,静态成员函数也属于类的静态成员,而不是对象的成员,使用时,通过类名(而不是对象名)进行引用。 在静态成员的实现中可以直接引用类中说明的静态成员,和通过对象来引用类中的非静态成员。 与静态数据成员一样,静态成员函数也属于整个类,由该类的所有对象共同维护和共享。 例题: #include <iostream.h> class M { public: M(inta){ A=a; B+=a; } //构造函数,内联 staticvoid f1(M m); //静态成员函数 private: intA; staticint B; //静态数据成员 }; void M::f1(M m) { cout<<"A="<<m.A<<endl; //通过对象引用一般成员 cout<<"B="<<B<<endl; //直接引用静态成员 } int M::B=0; //静态成员初始化 void main() { M P(5),Q(10); M::f1(P); //通过类引用静态成员函数。 M::f1(Q); } 5.6 友元 一、友元函数 对于类的私有数据,只有该类的成员函数或经特殊说明的函数才可以引用它们。这个所谓的特殊说明的函数就是友元函数。 对于一个定义在类的外部的普通函数,当在某个类中用关键字FRIEND进行说明时,该函数就成为了这个类的友元函数。友元函数不是类的成员函数,但可以访问类中的私有成员。 友元函数可以提高程序的运行效率(直接访问私有数据),但也破坏了类的封装性和隐藏性。 例题: #include <iostream.h> #include <math.h> class Point { public: Point(double xx,double yy) { x=xx; y=yy; } void printXY(); friend double Distance (Point &a,Point &b); private: double x,y; }; void Point::printXY() { cout<<"("<<x<<","<<y<<")"<<endl; } double Distance(Point &a,Point&b) { double dx=a.x-b.x; double dy=a.y-b.y; return sqrt(dx*dx+dy*dy); } void main() { Point p1(3.0,4.0),p2(6.0,8.0); p1.printXY(); p2.printXY(); double d=Distance(p1,p2); cout<<"Distance is"<<d<<endl; } 二、友元类 当一个类作为另一个类的友元时,这个类的所有成员函数都将成为另一个类的友元函数,称之为友元类。 例句:friend class 类名; 5.7 类的作用域(自学) 5.8 局部类和嵌套类(自学) 5.9 对象的生存期(自学) P164:练习题1-16 作业题:一、二、三、四 要求:手写作业,作业题三可不抄题。 时间:争取10.31交。 关于类的总结 一、“变量”的发展体现了编程理念的飞跃 从简单变量、数组、结构、类,从面向过程的程序设计到面向对象的程序设计。 二、类与对象的概念是面向对象程序设计的基础。 一、什么是类 类是一种复杂的数据类型,它将各种不同类型的彼此相关的数据和与这些数据相关的操作(函数)封装在一起的集合体。 二、如何定义类类型 C++的类类型是程序员自己“创造”出来的。 1、类的一般定义格式如下: class类名 { Public: 数据成员或成员函数的说明 Private: 数据成员或成员函数的说明 Protected 数据成员或成员函数的说明 }; 各个成员函数的实现 2、定义类时应注意的事项 A、class是定义类类型的关键字,“类名”是标识符,通常用大写字母开始的字符串作为类名。 B、花括号内是类的说明部分,说明该类包含哪些数据成员和哪些成员函数。 C、类中的数据成员的类型可以是任意的,包含整型、浮点型、字符型、数组、指针和引用等,也可以是对象。 D、在类体中不允许对所定义的数据成员进行初始化,因为类的定义只是在创造一个类型而已,而不是在说明“变量”。 3、类成员的访问控制 A、从访问权限上来分,类的成员又分为:公有的(public)、私有的(private)和保护的(protected)三类。 B、公有成员用public来说明,公有部分往往是一些操作(成员函数),它是提供给用户的接口。这部分成员可以在程序中引用。 C、私有成员用private来说明,私有部分通常是一些数据成员,这些成员是用来描述该类中的对象的属性的,用户是无法访问它们的,只有成员函数或经特殊说明的函数才可以引用它们,它们是被用来隐藏的部分。 D、保护成员用protected来说明,在大多数情况下,类的保护成员具有私有成员的性质,但在派生类的成员函数而言,它具有公有成员的性质。 关键字public、private和protected被称为访问权限修饰符或访问控制修饰符,用它们来说明类成员的访问权限。它们在类体内出现的先后顺序无关,并且允许多次出现。 一般地,在类体内先说明公有成员,它们是用户所关心的,后说明私有成员,它们是用户不感兴趣的。在说明数据成员时,一般按数据成员的类型大小,由小至大说明,这样可提高时空利用率。 经常习惯地将类定义的说明部分或者整个定义部分(包含实现部分)放到一个头文件中。 4、类成员函数的实现 在类类型的定义中,“各个成员函数的实现”是类定义中的实现部分,这部分包含所有在类体内说明的函数的定义。 返回值的类型 类名::成员函数名(参数表) { 函数体 } 其中“::”叫作用域运算符。 如果一个成员函数的类体内定义了,实现部分将不出现。如果所有的成员函数都在类体内定义,则实现部分可以省略。 三、如何使用类:创建对象 1、类似于用基本数据类型来说明变量的方法,我们可以用自己创建的类类型来说明“变量”,此时的“变量”叫对象。 2、访问对象的公有成员: 当用自己创建的类类型来说明“对象”以后,程序中就可以用运算符“.”来引用其公有成员,引用的方式如下: 对象名.公有数据成员名 对象名.公有成员函数名(实参表) 对“对象”中数据成员的赋值和数据传递一般都是通过成员函数来进行的。 四、关于成员函数 (一)成员函数的定义可以放在类的说明部分进行,但函数体后的花括号后面不能有分号。 (二)在类的说明部分定义的函数体,默认为具有内联性质。这种内联性质只对简单的函数体有效。 (三)调用成员函数有三种方式: 1)对象名.成员函数名(参数表); 2)对象名—>成员函数名(参数表); 3)(﹡对象指针).成员函数名(参数表) (四)关于常成员函数 1)常成员函数的申明和定义上都要求:参数表后加const。 2)常成员函数不能修改对象值。 3)只对对象进行读操作的函数可以设置为常函数。类的设计者限少数人,使用者无数,常成员函数理念可避免不必要的错误。 (五)特殊成员函数 1、构造函数的特点: 1)构造函数是类的一个成员函数,但有其特殊性。 2)构造函数的函数名与类名相同。 3)构造函数不能有返回值。 4)构造函数的主要功能是完成对象的初始化工作。 5)构造函数可以重载。 在使用类名创建对象的时候,系统会自动调用构造函数,实现其“完成对象的初始化工作”的功能。 构造函数的说明: 1、C++规定,每个类必须有构造函数,没有构造函数就不能创建对象。 2、在类的定义中,若程序员没有设计任何构造函数,那么C++会自动提供一个默认的构造函数,该默认构造函数是一个没有参数的构造函数,它仅仅负责创建对象而不做任何赋值操作。此时,如果创建的对象是静态的或全局的,则对象中的数据初始化为0,否则将会是随机的。 3、只要在类的定义中提供了任意一个构造函数,那么C++就不再自动提供默认构造函数。 2、构造函数的重载 构造函数不但可以重载,还可以设置默认值,这样可以灵活地创建对象。 3、拷贝构造函数 拷贝构造函数也一种特殊的构造函数。它的参数是本类对象的(常)引用。 在用该类的一个对象去初始化另一个对象时,将参数对象的数据逐个拷贝到新创建的对象中。 4、析构函数 析构函数也是特殊的类成员函数,它没有返回类型,没有参数,也没有重载,不能随意调用,只有在类对象的生命期结束的时候,由系统自动调用。 1)析够函数是类的一个特殊成员函数。 2)析够函数的函数名取“~类名”。 3)析够函数不能有返回值,也不能有函数参数。 4)析够函数的主要功能是在对象消失时,执行如释放内存等清理工作。 5)在对象消失时,析够函数会被自动调用