一文读懂C++构造函数&析构函数

一、对象的初始化

在建立一个对象时,常常需要作某些初始化的工作。错误的示例:如下给一个对象t1赋值。

#include<iostream>
using namespace std;
class Time
{
private:
int hour;
int minute;
int sec;
};
int main( )
{
Time t1={14,56,30};//错误的初始化方式,编译会报错
return 0;
}

(一)使用构造函数初始化对象

构造函数(constructor)是一种特殊的成员函数。 

构造函数不需要用户来调用它,而是在建立对象时自动执行。 

构造函数的名字必须与类名同名,而不能由用户任意命名。

构造函数不具有任何类型,不返回任何值

构造函数的功能是由用户定义的,用户根据初始化的要求设计函数体和函数参数。

构造函数内的对于数据成员的“赋值(初始化)”,不用带数据成员的类型。

1、类内定义构造函数

#include <iostream>
using namespace std;
class Time                 //时间类
{
public:
    Time( ) //类内定义构造函数
    {
        hour=0;            //注意:不用带参数类型
        minute=0;          //注意:不用带参数类型
        sec=0;             //注意:不用带参数类型
    }
    void set_time( );
    void show_time( );
private:
    int hour;
    int minute;
    int sec;
};

void Time::set_time( )
{
    cin>>hour;
    cin>>minute;
    cin>>sec;
}
void Time::show_time( )
{
    cout<<hour<<":"<<minute<<":"<<sec<<endl;
}
int main( )
{
    Time t1;
    t1.show_time( );
    Time t2;
    t2.set_time( );
    t2.show_time( );
    return 0;
}

2、类外定义构造函数

class Time
{
public:
    Time();
    void set_time();
    void show_time();
private:
    int hour;
    int minute;
    int sec;
};

Time::Time()
{
    hour=0;
    minute=0;
    sec=0;
}

(二)无参&带参构造函数(构造函数重载)

注:无参构造函数,只是不需要输入参数,但其初始化值仍在构造函数内定义。如本例中,无参构造函数Time();将类内的数据成员初始化值为0。

class Time
{
public:
    Time();            //无参数的构造函数
    /*构造函数的重载(函数名相同,参数个数或类型不同)*/
    Time(int,int,int); //有参数的构造函数,形参可以省略
    void show_time();
private:
    int hour;
    int minute;
    int sec;
};

Time::Time()            //无参数的构造函数
{
    hour=0;
    minute=0;
    sec=0;
}

Time::Time(int h,int m,int s) //有参数的构造函数
{
    hour=h;
    minute=m;
    sec=s;
}


void Time::show_time( )
{
    cout<<hour<<":"<<minute<<":"<<sec<<endl;
}

int main()
{
    Time t1;             //注意:此处会调用,无参数的构造函数
    t1.show_time();
    Time t2(15,39,59);   //注意:此处会调用,有参数的构造函数
    t2.show_time();
    return 0;
}

(三)"参数初始化表"构造函数

优点:不在函数体内对数据成员初始化,而是在函数首部实现。当需要初始化的数据成员较多时更显其优越性。

1、类内使用参数初始化列表

构造函数名(无参/带参):数据成员(形参){};

class Time
{
public:
    Time():hour(0),minute(0),second(0){}; //无参构造函数           
    Time(int h,int m,int s):hour(h),minute(m),second(s){}; //带参构造函数
    void show_time();
private:
    int hour;
    int minute;
    int sec;
};

2、类外使用参数初始化列表

类名::构造函数名(无参/带参):数据成员(形参){};

Time::Time(int h,int m,int s):hour(h), minute(m), sec(s){};

注意:

在构造函数的函数体中不仅可以对数据成员赋初值,而且可以包含其他语句;但一般不提倡在构造函数中加入与初始化无关的内容,以保持程序的清晰。

在类的成员函数中慎用输入输出,一旦如此,所有对象都会包含同样的输入输出。

(四)对象数据成员的赋值vs初始化的比较

初始化仅仅是在对象的声明时的操作,而赋值是在函数执行时的操作。

1、赋值操作

#include <iostream>
using namespace std;

class Student
{
private:
    int num;
    char name[20];
    char sex;
public:
    void set_data(int n, char *p,char s)//用于对数据成员赋值的成员函数
    {
        num=n;
        strcpy(name,p);
        sex=s;
    }
    void display( )
    {
        cout<<"num: "<<num<<endl;
        cout<<"name: " <<name<<endl;
        cout<<"sex: " <<sex<<endl<<endl;
    }
};

