• c++ 吕凤翥 第五章 类对象一


    一   类的声明和实现

     1. class tdate   //声明部分

      {

        public:

          void setdate(int y,int m,int d);

          int isleapyear();

          void print();

        private:

          int year,month,day;

      }

     void tdate::setdate(int y,int m,int d)//外联函数

      {

        year=y;

        month=m;

        day=d;

      }

     int tdate::isleapyear()

      {

        return(year%4==0&&y ear%100!=0)||(year%400==0);

      }

     void tdate::print()

      {

        cout<<year<<"."<<month<<"."<<day<<endl;

      }

      ::为作用域运算符    标识某个成员函数是属于哪个类的

      

    声明方式二

      class tdate

      {

        public:

          void setdate(int y,int m,int d)  //内联函数   适合函数体比较小

          {

          year=y;

          month=m;

          day=d;

          }

        int isleapyear(){....}  

        private:

          int year,month,day;

      }//内联函数的声明方法    将成员函数的实现 都写在类体内  ;如果函数的实现定义在类体外,则需要在函数头前面加上该函数所属的类的标识,使用作用域运算符 ::

    2:  注意事项

      1.类体中不允许对所定义的数据成员进行初始化     需要使用构造函数

      2.类中的数据成员的类型可以是任意的,其它类的对象可以作为该类的成员,但是自身类的对象不可以,但是自身类的指针或者引用可以。

        另一个类的对象作为这个类的成员时,如果另一个类的定义在后,则需要提前说明,这种说明成为引用性说明。

      class N;

      class M

      {

        public:  ...

        private:

          N n;//n是N类的对象       N类在前面说明

      }

      class N

      {

        public:

          void f(M m);  //m是M类的对象    M类已在前面说明

          ...

      };

      M类中使用了N类的对象n,而N类的定义又在后面,所以,对N类要提前说明

      3.先声明public  公有成员,后声明私有private 私有成员   一般按照数据成员的类型大小   由小到大来说明   

      4.将类定义的说明部分或整个实现部分放到一个头文件中 ;也可以将声明放在一个头文件,实现放在另一个文件中

     

    二.对象的定义

      类名  对象名

      tdate t1,t2,t3,* pdate,date[31],&rdate=t1;  //其中多个对象用逗号隔开    pdate为指向tdate对象的指针,date为一个对象数组, rdate 为一个对象的引用。

      对象成员的表示方法

      对象名.成员名

      对象的指针名->成员名 

      对象名.成员名(参数表)

      对象的指针名->成员名(参数表)

       (* 指针).成员名

      同一个类所创建的对象的数据结构是相同的,类中的成员函数是共享的。

    三.对象的初始化

      1.构造函数和析构函数

      都是特殊的成员函数:构造函数初始化正在创建的对象;析构函数的功能是用来释放一个对象

      class tdate//类声明

      {

        public:

          tdate(int y,int m,int d);  //构造函数  非默认

          ~tdate();      //析构函数   非默认

        void print();      //普通的成员函数

        private:        //数据成员变量

          int year,month,day;

      }

      tdate::tdate(int y,int m,int d)  //构造函数的实现

      {

        year=y;

        month=m;

        day=d;

        cout<<"constructor called. ";

      }

      tdate::~tdate()        //析构函数的实现

      {

        cout<<"destructor called. ";

      }

      void tdate::print()        //普通成员函数的实现

      {

        cout<<year<<"."<<month<<"."<<day<<endl;

      }

      构造函数特点:

        a  构造函数可以写在类体内(内联),也可以写在类体外  (外联)

        b  构造函数为特殊的成员函数  名称同类名,不指定类型,有隐含的返回值,此值由系统内部使用。该函数可以没有参数,也可以有多个参数

        c  构造函数可以重载,根据参数个数不同

        d  程序不直接调用构造函数,创建对象时,系统自动调用构造函数

      析构函数特点:

        a  同构造函数

        b  类名前加~    用来与构造函数加以区别  不指定数据类型,无参数

        c  只能有一个析构函数     这一点不同于构造函数

        d  通常系统自动调用  下面的两种情况

            1.函数体内定义的对象   当函数结束时,系统将调用析构函数释放此对象

            2.一个对象使用new运算符被动态创建时,则使用delete运算符(需要手动)释放,delete将自动调用析构函数

      源代码如下:

        #include <iostream.h>

        #include "tdate.h"

        void main()

        {

          tdate today(1998,4,9),tomorrow(1998,4,10);

          cout<<"today is";

          today.print();

          cout<<"tomorrow is";

          tomorrow.print();

        } 

        执行结果如下:

        constructor called.

        constructor called.

        today is 1998.4.9

        tomorrow is 1998.4.10

        destructor called. 

        destructor called.

    2.默认构造函数和默认析构函数

      默认构造函数: 无参的构造函数  称为默认构造函数

          a  系统自动提供的       程序员未定义      则系统自动生成

          b  程序员定义的  

             c  系统默认构造函数 创建对象,外部类对象和静态类对象的所有数据成员默认值,自动类对象的所有数据成员为无意义值。

      默认的析构函数:系统提供  的一个空函数

          程序员定义的析构函数可以不是一个空函数,可以给出适当的函数体。

    3.拷贝/复制构造函数 

      用一个已知对象来创造一个新的对象,而新创建的对象与已知对象的数据成员的值可以相同,也可以不同。

      仅有一个参数,就是已知对象的引用

      类名::复制构造函数名(类名 & 引用名)

      {

        函数体

      }

       如果程序员未定义,则系统提供默认的复制构造函数   该类的公有成员

      案例

      class tpoint

      {

        public:

          tpoint(int x,int y){X=x;Y=y;}

          tpoint(tpoint & p);

        private:

          int X,Y;

      }

      tpoint::tpoint(tpoint & p)

      {

        X=p.x;

        Y=p.y;

        cout<<"adsfalsdf. ";

      }

     调用:

       #include <iostream.h>

       #include "tpoint.h"

       void main()

       {

        tpoint p1(5,7);

        tpoint p2(p1);

        cout<<"asdfasdf"<<endl;

       }

    需要调用复制构造函数来由一个对象初始化另一个对象

     a  明确表示一个对象初始化另一个对象

        tpoint p2(p1);

     b  当对象作为函数实参传递给函数形参时

        p=f(N);

     c  当一个自动存储类对象作为函数返回值时

        return R;

      

      案例代码:

      ...

      tpoint f(tpoint q);  //prototype

      void main()

      {

        tpoint m(20,35),p(0,0);   //构造函数初始化

        tpoint n(m);//复制构造函数

        p=f(n);//n对象作为参数传递给函数f

        cout<<"adsf"<<"adsfasdf"<<endl;

      }

      tpoint  f(tpoint q)

      {

        ...

        tpoint r(x,y);//调用构造函数初始化对象

        return r;//返回一个tpoint类的对象

      }

       代码中标红的位置是复制构造函数被调用的位置。

      1.tpoint n(m)         n对象被复制构造函数初始化

      2.f函数   实参n传递给形参q时     会调用复制构造函数

      3.f函数定义的最后    return r     函数返回的对象由于是局部变量  类型为自动存储类变量  会复制到一个无名的此类的对象,然后再返回,给原函数,所以此处会调用复制构造函数

      析构函数     根据构造函数来分析   共调用6次析构函数

      1.退出f函数时,调用2次析构函数,用来释放对象r和q  一个是形参对象  另一个是返回的r对象

      2.主函数中,将f函数的值赋值给对象p后,无名对象(被r赋值的对象)被析构,退出主函数时,调用3此析构函数  分别释放n p和m

    4.成员函数的特性

      a  内联函数和外联函数

        内联为定义在类体内的成员函数       执行时不转移ip执行,而是在调用内联函数处,用内联函数来替换   以节省开销,提高速度 ,类似于宏

        外联为说明在类体内,但实现在类体外的成员函数

      b  外联变内联    在外联函数前加上inline

      案例:

        class A

        {

          public:

            A(int x,int y){X=x;Y=y;}      //内联函数

            int a(){return X;}          //内联函数

            int b(){return Y;}        //内联函数

            int c();

            int d();

          private:

            int X,Y;

        };

        inline int A::c()          //内联函数

        {

          return a()+b();

        }

        inline int A::d()          //内联函数

        {

          return c();

        }

        #include <iostream.h>

        void main()

        {

          A m(3,5);      //构造函数调用

          int i=m.d();    //对象的成员函数d()再调用c()  再调用a和b

          cout<<"d() return :"<<i<<endl;

        }

       

    重载性

       一般的成员函数,特殊的构造函数可重载,但是析构函数是不能重载的   注意重载函数中的参数个数不同

      案例:

        class m

        {

          public:

            m(int x,int y){X=x;Y=y;}      //构造函数 1

            m(int x){X=x;Y=x*x;}        //构造函数2  重载

            

            int add(int x,int y);        //成员函数1

            int add(int x);          //成员函数2

            int add();            //成员函数3    重载

            int xout(){return X;}        

            int yout(){return Y;}

          private:

            int X,Y;

        };

        int m::add(int x,int y)

        {

          X=x;

          Y=y;

          return X+Y;

        }

        int m::add(int x)

        {

          X=Y=x;

          return X+Y;

        }

        ....

     4.3设置参数的默认值

       成员函数和构造函数都可以设置参数的默认值

      案例:

      class n

      {

        public:

          n(int a=3,int b=5,int c=7);

          int Aout(){return A;}

          int Bout(){return B;}

          int Cout(){return C;}

        private:

          int A,B,C;

      };

      n::n(int a,int b,int c)  //构造函数  三个参数

      {

        A=a;

        B=b;

        C=c;

      }

      #include <iostream.h>

      void main()

      {

        n x,y(9,11),z(13,15,17)

        cout<<"x="<<x.Aout()<<","<<x.Bout()<<","<<x.Cout()<<endl;

        cout<<"y="<<y.Aout()<<","<<y.Bout()<<","<<y.Cout()<<endl;

        cout<<"z="<<z.Aout()<<","<<z.Bout()<<","<<z.Cout()<<endl;

      }

      程序执行输出如下:

      X=3,5,7

      Y=9,11,7

      z=13,15,17

      未提供实参的使用原类体内的给出的默认值,给实参的使用实参值

    五 静态成员

      全局变量或对象和局部的静态变量或对象能解决数据共享问题

      案例1:

      #include <iostream.h>

      int g=5;        //全局

      void f1(),f2();      //函数原型

      void main()

      {

        g=10;      // 此时g是全局变量

        f1();       //函数调用 

        f2();        //函数调用

        cout<<g<<endl;    //输出变量g

      }

      void f1()

      {

        g=15;    //修改了全局变量

      }

      void f2()

      {

        g=20;    //修改了全局变量

      }

      输出结果为20,函数间共享变量g,   全局变量或对象在任何位置都可以被修改。  不安全

     1.静态成员包含静态数据成员和静态成员函数。它们都属于类,也属于类的所有对象。

     2.静态成员可以用对象引用,也可以用类来引用

     3.静态数据成员和静态成员函数在没有对象时就已经存在,并可以用类来引用。

     A  静态数据成员

        实现多个对象之间的数据共享,并使用静态数据成员还不会破坏隐藏的原则,保证安全性。

        静态数据成员是类的所有对象共享的成员,而不是某个对象的成员。

        只存储一处,供所有对象公用。可以由共享它的任一个对象修改更新。

        1.静态数据成员在定义或说明时前面加上关键字static

        private:

          int a,b,c;

          static int s;

        其中a,b和c是非静态数据成员,而s是静态数据成员

        2.静态数据成员的初始化与一般数据成员不同

        在类体外进行

        数据类型  类名::静态数据成员名=初值

          a  初始化在类体外进行,前面不加static,以免与一般静态变量或对象相混淆

          b  作用域运算符来标明它所属的类,静态数据成员属于类不属于某个对象

        3.静态数据成员被存放在静态存储区

         静态数据成员是被放在静态存储区的,必须进行初始化。

         案例:

        class nclass

        {

          ...

          private:

            static int a;      //静态成员变量  声明

          ...

        };

        int nclass::a=5;        //必须初始化

          ...

      4.引用静态数据成员的格式

        类名::静态成员名

       案例:

       class myclass

      {

        public:

          myclass(int a,int b,int c);    //构造函数

          ..

        private:

          int A,B,C;

          static int Sum;    //静态成员变量声明

      };

      int myclass::sum=0;      //初始化

       myclass::myclass(int a,int b,int c)  //构造函数的实现

      {

        A=a;

        B=b;

        C=c;

        sum+=A+B+C;

      }

       

      void main()

      {

        myclass m(3,7,10),n(14,9,11);

        cout<<m.A<<m.B<<m.C<<endl;

        cout<<n.A<<n.B<<n.C<<endl;

        cout<<m.sum<<endl;

        cout<<n.sum<<endl;

      }

      执行结果为 

        3,7,10

        14,9,11

        54

        54

     将m初始化时  三个数的和赋值给了sum,值为20,之后n初始化又为34   sum的总值为54;可见静态数据成员是多个对象共享的。

     B  静态成员函数

      同静态成员变量一样,函数也是属于类,而不是具体的对象,所以使用时可以用类名来引用静态成员函数。

        静态成员函数实现的函数体中可以直接引用类中说明的静态成员,而不可以直接引用类中说明的非静态成员

        静态成员函数中要引用非静态成员时,可通过对象来引用

        

    #include <iostream>
    #include <math.h>

    using namespace std;

    class point
    {
    public:
    point(double _x,double _y)
    {
    x=_x;y=_y;
    }
    void getxy();
    friend double distance(point &a,point &b); //友元函数的声明
    private:
    double x,y;
    };
    void point::getxy()
    {
    cout<<"("<<x<<","<<y<<")"<<endl;
    }
    double distance(point &a,point &b) //友元函数的实现  注意参数和声明的方式
    {
    double dx=a.y-b.x;
    double dy=a.y-b.y;
    return sqrt(dx*dx+dy*dy);
    }

    int main()
    {
    point p1(3.0,4.0),p2(6.0,8.0);
    p1.getxy();
    p2.getxy();
    double d=distance(p1,p2);
    cout<<"distance is"<<d<<endl;
    }

     执行结果为:

      (3.0,4.0)       (6.0,8.0)     distance is 5

     案例2:

    #include <iostream>
    #include <math.h>

    using namespace std;       //命名空间

    class time      //类 声明
    {
    public:
    time(int _h,int _m)    //内联函数
    {
    h=_h;m=_m;
    }
    friend void time12(time t);  //友元
    friend void time24(time t);
    private:
    int h,m;
    };

    void time12(time t)  //友元实现
    {
    if(t.h>12)
    {
    t.h-=12;
    cout<<t.h<<":"<<t.m<<"PM"<<endl;
    }
    else
    cout<<t.h<<":"<<t.m<<"AM"<<endl;
    }
    void time24(time t)  //友元实现
    {
    cout<<t.h<<":"<<t.m<<endl;
    }

    int main()    //主函数
    {
    time t1(20,30),t2(10,45);      //调用构造  初始化对象
    time12(t1);    //友元调用
    time24(t1);
    time12(t2);
    time24(t2);

    return 0;
    }

    执行结果:

      8:30 PM

      20:30

      10:45AM  

      10:45

    如果形参改为引用  则函数内部的实现 修改的是实参对象的值

      8:30 PM

      8:30

      10:45AM  

      10:45

      

    友元类

      一个类作为另一个类的友元,这个类的所有成员函数都是另一个类的友元函数,

       class x

      {

        friend class y;

        public:...

        private:

          int a;

          ...

        }

      class y

      {

        public:

          void display();    

        private:

        ...

      }

      y类作为x类的友元类     y中的成员函数display 是x类的友元函数  可以访问x的私有成员变量

      案例:

      

    #include <iostream>
    #include <math.h>

    using namespace std;

    class X
    {
      friend class Y;    //声明友元类
      public:
      void set(int i)
      {
        x=i;
      }
    void display()
      {
        cout<<"x="<<x<<",";
        cout<<"y="<<y<<endl;
      }
    private:
      int x;      //成员变量
      static int y;    //静态成员变量
    };


    class Y                   //类声明
    {
      public:
        Y(int i,int j);    //构造函数
        void display();
      private:
        X a;      //私有成员变量为另一个类的对象
    };


    int X::y=1;          //静态成员变量的初始化


    Y::Y(int i,int j)    //Y类中的Y构造函数

    {
      a.x=i;     //x类对象a的x成员变量设置为i       此时x成员变量为非静态成员
      X::y=j;      //X类中的私有变量设置为j 因为此为构造函数,也是成员函数   即构造函数也是X类的友元函数,可以访问私有的成员变量X::y
    }
    void Y::display()  //Y类中的display函数的实现
    {
      cout<<"X="<<a.x<<",";        //       访问X类对象a的x成员变量    对象名调用普通成员变量
      cout<<"y="<<X::y<<endl;    //  访问X类y成员变量      类名调用静态成员变量   共享
    }

    int main()
    {
      X b;      //b对象
      b.set(5);    //构造初始化     
      b.display();   
      Y c(6,9);    //Y类的c对象 
      c.display();    
      b.display();
      return 0;
    }

       x=5;//b.display()

       y=1;

       x=6;//c.display()

       y=9;

       x=5;//b.display()

       y=9;

       

    注意:友元的关系不可逆     y类是x类的友元类,不能说x类是y类的友元类

    5.7类的作用域

       类的作用域  简称类域,类的定义中由一对花括号括起来的部分。

       类的成员 位于类域中。

       类域中的变量不能使用auto   register  和 extern等修饰符,只能使用static修饰符;函数也不能用extern修饰符

       类域中的静态成员变量和静态成员函数都具有外部的连接属性

       类域小于文件域     大于函数域

      类A的某个成员m在下列情况下具有类A的作用域

      a  m出现在a的某个成员函数中,且成员函数未定义同名标识符

      b  a.m  表达式

      c  pa->m   表达式     pa为指向a对象的指针

      d  A::m

    5.8局部类和嵌套类

      函数体内定义的类称为局部类     局部类中只能使用它的外围作用域中的对象和函数进行联系,    

      局部类中不能说明静态成员函数,所有成员函数必须定义在类体内。   不常用

      int a;

      void fun()    //函数

      {

        static int s;

        class A   //类

        {

          public:

            void init(int i){s=i;}

        };

        A m;

        m.init(10);    //A类的对象m与函数fun联系

      }

      嵌套类

        一个类中定义的类称为嵌套类    定义嵌套类的类称为外围类。

        主要目的是为了隐藏类名,减少全局的标识符。提高类的抽象能力,建立类间的主从关系

        class A    //外围类

        {

          public:

            class B    //嵌套类

            {

              public:

                void bf(){...};    //成员函数

              private:

            };

            void f();

          private:

            int a;

        }

        说明:

        a  B被隐藏在A中       B只能在A中使用,或者使用作用域运算符  A::B

        b  B的访问权限与f()函数是相同的都是public

        c  bf()函数只能在B中定义    不能在B之外定义

        d  B类中的成员不是A类的成员,反之一样;B的成员函数对A的成员变量没有访问权,反之一样。

        综上:嵌套类可以看作普通的非嵌套类来处理。    嵌套类仅仅是语法上的嵌入   不影响功能   可以如下写法:

        class A

        {

          ...

        }

        class B

        {

          ...

        }

    5.9对象的生存期

      不同的存储对象生存期不同

      对象从被创建开始到被释放为止的存在时间

      a  局部对象:对象被构造函数初始化创建时开始    到函数或者函数体结束   调用析构函数  释放该对象        函数或者程序块内

      b  静态对象:程序第一次执行所定义的静态对象时,到程序结束,对象被释放                文件中

      c  全局对象:程序开始时,创建该对象      程序结束时释放对象                     定义在文件中,包含在该文件的整个程序

       案例

      

    #include <iostream>
    #include <string.h>
    using namespace std;
    class A //类声明 实现
    {
    public:
    A(char * st);
    ~A();
    private:
    char string[50];
    };
    A::A(char * st)
    {
    strcpy(string,st);
    cout<<"constructor called for"<<string<<endl;
    }
    A::~A()
    {
    cout<<"Destructor called for"<<string<<endl;
    }

    void fun()
    {
    A FunObject("FunObject"); //局部对象
    static A staticObject("staticobject"); //静态对象
    cout<<"in fun()."<<endl;
    }
    A globalobject("globalobject"); //全局对象
    int main()
    {
    A mainobject("mainobject"); //局部对象
    cout<<"in main(),before called fun ";
    fun(); //函数调用
    cout<<"in main(),after called fun ";
    return 0;
    }

    练习:

    一:

    1.抽象类  模拟

    2.class A

      {

        public:

        private:

        protected:

      }

    3.4.

    5.     ::   作用域运算符     标明变量的归属

    选择:

    1.a     2.c    3.d    4.d  5.d    6.a    7.c    8.a    9.d    10.

     二 

    claas  成员缺省访问权限为私有

    ::   运算符用来限定成员函数所属的类

    析构函数可以不为空 但是只能有一个

    构造函数可以重载   析构不可重载

    说明和定义对象时,前面加上类名即可,不用加上class关键字

    三程序输出

    1.default  constructor   called

    constructor called

    a=0   b=0

    a=4,b=8

    2. 

    a=7,b=9

     #include <iostream>

    using namespace std;
    class B
    {
    public:
    B();
    B(int i,int j);
    void printb();
    private:
    int a,b;
    };

    class A
    {
    public:
    A();
    A(int i,int j);
    void printa();
    private:
    B c;
    };

     A::A(int i,int j):c(i,j){};//¶ÔÏóAµÄ¹¹Ô캯Êý

    void A::printa()
    {
    c.printb();
    }

    B::B(int i,int j)
    {
    a=i;
    b=j;
    }

    void B::printb()
    {
    cout<<"a="<<a<<";b="<<b<<endl;
    }

    int main()
    {
    A m(7,9);//设置断点      单步进入可以查看程序执行语句的顺序    此句仅调用A的构造函数  设置B对象c的a b值

    m.printa();    //此句输出a=7,  b=9     调用B的printb来输出
    return 0;
    }

     三   所有对象共享此静态变量     类中的静态成员函数可以直接以类名来调用       静态成员变量直接用静态成员函数来调用

    104

    1035     789.504

    //5.6 编程要求
    #include <iostream>
    #include <math.h>

    using namespace std;

    class sum
    {
    friend void youyuan(sum &a);
    public:
    sum(); //default
    sum(int _x,int _y);
    sum(int _x, int _y,int _z);
    void print(int i=1);
    static void jingtai();
    private:
    int x;
    int y;
    int z;
    static int m;

    };
    int sum::m=1;
    sum::sum()
    {
    x=y=0;
    cout<<"default constructor!"<<" ";
    cout<<"x:"<<x<<";y:"<<y<<" ";
    }
    sum::sum(int _x,int _y)
    {
    x=_x;y=_y;
    cout<<"constructor #1!"<<" ";
    cout<<"x:"<<x<<";y:"<<y<<" ";
    }
    sum::sum(int _x,int _y,int _z)
    {
    x=_x;y=_y;z=_z;
    cout<<"constructor #2!"<<" ";
    cout<<"x="<<x<<";y="<<y<<";z="<<z<<" ";
    }
    void sum::print(int i)
    {
    if(i==1) cout<<"默认参数:"<<i<<" ";
    else cout<<"非默认参数:"<<i<<" ";

    }

    void youyuan(sum &a)
    {
    cout<<"member is"<<a.x<<":"<<a.y<<" ";
    }

    void sum::jingtai()
    {
    cout<<"访问静态成员变量:"<<m<<"::fuck"<<" ";
    }
    int main()
    {
    sum A; //构造重载
    sum B(3,5);
    sum C(3,5,8);
    B.print(5);//成员函数非默认
    A.print();//默认构造函数
    C.jingtai();
    youyuan(A);
    youyuan(B);
    return 0;
    }

         

      

  • 相关阅读:
    python操作redis
    python正则表达式-案例
    hive序列化和反序列化serde
    python配置文件
    Java写入的常用技巧
    Java从string数组创建临时文件
    Java官方操纵byte数组的方式
    python实例方法、静态方法和类方法
    ast.literal_eval(转)
    impala学习笔记
  • 原文地址:https://www.cnblogs.com/dongguolei/p/9765895.html
Copyright © 2020-2023  润新知