从C到C++【类和对象02】

这章我们主要讲解类的6个默认成员函数
image.png

一、构造函数

1.1 概念

构造函数的主要任务是初始化对象,名字与类名相同,创建类型对象时编译器自动调用,以保证每个对象在被创建时都有一个合适的初值,并且在整个对象的声明周期只调用一次

1.2 特性

构造函数的主要任务并不是开空间创建对象,而是初始化对象
特点:

1.与类名相同
2.可以构成函数重载
3.无返回值
4.对象实例化时自动调用构造函数

1.3 示例

#include<iostream>
using namespace std;
class Date
{
public:
    //可以使用缺省参数
    Date(int year=2023,int month=2,int day=23)
    {
        _year=year;
        _month=month;
        _day=day;
    }
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1;
    Date d2(1998,2,3);
    cout << d1._year << endl;
    cout << d2._year << endl;
    return 0;
}

如果这里不写构造函数,那么输出d1会变成随机值,在内置类型中,我们必须自己写构造函数

#include<iostream>
using namespace std;
class Time
{
public:
    //C++11的特性,我们既可以在构造函数里写,也可以在声明的时候写上去
    int hour=0;
    int time=0;
    int mintue=0;
};
class Date
{
public:
    //可以使用缺省参数
    Date(int year=2023,int month=2,int day=23)
    {
        _year=year;
        _month=month;
        _day=day;
    }
    int _year;
    int _month;
    int _day;
    //自定义类型
    Time t;
};

int main()
{
    Date d1;
    Date d2(1998,2,3);
//    cout << d1._year << endl;
    cout << d2.t.hour << endl;
    return 0;
}

写成如上这样,C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员t调用的它的默认成员函数。

另外,我们常常把编译器提供给我们的构造函数称为默认构造,同时我们写的全缺省构造函数也是默认构造

二、析构函数

2.1 概念

与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

2.2 特性

  1. 析构函数名是在类名前加上字符 ~
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数

2.3 示例

class Stack
{
public:
    //默认构造函数
    Stack(int sz=4)
    {
        arr=new int[sz];
        top=0;
        capacity=sz;
    }
    //析构函数
    ~Stack()
    {
        delete[] arr;
        arr= nullptr;
        top=capacity=0;
    }
    //push
    //为了演示 这里不写扩容
    void push(int val)
    {
        arr[top++]=val;
    }
    int top1()
    {
        return arr[top-1];
    }
    void pop()
    {
        top--;
    }

private:
    int* arr;
    int top;
    int capacity;
};
int main()
{
    Stack st;
    st.push(1);
    st.push(3);
    cout << st.top1() << endl;
    st.pop();
    Stack st2(10);
    return 0;
}

这里的析构顺序是先析构st2然后析构st1,遵从栈的顺序
同理,析构函数也会生成默认析构,但是不对内置类型进行处理,本身内置类型类型也不需要处理,例如int a=10,结束程序后我们什么操作都不用干,编译器帮我们销毁对象
我们在看一个栗子:

class Stack
{
public:
    //默认构造函数
    Stack(int sz=4)
    {
        arr=new int[sz];
        top=0;
        capacity=sz;
    }
    //析构函数
    ~Stack()
    {
        delete[] arr;
        arr= nullptr;
        top=capacity=0;
    }
    //push
    //为了演示 这里不写扩容
    void push(int val)
    {
        arr[top++]=val;
    }
    int top1()
    {
        return arr[top-1];
    }
    void pop()
    {
        top--;
    }

private:
    int* arr;
    int top;
    int capacity;
};
class Date
{
public:
    //可以使用缺省参数
    Date(int year=2023,int month=2,int day=23)
    {
        _year=year;
        _month=month;
        _day=day;
    }
    int _year;
    int _month;
    int _day;
    //自定义类型
    Stack st;
};

int main()
{
    Date d;
    return 0;
}

假设日期类声明了一个栈,复用刚刚的栈类,我们发现编译器在Date里面会生成默认构造,而这个默认构造会去调用Stack的析构函数
这里给大家一个原则: 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类

三、拷贝构造函数

3.1 概念

创建一个一摸一样的新对象

3.2 特性

  1. 拷贝构造函数是构造函数的一个重载形式。
  2. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用

3.3 示例

class Date
{
public:
    //可以使用缺省参数
    Date(int year=2023,int month=2,int day=23)
    {
        _year=year;
        _month=month;
        _day=day;
    }
    //拷贝构造函数
    Date(const Date& d1)
    {
        _year=d1._year;
        _month=d1._month;
        _day=d1._day;
    }
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d(2021,1,1);
    //和d一摸一样
    Date d2(d);
    return 0;
}

不过这里我们不写拷贝构造函数,也是正确的,对于内置类型我们可以不用写拷贝构造函数,但是自定义类型的拷贝必须要写,因为有深拷贝和潜拷贝的问题,例如stack st1; stack st2(st1);这里必须自己写拷贝构造函数,关于深浅拷贝的问题这里不赘述,后面会有文章专门讲解一下这个问题

四、赋值重载

4.1 概念

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

4.2 示例

class Date
{
public:
    //可以使用缺省参数
    Date(int year=2023,int month=2,int day=23)
    {
        _year=year;
        _month=month;
        _day=day;
    }
    //返回引用可以减少拷贝
    //赋值重载
    Date& operator=(const Date& d)
    {
        if(this!=&d)
        {
            _year=d._year;
            _month=d._month;
            _day=d._day;
        }
        return *this;
    }
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1(2001,11,25);
    Date d2;
    //这个叫赋值!
    //拷贝构造是实例化的时候就拷贝给他,意义不一样
    d2=d1;
    return 0;
}

不过我们发现,我们不写赋值重载,照样可以完成赋值,赋值重载内置类型可以不写,但是自定义类型必须写,就例如刚刚的stack

五、总结

记住只有这几个成员函数编译器才会默认生成,例如重载大小,编译器不会自动生成,必须我们自己写,所谓的默认成员函数就是编译器可以自动帮我们写的,但是多数情况下不靠谱
自定义类型可以不用写的成员函数:构造函数、析构函数
内置类型可以不用写的成员函数:拷贝构造、赋值重载

取地址重载不太常用,这里不做介绍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值