int main()
{
    Student stud1,stud2;
    stud1.set_data(1,"He",'f');    //对stud1进行赋值操作
    stud2.set_data(2,"She",'m');   //对stud2进行赋值操作
    stud1.display();
    stud2.display();
    return 0;
}

2、初始化操作

#include <iostream>
#include<string.h>
using namespace std;

class Student
{
private:
    int num;
    char name[20];
    char sex;
public:
    Student(int n,char *m,char s)
    {
        num = n;
        strcpy(name,m);
        sex = s;
    }
    void display( )
    {
        cout<<"num: "<<num<<endl;
        cout<<"name: " <<name<<endl;
        cout<<"sex: " <<sex<<endl<<endl;
    }
};

int main()
{
    
    Student stud1(1,"He",'f');    //对stud1进行初始化
    Student stud2(2,"She",'m');   //对stud2进行初始化
    stud1.display();
    stud2.display();
    return 0;
}

(五)私有数据成员包含字符数组和字符串的初始化比较

1、私有数据成员包含字符数组

例1.1 类外带参构造函数进行初始化
#include <iostream>
#include<string.h>


using namespace std;

class Student
{
private:
    int num;
    char name[20];
    char sex;
public:
     Student(int n,char *m,char s);
    void display( )
    {
        cout<<"num: "<<num<<endl;
        cout<<"name: " <<name<<endl;
        cout<<"sex: " <<sex<<endl<<endl;
    }
};

Student::Student(int n,char *m,char s)
{
	num = n;
	strcpy(name,m);
	sex = s;
}
   
int main()
{
    Student stud1(1,"AAA",'f');
	Student stud2(2,"BBB",'m');
    
    stud1.display();
    stud2.display();
    return 0;
}
例1.2 类外带参构造函数使用参数列表初始化(特殊)

注意如下用法

Student::Student(int n,char *m,char s):num(n),sex(s){strcpy(name,m);};

// Example program
#include <iostream>
#include<string.h>


using namespace std;

class Student
{
private:
    int num;
    char name[20];
    char sex;
public:
     Student(int n,char *m,char s);
    void display( )
    {
        cout<<"num: "<<num<<endl;
        cout<<"name: " <<name<<endl;
        cout<<"sex: " <<sex<<endl<<endl;
    }
};

Student::Student(int n,char *m,char s):num(n),sex(s){strcpy(name,m);};

int main()
{
    Student stud1(1,"AAA",'f');
	Student stud2(2,"BBB",'m');
    
    stud1.display();
    stud2.display();
    return 0;
}

2、私有数据成员包含字符串

例2.1:类内带参构造函数初始化
#include <iostream>
using namespace std;

class Student
{
private:
    int num;
    string name;
    char sex;
public:
    Student(int n,string m,char s)
    {
        num = n;
        name = m;
        sex = s;
    }
    void display( )
    {
        cout<<"num: "<<num<<endl;
        cout<<"name: " <<name<<endl;
        cout<<"sex: " <<sex<<endl<<endl;
    }
};

int main()
{
    Student stud1(1,"AAA",'f');
	Student stud2(2,"BBB",'m');
    
    stud1.display();
    stud2.display();
    return 0;
}
例2.2:类外带参构造函数(参数列表法)初始化
#include <iostream>
using namespace std;

class Student
{
private:
    int num;
    string name;
    char sex;
public:
    Student(int,string,char);
    void display( )
    {
        cout<<"num: "<<num<<endl;
        cout<<"name: " <<name<<endl;
        cout<<"sex: " <<sex<<endl<<endl;
    }
};

Student::Student(int n,string m,char s):num(n),name(m),sex(s){};
   
int main()
{
    Student stud1(1,"AAA",'f');
	Student stud2(2,"BBB",'m');
    
    stud1.display();
    stud2.display();
    return 0;
}

二、默认构造函数

调用构造函数时不必给出实参的构造函数,称为默认构造函数(default constructor)。

class Time
{
public:
    Time( );
    void show_time();
private:
    int hour;
    int minute;
    int sec;
};
Time::Time( )
{
    hour=0;
    minute=0;
    sec=0;
}

int main()
{
    Time t1; //非t1();
    t1.show_time();
}

构造函数的使用说明:

如果用户自己没有定义构造函数,则C++系统会自动生成一个默认构造函数,只是这个构造函数的函数体是空的,也没有参数,不执行初始化操作。

即使提供了其他构造函数,提供一个默认构造函数几乎总是对的。

通常,在默认构造函数中,给成员提供的初始值应该指出该对象是“空”的。

三、带默认参数的构造函数(声明时形参有值)

