文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>类与对象(二)

类与对象(二)

时间:2010-11-16  来源:osullishuai80

(一)构造函数
(1)当定义一个类对象时,编译程序需要为对象分配存储空间,进行必要的初始化,这部分工作随着类的不同而不同.在C++中,由构造函数来完成这些工作.即构造函数用于为对象分配地址空间,完成对类的初始化.
(2)构造函数可以由用户提供,也可以由系统自动生成.
(3)构造函数的特点:
   1.构造函数名必须与类名相同,否则将被当作一般的成员函数来处理.
   2.定义对象时被系统自动调用
   3.可以有任意类型的参数(参数也可以是类),但不能有返回值.即不能说明其返回类型的,即使是void型也不行.当然,构造函数也可以不带参数.
   4.构造函数是共有的,但其它时间无法被调用.
   5.每一个类都必须有一个构造函数.如果用户没有给类定义构造函数,则编译系统自动的生成一个缺省的构造函数.形如:  类名::类名(){}
   6.构造函数不能像其它成员函数那样被显式的调用,它是在定义对象的同时调用的.
   例程1:
   #include <iostream.h>
   #include <math.h>
   class complex
  {
     private:
        double real;
        double imag;
     public:
        complex() //构造函数,且无参数
       { 
           cout<<"In Constructor!"<<endl;
       }
       double abscomplex()
       { 
           double t;
           t=real*real+imag*imag;
           return sqrt(t);
       }
   };
    void main()
   {
      complex A;    //定义类的对象A时调用构造函数complex
      cout<<" abs of complex A="<<A.abscomplex()<<endl; 
   }
   执行结果:
      In Constructor!
      abs of complex A=1.30899e+062
 Attention!!!
   由于用户已经定义了类的构造函数,因此当用户定义一对象A时必须调用该构造函数.但构造函数无参数,因此写成上述形式.
   例程2:
   #include <iostream.h>
   #include <string.h>
   class myclass1
   {
     public:
        char name[10];
        int no;
        myclass1(char *s,int n)
        {
           cout<<"in myclass1"<<endl;
           strcpy(name,s);
           no=n;
        }
   }m("eer",54);
   class myclass2
   {
      public:
         char name[10];
         int no;
   };
   void main()
  {
      myclass1 a("chen",25);
      cout<<"after a"<<endl;
      cout<<a.name<<' '<<a.no<<endl;
      myclass2 b={"chang", 20};
      cout<<b.name<<' '<<b.no<<endl;
  }
  执行结果:
     in myclass1
     in myclass1
     after a
     chen 25
     chang 20
 Attention!!!
 1.用户已经定义了类的构造函数,因此当用户定义一对象A时必须调用该构造函数.而构造函数需要两个参数,因此调用构造函数时必须传递两个参数.
 2.在C++中,每调用一次构造函数,都会执行一次构造函数.因此,系统会打印出两次"in myclass1".一次是对象m产生,另一次由对象a产生.
 3.构造函数由于其特殊性,它只有在定义对象的同时调用,其它时刻不能再调用.比如:
      myclass1 a;
      a("chen",25);
   这种构造函数的调用是错误的.
  例程3:
    #include <iostream.h>
    #include <math.h>
    class complex
    {
      private:
         double real;
         double imag;
      public:
         complex(double r=0.0,double i=0.0); //有缺省值的构造函数
         double abscomplex();
    };
    complex::complex(double r,double i)
    { 
       real=r;
       imag=i; 
    }
    double complex::abscomplex()
    { 
       double t;
       t=real*real+imag*imag;
       return sqrt(t);
    }
    void main()
    { 
       complex s1;       //不传递参数,全部用缺省值
       complex s2(1);    //只传递一个参数
       complex s3(2,4);   //传递两个参数
       cout<<"mode of s1 is:"<<s1.abscomplex()<<endl;
       cout<<"mode of s2 is:"<<s2.abscomplex()<<endl;
       cout<<"mode of s3 is:"<<s3.abscomplex()<<endl;
    }
Attention!!!
   对于带参数的构造函数,在定义对象时必须给构造函数传递参数,否则构造函数将不被执行.在实际应用中,有些构造函数的参数值通常是不变的,只有在特殊情况下才改变它的值,这时可以将其定义为带缺省参数的构造函数.
 
