[C++]类和对象(三)

类和对象

缺省的构造函数

在定义类时,若没有定义类的构造函数,则编译器自动产生一个缺省的构造函数,其格式为:

className::className() {  }

缺省的构造函数并不对所产生对象的数据成员赋初值;即新产生对象的数据成员的值是不确定的。

class A{

float  x,y;

public:

A(){}//缺省的构造函数,编译器自动产生,可以不写

float Sum(void) {   return  x+y;   }

void Set(float a,float b) {    x=a; y=b;}

      void Print(void) { cout<<"x="<<x<<'\t'<<"y="<<y<<endl;   }

};

void main(void)

{ A a1,a2;//产生对象时,自动调用缺省的构造函数,不赋值

a1.Set (2.0,4.0);

cout<<"a1:  ";

   a1.Print ();

cout<<"a1.sum="<<a1.Sum ()<<endl;

a2.Print();//打印随机值

}

关于缺省的构造函数,说明以下几点:

1、在定义类时,只要显式定义了一个类的构造函数,则编译器就不产生缺省的构造函数

2、所有的对象在定义时,必须调用构造函数

不存在没有构造函数的对象!

class A{

float x,y;

public:

   A(float a,float b){x=a;y=b;}    显式定义了构造函数,不产生缺省的构造函数

   void Print(void){cout<<x<<'\t'<<y<<endl;}

};

void main(void)

{A  a1;           error,定义时,没有构造函数可供调用

A  a2(3.0,30.0);

}

3、在类中,若定义了没有参数的构造函数,或各参数均有缺省值的构造函数也称为缺省的构造函数,缺省的构造函数只能有一个。

4、产生对象时,系统必定要调用构造函数。所以任一对象的构造函数必须唯一。

class A{

float x,y;

public:

    A(float a=10,float b=20){x=a;y=b;}          两个函数均为缺省的构造函数

    A(){  }

   void Print(void){cout<<x<<'\t'<<y<<endl;}

};

void main(void)

{A  a1;            两个构造函数均可供调用,构造函数不唯一

A  a2(3.0,30.0);

}

构造函数与new运算符

·以使用new运算符来动态地建立对象。建立时要自动调用构造函数,以便完成初始化对象的数据成员。最后返回这个动态对象的起始地址。

·用new运算符产生的动态对象,在不再使用这种对象时,必须用delete运算符来释放对象所占用的存储空间。

·用new建立类的对象时,可以使用参数初始化动态空间。

class  A{

float   x,y;

public:

A(float a, float b){x=a;y=b;}

A(){x=0;  y=0;}

void  Print(void){  cout<<x<<'\t'<<y<<endl;  }

};

void main(void)

{   A   *pa1,*pa2;

    pa1=new  A(3.0, 5.0);//用new动态开辟对象空间,初始化

    pa2=new A;//用new动态开辟空间,调用构造函数初始化

    pa1->Print();

    pa2->Print();

    delete  pa1;  //用delete释放空间

    delete  pa2; //用delete释放空间

}

析构函数

析构函数的作用与构造函数正好相反,是在对象的生命期结束时,释放系统为对象所分配的空间,即要撤消一个对象。

析构函数也是类的成员函数,定义析构函数的格式为:

ClassName::~ClassName( )

{

   ......

//         函数体;

          }

析构 函数的特点如下:

1、析构函数是成员函数,函数体可写在类体内,也可写在类体外。

2、析构函数是一个特殊的成员函数,函数名必须与类名相同,并在其前面加上字符“~”,以便和构造函数名相区别。

3、析构函数不能带有任何参数,不能有返回值,不指定函数类型。

4、一个类中,只能定义一个析构函数,析构函数不允许重载。

5、析构函数是在撤消对象时由系统自动调用的。

在程序的执行过程中,当遇到某一对象的生存期结束时,系统自动调用析构函数,然后再收回为对象分配的存储空间。

class A{

float x,y;

public:

   A(float a,float b)

 {x=a;y=b;cout<<"调用非缺省的构造函数\n";}

   A() {  x=0;  y=0;  cout<<"调用缺省的构造函数\n" ;}

   ~A() {cout<<"调用析构函数\n";}

    void Print(void) {    cout<<x<<'\t'<<y<<endl;}

};

void main(void)

{A  a1;

A  a2(3.0,30.0);

cout<<"退出主函数\n";

}

调用缺省的构造函数

调用非缺省的构造函数

退出主函数

调用析构函数