使用默认参数的构造函数说明:

1)在声明构造函数时指定默认值,而不是在定义构造函数时指定默认值。

2)声明时,构造函数的形参名可以省略

3)构造函数的声明和定义需一一对应

注意1:

Time();

Time(int h,int m=0,int s=0);

这两个重载函数不冲突

注意2:

Time();

Time(int h=0,int m=0,int s=0);等冲突重载

#include <iostream>
using namespace std;

class Time
{
public:
    Time();    //无默认参数的构造函数
    Time(int,int=0,int=0);//带默认参数的构造函数;声明时,省略了构造函数的形参名
    //上一行等同于 Time(int h,int m=0,int s=0);
    void show_time();
private:
    int hour;
    int minute;
    int sec;
};

Time::Time( ) : hour(0), minute(0), sec(0){ };//对应 声明 Time();
/*对应声明 Time(int,int=0,int=0); */
Time::Time(int h,int m,int s): hour(h), minute(m), sec(s){ };

void Time::show_time()
{
    cout<<hour<<":"<<minute<<":"<<sec<<endl;
}

int main( )
{
    Time t1;            //使用默认构造函数初始化
    t1.show_time();
    Time t2(8);         //使用带参数的构造函数初始化,仅一个参数,所以仅对数据成员1初始化
    t2.show_time();
    Time t3(8,30);      //使用带参数的构造函数初始化,两个参数,所以依次对数据成1&2初始化
    t3.show_time();
    Time t4(18,56,48);  ////使用带参数的构造函数初始化,三个参数,依次对全部数据成员初始化
    t4.show_time();
    return 0;
}


 

Time(int h,int m=0,int s=0);等同于Time(int,int=0,int=0);

避免由于默认参数引起歧义,如下面两个重载构造函数非法

Time();

Time(int h=12,int=0,int=0);

四、析构函数

(一)析构函数定义

析构函数(destructor)也是一个特殊的成员函数。

构造函数在对象创建时自动执行;当对象的生命期结束时,会自动执行析构函数。

析构函数的名字是类名的前面加一个“~”符号。

Time::Time(); //构造函数

Time::~Time(); //析构函数

析构函数的作用在撤销对象占用的内存之前完成一些清理、善后的工作。

只要对象的生命期结束,程序就自动执行事先设计好的析构函数来完成相关工作。

#include <iostream>
using namespace std;

class Student
{
public:
    Student(int n,string nam,char s)
    {
        num=n;
        name=nam;
        sex=s;
        cout<<"执行构造函数:"<<name<<" come."<<endl;
    }
~Student( )
    {
        cout<<"执行析构函数:"<<"Bye bye, "<<name<<"."<<endl;
    }

void display();


private:
    int num;
    string name;
    char sex;
};


void Student::display( )
{
    cout<<"num: "<<num<<endl;
    cout<<"name: "<<name<<endl;
    cout<<"sex: "<<sex<<endl<<endl;
}

int main( )
{
    Student stud1(10010,"Wang_li",'f');
    stud1.display( );
    Student stud2(10011,"Zhang_fun",'m');
    stud2.display( );
    return 0;
}

(二)不同对象执行析构函数的时机

对于函数中定义的自动局部对象,当函数被调用结束时对象释放,在对象释放前自动执行析构函数。

static局部对象只在main函数结束或调用exit函数结束程序时,调用static局部对象的析构函数。

对于全局对象,在程序的流程离开其作用域时(如main函数结束或调用exit函数) 时,调用该全局对象的析构函数。

1、new和delete的配对

若在构造函数中用new运算符为对象成员动态地分配了空间,要在析构函数中,用delete运算符释放分配的空间。举例如下:

class ClassName
{
private:
    int *p;
public:
    ClassName();
    ~ClassName();
};
ClassName::ClassName()
{
    p = new int;
}
ClassName::~ClassName()
{
    delete p;
}

2、关于析构函数

析构函数不返回任何值,没有函数类型,也没有函数参数。

析构函数不能被重载:一个类可以有多个构造函数,但只能有一个析构函数。

析构函数的作用并不仅限于释放资源方面:它还可以被用来执行“用户希望在最后一次使用对象之后所执行的任何操作”。

析构函数可以完成类的设计者所指定的任何操作:一般情况下,类的设计者应当定义析构函数,以指定如何完成“清理”的工作。

如果用户没有定义析构函数,C++编译系统会自动生成一个析构函数:自动生成的析构函数只是徒有析构函数的名称和形式,实际上什么操作都不进行。

五、构造函数和析构函数的调用顺序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值