(二)重载构造函数
   与一般的成员函数一样,C++允许重载构造函数,以适应不同的场合.这些构造函数之间以它们所带参数的个数或类型的不同加以区分.
   例程:
   //构造函数重载
   #include <iostream.h>
   #include <stdlib.h>
   class timer
  {
      int seconds;
      public:
        timer()                 //无参构造函数
        {
           seconds=0;             
        } 
        timer(char *t)           //含有一个数字串参数的构造函数
        { 
           seconds=atoi(t);
        }
        timer(int t)             //含一个整型参数的构造函数
        { 
           seconds=t;
        }   
        timer(int min,int sec)     //含两个整型参数的构造函数
        { 
           seconds=min*60+sec;
        }
        int gettime()
        { 
           return seconds;
        }
   };
   void main()
  { 
      timer a;
      cout<<"seconds1="<<a.gettime()<<endl;
      timer b(10);
      cout<<"seconds2="<<b.gettime()<<endl;
      timer c("20");
      cout<<"seconds3="<<c.gettime()<<endl;
      timer d(1,10);
      cout<<"seconds4="<<d.gettime()<<endl;
  }
  执行结果:
     0
     10
     20
     70
Attention!!!
   该例程中有4个构造重载函数,它们以所带参数的个数或类型的不同加以区分.但编写程序时注意别引起二义性.比如:
 1.若将构造函数 timer(int min,int sec) 改成:
     timer(int min = 0,int sec = 0)
   该构造函数有两个缺省参数.这样,定义对象 timer a 时,本来应该会执行第1个构造函数timer(),但现在第4个构造函数带有缺省值,因此对象a就不直到应该执行哪个构造函数,因此编译无法通过.
 2.若将构造函数 timer(int min,int sec) 改成:
     timer(int min,int sec = 0)
   该构造函数有一个缺省参数.这样,定义对象 timer b 时,本来应该会执行第3个构造函数timer(),但现在第4个构造函数最右边的参数是缺省值,因此对象b就不知道应该执行哪个构造函数,因此编译无法通过.
 3.注意,无论怎么修改第4个构造函数,都不能将其定义为:
    timer(int min = 0,int sec)
   
(三)析构函数
   析构函数也是一种特殊的成员函数,它与构造函数是动作相反的操作,通常用于执行一些清理任务,如释放分配给对象的内存空间.析构函数的特点:
  1.析构函数与构造函数名字相同,但它前面必须加一个波浪号(~).
  2.析构函数没有参数,也没有返回值,而且不能重载,因此一个类中只能有一个析构函数.
  3.当撤销对象时,编译系统会自动的调用析构函数.
  4.每个类必须有一个析构函数.若没有显式的为一个类定义析构函数,编译系统会自动的生成一个缺省的析构函数.如: ~类名(){}
  5.析构函数也属于某一个类,它可以由用户提供,也可以由系统自动生成.
  6.类对象构造和析构的执行顺序:先构造的后析构
  例程:
  //析构函数
   #include <iostream.h>
   #include <math.h>
   class complex
   {
     private:
        double real;
        double imag;
     public:
        complex(double r=0.0,double i=0.0)
        {
           cout<<"construction…"<<endl;
           real=r; imag=i;
        }
       ~complex()
        {
           cout<<"destruction…"<<endl;
        }
       double abscomplex()
       {
           double t;
           t=real*real+imag*imag;
           return sqrt(t);
        }
    };
    void main()
    { 
       complex A(1.1,2.2);
       cout<<"abs of complex A="<<A.abscomplex()<<endl;
    }
    执行结果:
       construction…
       abs of complex A=2.45967
       destruction…
   
