通知:网站正在变更备案,由于一些bug...部分功能暂不可用。

MENU

C++:构造函数与析构函数

• February 3, 2021 • Read: 635 • 高级语言,学习笔记阅读设置

构造函数

构造函数用于对象的初始化,一旦建立对象,就需要有一个有意义的初始值,构造函数的作用即是在对象初始化时被调用,给对象分配内存空间以及完成初始化。

变量和结构体的初始化方法如下:

int a = 1;//变量的初始化
int b = 2;
int *p = &a;
struct instance {
    int num;
    string name;
};
void fun(){
    instance s = {50 ,"Henry"};//结构体初始化
}

但是对于对象而言,这种初始化方法显然不可取,因为类中的 private 和 protected 成员外界并不能对其访问:

class instance{
    public:
        //....
    private:
        int num;
        string name;
};
void fun {
    instance s = {50,"Henry"}//error
} 

这样的初始化操作意味着在其他函数中允许访问类对象中的保护数据成员,然而类的封装性就体现在有一部分被封装的数据外界是不能对其进行访问的,该行为被编译器禁止。

所以,在初始化对象时,常常使用构造函数来对受保护的数据成员进行初始化操作:

class instance
{
public:
    instance() //构造函数与类同名,无返回类型,也不允许return
    {
        test = 55;
        name = "Hello World";
    }
    void print()
    {
        cout << "tset:" << test1 << ",name:" << name << endl;
    }

private:
    int test;
    string name;
};
int main()
{
    auto pointer = new instance();//调用构造函数instance()
    pointer->print();
    delete pointer;
    return 0;
}

程序将输出 :tset:55,name:Hello World

刚才演示的是构造函数的类内定义,同样的,构造函数也允许类外定义,但要注意,构造函数的类外定义和普通的成员函数的类外定义一样,需要在函数名前面加上 类名:: 为了区分成员函数与其他普通函数,故该操作是必要的。

class instance{
    public:
        instance();//构造函数声明
        //...
    private:
        //...
};
void fun(){
    instance::instance(){//构造函数的类外定义
        //.....
    }
}

以上为构造函数类外定义演示。

对于对象数组,声明时,数组成员有多少个,就会调用多少次的构造函数,来分别初始化数组中每一个成员:

void fun() {
    instance temp[5];//对象数组,5个成员
    //..
}

以上就会调用五次 instance 的构造函数,来分别初始化 temp[0],temp[1]......

那么,如果一个类的对象是另一个类的数据成员,在进行另一个类的对象初始化时,会对该成员自动调用其构造函数,每个类只复制处理自己的对象,其它对象就由它所属类的构造函数来进行处理。

注意:若一个类中有某些数据成员是另一个类的对象,那么在调用这个类的构造函数时,该构造函数会先调用另一个类的构造函数将这些对象初始化后后再执行自己的函数体对该对象进行初始化。

析构函数

说简单点就是和构造函数有着相反的作用,析构函数用于初始化而析构函数用于在对象销毁前将构造函数申请的资源释放。可以理解为用 new 申请堆内存后需要使用 delete 对其进行释放,析构函数的作用相当于 delete。

正是由于析构函数的此特点,所以它的定义与构造函数的区别仅为一个逻辑非运算符,即:~

class instance{
    public:
    instance(){
    name =  new char[20];
}
    ~instance(){
    delete[] name;//释放申请的堆空间
    name = nullptr;
}

析构函数在对象生命周期时会依次调用,且调用顺序与构造函数相反,即先初始化的对象后释放。

带参数的构造函数

前面介绍的构造函数都是无参构造函数,它有时并不能满足我们的要求,它只能机械地去初始化一个对象。

对于以下的实例,我们就需要使用含参的构造函数:

class Mankind{
    public:
    //..
    private:
        int age;
        int sex;
        Date birthday;
};

众所周知,每个人有每个人的特点,如果该构造函数依旧是无参类型的,那么我们初始化的对象将是千篇一律。

其次,构造函数可以重载,显式调用构造函数将产生一个无名对象,无名对象将在之后介绍。

class Mankind{
    public:
        Mankind(string n, int a, int s, Date b)
        {
            name = n;
            age = a;
            sex = s;
            birthday = b;
}
    private:
        string name;
        int age;
        int sex;
        Date birthday;//Date为另一个类
};

这样,我们就可以根据不同人的特点,使用不同的参数,初始化不同的对象。

默认构造函数

C++ 规定每一个类都必须有一个构造函数,如果没有定义构造函数,系统将调用默认的构造函数(等价于定义一个空的构造函数)。

默认构造函数仅复制创建对象所需空间,不提供任何初始化工作,当然如果一个类中定义了构造函数,系统将不在提供该默认构造函数,如需要在定义有参构造函数后使用无参构造函数,那么需要自己定义同名的无参构造函数。

我们在定义有参构造函数后需要注意以下问题:

class student{
    public:
        student(string nName){
          name = nName;
        }
    private:
        string name;
        //..
};
int main() {
    student noName;//error,没有与其匹配的构造函数
}

在初始化对象 noName 时,我们没有传入任何参数,但在类中我们却定义了需要 string 型参数的构造函数,所以系统不再提供无参的默认构造函数,编译器因此报错。

对类进行以下处理即可解决该问题:

class student{
    public:
        student(string nName){
          name = nName;
        }
        student(){}
    private:
        string name;
        //..
};

我们自行定义一个无参的空构造函数即可,之前我们也说到:系统调用默认的构造函数等价于定义一个空的无参构造函数。

最后,在创建无参对象时请勿在后面加括号,因为这会与定义一个返回类对象的无参函数混淆:

void fun(){
    student stu;//正确的无参对象声明方式
    student stu();//错误的无参对象声明方式
}

编辑:Henry 2021-02-03 转载前请申请


Archives QR Code
QR Code for this page
Tipping QR Code