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

构造函数和析构函数

一.构造函数

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

(注:构造函数没有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中的那个指针就没有意义了,这就造成了错误。所以建议再使用指针的相关操作的时候,尽量去自定义拷贝构造函数。

 

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值