(四)拷贝构造函数
   拷贝构造函数是一种特殊的构造函数.它是根据已存在的对象来创建一个新的对象.典型情况是将参数代表的对象逐域拷贝到新创建的对象中.在C++中,用户可以根据自己的需要定义拷贝构造函数,系统也可以为类产生一个缺省的拷贝构造函数.
  1.自定义拷贝构造函数格式
     classname(const classname &ob)    //ob是用于初始化另一个对象的对象的引用
     {
         //拷贝构造函数的函数体
     }
  2.若没有编写自己定义的拷贝构造函数,C++会自动的将一个已存在的对象复制到新对象,这种按成员逐一复制的过程是由缺省的拷贝构造函数自动完成的.
  3.与一般的构造函数一样,拷贝构造函数没有返回值.
   例程1:系统自动添加拷贝构造函数
     #include <iostream.h>
     #include <stdlib.h>
     class point
     {
        int x,y;
        public:
        point(int a,int b)   //构造函数
       {
           x=a;
           y=b;
        }
        void print()
       {
           cout<<x<<' '<<y<<endl;
       }
    };
    void main()
    { 
       point p1(30,40);   //定义对象p1
       point p2(p1);      //显式调用拷贝构造函数,通过对象p1创建对象p2
       p1.print();
       p2.print();
    }
   执行结果:
      30 40
      30 40
   例程2:自己定义的拷贝构造函数
   #include <iostream.h>
   #include <stdlib.h>
   class point
   {
     int x,y;
     public:
        point(int a,int b)    //构造函数
       {
           x=a;
           y=b;
        }
        point(const point &p) //拷贝构造函数
       {
           y=p.x;
           x=p.y;
        }
        void print()
       {
           cout<<x<<' '<<y<<endl;
       }
    };
   void main()
   { 
      point p1(30,40);   //定义对象p1
      point p2(p1);      //显式调用拷贝构造函数,通过对象p1创建对象p2
      p1.print();
      p2.print();
   }
   执行结果:
      30 40
      40 30 
   例程3:若类中有指针类型,按成员复制时可能会产生错误.该例程中,拷贝构造函数由编译器产生.
     #include <iostream.h>
     #include <string.h>
     class string_data
     {
         public:  char *str;
         public:
         string_data(char *s)
        {  
            cout<<"构造函数"<<' '<<s<<endl;
            str=new char[strlen(s)+1];
            if(str!=NULL)
               strcpy(str,s); 
        }
        ~string_data()
        {
            cout<<"析构函数"<<' '<<str<<endl;
            delete str;
        } 
     };
     void main()
    {
         string_data x("fadfas");   //定义对象x,并执行构造函数
         string_data y = x;          //定义对象y,并执行拷贝构造函数,或写成 y(x);
         cout<<x.str<<endl;
         cout<<y.str<<endl;
         cout<<x.str<<endl;
     }
 Attention!!!
(1)在C++中,若程序中用户已定义拷贝构造函数,则程序会执行自定义的拷贝构造函数.若程序中没有拷贝构造函数,则编译器自动补齐一个拷贝构造函数,但该函数是简单的直接赋值,在类成员有指针时容易出错.
(2)当程序执行语句 string_data x("fadfas") 后,对象x内的成员str所指向的空间内存放了字符串"fadfas".当程序执行语句 string_data y = x; 后,由于程序中没有拷贝构造函数,因此编译器会自动产生一个拷贝构造函数,该函数是简单的直接赋值,即将对象x内成员的值逐一的赋给对象y.因此,当该语句执行结束后,对象y内的成员str和对象x内的成员str指向了同一段地址空间,且该空间内存放了字符串"fadfas".
(3)当所有程序执行结束后,系统会执行析构函数,由于对象y比对象x后创建,因此系统会对对象y先析构,这样系统会delete掉对象y内的成员str指向的地址空间.当系统对对象x析构时,由于对象x的成员str所指向的空间已经delete了,因此会发生段错误.
(4)因此,当对象的成员中含有指针时,用户需要自己编写拷贝构造函数.因此,该例程可以加上下面代码即可:
   string_data(const string_data &p)    //自定义拷贝构造函数
   {
       str=new char[strlen(p.str)+1];
       strcpy(str,p.str);
   }
   当程序执行语句 string_data y = x; 时,系统会自动执行该拷贝构造函数.对象p是对象x的别名,两者都"指向"同一个对象.在该函数内,别名p又为成员str申请了一段地址空间,并将字符串"fadfas"复制到该新申请的地址空间内.此时对象y的成员str指向了新申请的地址空间.此时,对象x的成员str指向了原申请的地址空间,因此,当程序执行结束后,先析构对象y,再析构对象x,两者互不影响.
(5)对象之间赋值分析      1.第一条语句是定义一对象x,此时系统会调用构造函数,对对象x进行初始化.    2.第二条语句是定义一对象y,并将对象x所有成员的值赋给对象y,此时系统会调用拷贝构造函数.注意,第二条语句会产生两个动作,一是定义对象y,二是对对象y进行赋值,且这种赋值过程是通过"拷贝构造函数"实现的.具体如何赋值,由拷贝构造函数体决定.
相关阅读 更多 +
排行榜 更多 +
辰域智控app

辰域智控app

系统工具 下载
网医联盟app

网医联盟app

运动健身 下载
汇丰汇选App

汇丰汇选App

金融理财 下载