首页 > 编程 > C++ > 正文

C++构造函数和析构函数

2019-11-06 06:10:30
字体:
来源:转载
供稿:网友

构造函数和析构函数

一.构造函数

构造函数时一种特殊的函数,它主要用于为对象分配空间,进行初始化。

(注:构造函数没有this指针)

1.      构造函数的几个特点:

函数名与类名相同

参数任意,但是没有返回值,viod也不行

它是在实例化对象的时候自动的调用,而不需要用户调用

构造函数可以重载

构造函数可以写在类体内,也可以写在类体外

2.      构造函数的调用形式

(1)     类名对象名[(实参表)]

(2)     类名 *指针变量名 = new 类名[(实参表)]

下面是构造函数的使用方法

#include<iostream>usingnamespace std; classDate{PRivate:    int year;    int month;    int day;public:    Date(int y,int m,int d);    voidsetDate(int y,int m,int d);    voidshowDate();}; Date::Date(inty, intm, intd){    cout << "Constcuting..." << endl;    year = y;    month = m;    day = d;} voidDate::setDate(inty, intm, intd){    year = y;    month = m;    day = d;} inlinevoidDate::showDate(){    cout <<year << "." << month << "."<< day << endl;} int main(){    Datedate1(2017,3,2);    cout << "date1:" << endl;    date1.showDate();    date1.setDate(2017,3,3);    cout << "date2:" << endl;    date1.showDate();    return 0;}上面代码中主函数中的构造函数的使用是采用第一种方式,这里再提供第二种方式int main(){    Date *pdate;    pdate = newDate(2017,2,3);    cout << "Date1" << endl;    pdate->showDate();    pdate->setDate(2017,2,3);    cout << "Date2" << endl;    pdate->showDate();    return 0; }

这段代码中编译器开辟了一个存储空间,并且存放了一个Date类,但是这个对象没有名字,成为无名对象,但是该对象有地址,这个地址存放在pdate中,我们通过指针可以找到他,访问时用new动态建立的对象一般是不用对象名的,而是通过指针进行访问。如果不需要时,可以通过delete进行释放。

二. 成员初始化列表

  C++还提供了另一种初始化成员的方法——用成员初始化列表来实现对数据成员的初始化,这种方法不在函数体内用赋值语句,而是在函数首部实现的。

例如在构造函数的定义中可以使用如下的方式:

Date::Date(inty, intm, intd) :year(y),month(m), day(d){    cout << "Constcuting..." << endl;}

带有成员列表的构造函数一般的形式如下:

类名 ::构造函数名([参数表]):[(成员初始化列表)]

{}

成员初始化列表的一般形式为:

数据成员名1(初始值1),数据成员名2(初始值2),…

成员初始化列表有什么用途呢,一般对于const修饰的数据成员,或者是引用类型的数据成员。

注意:使用成员初始化列表初始化的时候,它的初始化的顺序是按照在类中声明的顺序进行初始化的,而不是按照成员初始化列表中的顺序进行初始化。

三. 带默认参数的构造函数(带缺省参数)

   在构造函数中,有一些成员值是不变的,这时我们可以使用带默认参数的构造函数

   意思就是我们在定义构造函数时,可以在形参的部分队参数进行赋初值。

四. 析构函数

析构函数也是一种特殊的成员函数,它通常用于撤销对象时的一些清理任务

析构函数的特点如下:

析构函数和构造函数名字相同,但是它的前面必须加一个波浪号(~)

析构函数没有参数,也没有返回值,而且不能重载。

析构函数自动被调用

例子:

同样是上文的Date类,我们可以在定义类的时候定义析构函数,如

class Date

{

  …

  ~Date();

}

 

Date::~Date()

{

 cout<<”destruting …”<<endl;

}

 

在以下情况下,当对象的声明周期结束时,析构函数会被自动调用

(1). 如果定义了一个全局对象,则在程序流离开作用域(如main()函数结束或者调用exit()函数)时,调用该全局对象的析构函数。

(2). 如果一个对象被定义在一个函数体内,则当这个函数调用结束时,该函数应该释放,析构函数自动被调用

(3). 若一个对象是使用new运算符进行动态创建的,在使用delete运算符释放它时,delete会调用析构函数。

五. 默认的构造函数和默认的析构函数

   1.默认的构造函数(系统自带的构造函数,全参数构造函数,无参的构造函数都可以叫默认的构造函数)

   一般写程序时会定义构造函数,但是如果没有定义构造函数,系统会自动生成一个构造函数,这就是默认的构造函数。上面的程序中,如果没有定义构造函数,而直接使用Date date1;这时系统会为Date类生成下面形式的构造函数:

Date ::Date()

{}

并且使用这个默认的构造函数对date1进行初始化,但是这个构造函数没有任何参数,它只能开辟一个存储空间,而不能给对象中的数据成员赋值,这时的初始值是个随机数,程序运行的时候可能会造成错误。