调用析构函数

   在程序的执行过程中,对象如果用new运算符开辟了空间,则在类中应该定义一个析构函数,并在析构函数中使用delete删除由new分配的内存空间。因为在撤消对象时,系统自动收回为对象所分配的存储空间,而不能自动收回由new分配的动态存储空间。

用new运算符为对象分配动态存储空间时,调用了构造函数,用delete删除这个空间时,调用了析构函数。当使用运算符delete删除一个由new动态产生的对象时,它首先调用该对象的析构函数,然后再释放这个对象占用的内存空间。

class  A{

float   x,y;

public:

  A(float a, float b){x=a;y=b;cout<<"调用了构造函数\n";}

  void  Print(void){  cout<<x<<'\t'<<y<<endl;  }

  ~A(){  cout<<"调用了析构函数\n";   }

};

void main(void)                                 进入main()函数

{  cout<<"进入main()函数\n";                    调用了构造函数

    A   *pa1;                              

    pa1=new  A(3.0, 5.0);//调用构造函数          3     5

    pa1->Print(); 

    delete  pa1;  //调用析构函数               调用了析构函数

    cout<<"退出main()函数\n";                  退出main()函数

}

 

不同存储类型的对象调用构造函数及析构函数

 1、对于全局定义的对象(在函数外定义的对象),在程序开始执行时,调用构造函数;到程序结束时,调用析构函数。

2、对于局部定义的对象(在函数内定义的对象),当程序执行到定义对象的地方时,调用构造函数;在退出对象的作用域时,调用析构函数。

3、用static定义的局部对象,在首次到达对象的定义时调用构造函数;到程序结束时,调用析构函数

 4、对于用new运算符动态生成的对象,在产生对象时调用构造函数,只有使用delete运算符来释放对象时,才调用析构函数。若不使用delete来撤消动态生成的对象,程序结束时,对象仍存在,并占用相应的存储空间,即系统不能自动地调用析构函数来撤消动态生成的对象。

 

动态构造及析构对象数组

用new运算符来动态生成对象数组时,自动调用构造函数,而用delete运算符来释放p1所指向的对象数组占用的存储空间时,在指针变量的前面必须加上[ ], 才能将数组元素所占用的空间全部释放。否则,只释放第0个元素所占用的空间。

pa1=new  A[3];

.....

delete  [ ]pa1;

缺省的析构函数

若在类的定义中没有显式地定义析构函数时,则编译器自动地产生一个缺省的析构函数,其格式为:ClassName::~ClassName() { };

任何对象都必须有构造函数和析构函数,但在撤消对象时,要释放对象的数据成员用new运算符分配的动态空间时,必须显式地定义析构函数。

实现类型转换的构造函数

同类型的对象可以相互赋值,相当于类中的数据成员相互赋值;

如果直接将数据赋给对象,所赋入的数据需要强制类型转换,这种转换需要调用构造函数。

 

完成拷贝功能的构造函数

可以在定义一个对象的时候用另一个对象为其初始化,即构造函数的参数是另一个对象的引用,这种构造函数常为完成拷贝功能的构造函数。

完成拷贝功能的构造函数的一般格式为:

ClassName::ClassName(ClassName  &<变量名>)

{......

//   函数体完成对应数据成员的赋值

}

如果没有定义完成拷贝功能的构造函数,编译器自动生成一个隐含的完成拷贝功能的构造函数,依次完成类中对应数据成员的拷贝。

A::A(A &a)

{

       x=a.x;

       y=a.y;

 }

由编译器为每个类产生的这种隐含的完成拷贝功能的构造函数,依次完成类中对应数据成员的拷贝。

但是,当类中的数据成员中使用new运算符,动态地申请存储空间进行赋初值时,必须在类中显式地定义一个完成拷贝功能的构造函数,以便正确实现数据成员的复制。

 

 

构造函数与对象成员

 

对类A的对象初始化的同时还要对其成员数据类B的对象进行初始化,所以,类A的构造函数中要调用类B的构造函数。

ClassName::ClassName(args):c1(args1),..,cn(agrsn)

{

...... //对其它成员的初始化

}

初始化对象成员的参数(实参)可以是表达式。,也可以仅对部分对象成员进行初始化。

 

对对象成员的构造函数的调用顺序取决于这些对象成员在类中说明的顺序,与它们在成员初始化列表中的顺序无关。

当建立类ClassName的对象时,先调用各个对象成员的构造函数,初始化相应的对象成员,然后才执行类ClassName的构造函数,初始化类ClassName中的其它成员。析构函数的调用顺序与构造函数正好相反。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值