补充说明:对没有定义构造函数的类,其公有数据成员可以用初始值列表进行初始化。

      只要一个类定义了一个构造函数,系统将不再给它提供默认的构造函数

3.      默认的析构函数

每个类都有一个析构函数,如果一个类没有定义析构函数,那么编译系统会自动的生成一个析构函数。

注意:在C++调用构造函数的时候注意不能出现这种形式例如有一个类Date,这个时候实例化一个对象Date date();这里不是调用了这个构造函数,而是一个函数的声明,是错误的,如果不给这个对象传递参数就不要写后面的括号,传递参数的时候写括号,后面在加入参数。

六.拷贝构造函数

     拷贝构造函数的形参是本类对象的引用,拷贝构造函数的作用是在建立一个新得对象时,使用一个已经存在的对象去初始化这个新对象。形如:Point p2(p1);

     拷贝构造函数的几个特点:

1.      因为拷贝构造函数也是构造函数,所以它的函数名必须与类名相同,而且也没有返回值

2.      拷贝构造函数只有一个参数,而且是同类对象的引用

3.      每个类都有一个拷贝构造函数,可以自己定义用于初始化新的对象,如果没有定义系统会自动的定义,用于复制与数据成员值相同的对象。

拷贝构造函数的使用:

1.      自定义拷贝构造函数

自定义拷贝构造函数的一般形式如下:

类名::类名(const 类名 &对象名)

 

自定义拷贝构造函数的调用:

代入法:类名 对象2(对象1);

赋值发:类名 对象2 = 对象1;

2.      默认的拷贝构造函数

如果用户没有定义自定义的拷贝构造函数,然后又使用了拷贝构造函数,系统会调用默认的拷贝构造函数,例如Rectangle p2(p1);此时会把p1中各个域的值均复制给p2

3.      调用拷贝构造函数的三种情况

(1)    当用类的一个对象去初始化类的另一个对象的时候

(2)    当函数的形参是类的对象,调用函数进行形参和实参结合时

(3)    当函数的返回值是对象,函数执行完成返回调用者时

        下面是具体的程序,还有注释部分,注释部分就是上面各种的讲解

 

   #include<iostream>usingnamespace std; classRectangle{private:    int_length;    int _width;public:    Rectangle(int len =10, int wid =10); //构造函数    Rectangle(constRectangle&p);  //拷贝构造函数    void disp();}; Rectangle::Rectangle(intlen, intwid)  //构造函数{    _length = len;    _width = wid;    cout << "usingnormal constructor" << endl;} Rectangle::Rectangle(constRectangle &p)  //拷贝构造函数{    _length = 2 * p._length;    _width = 2 * p._width;    cout << "usingcopy constructor" << endl;} voidRectangle::disp(){    cout <<_length << " " << _width << endl;} void fun1(Rectanglep){    p.disp();} Rectangle fun2(){    Rectanglep4(10,30);     //这里调用了普通的构造函数    returnp4;             //这里返回的一个Rectangle的对象,我们会使用p2 = fun2();去接受他的返回值,所以这里相当于给p2用了一个拷贝构造函数} int main(){    Rectangle p1(30,40);  //定义了p1,调用构造函数    p1.disp();    Rectanglep2(p1);   //调用拷贝构造函数把p1里面的值全部复制给p2(情况1)    p2.disp();    Rectangle p3 =p1;  //调用拷贝构造函数,把p1的值复制给p3(情况1)    p3.disp();    fun1(p1);   //这个时候传入的是p1的一份引用,然后会调用Rectangle的拷贝构造函数                //这里应该思考的一个问题就是,为什么调用的是Rectangle的拷贝构造函数,这里我的一个猜测是,直接传一个对象的时候,就像传数组名一样了,发生了一个转换之类的                //可能就把对象转换成对象的一个引用了吧    p1.disp();    p2 =fun2();   //函数的返回值是对象,属于第三种情况调用拷贝构造函数    p2.disp();    system("pause");    return 0;}

 

六. 浅拷贝和深拷贝

 所谓浅拷贝,就是如果没有自定义拷贝构造函数而直接使用的话,就只是单纯的进行值得赋值,但是当类的数据成员有指针类型的时候,并且在使用指针的时候,给这个指针动态的开辟了一个新的循存储空间的话,当我实例化一个对象a之后,a里面的一个数据成员p动态的开辟了一个存储空间,然后我有实例化b,而且是通过调用未定义的拷贝构造函数,这样就直接把a里面的内容给了b,b里面的一个指针也指向刚刚a里面的内存空间,这时候就出现了一种情况就是当我调用析构函数清理a 的内存空间的时候,因为b中的指针也指向了那片空间,所以此时b中的那个指针就没有意义了,这就造成了错误。所以建议再使用指针的相关操作的时候,尽量去自定义拷贝构造函数。

 

  


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表

图片精选