C++默认构造函数(二)

news2024/11/15 13:41:14

目录

构造函数补充

构造函数初始化列表的使用

赋值运算符重载函数

运算符重载函数介绍

运算符重载函数的使用

赋值运算符重载函数

赋值运算符重载函数的使用

拷贝构造函数和赋值运算符重载函数

重载前置++和后置++

前置++

后置++

重载流插入<<与流提取>>

流插入运算符<<重载

流提取运算符>>重载

const成员函数

取地址操作符重载与const成员取地址操作符重载

实现日期类练习


声明:本篇为C++默认构造函数最后一篇

构造函数补充

在C++中,可以在构造函数的函数体中为变量进行初始化,但是实际上该过程并不是初始化,可以理解为赋值,因为对象还没有真正创建,并且初始化只能初始化一次,但是赋值可以执行多次,而当对象创建时,所有成员变量就是当成整体创建,那么每一个变量在和何处被初始化变成了一个问题,为了解决这个问题,C++标准中引入构造函数初始化列表,只要是本类的成员变量都是在初始化列表处初始化,具体初始化的内容由程序员自己决定


#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 23)
        :_year(year)
        , _month(month)
        , _day(day)
    {}

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
};

int main()
{
    Date d;
    d.print();
    return 0;
}
输出结果:
2024/3/23

构造函数初始化列表的使用

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式,语法如下

类名(参数列表)
:成员变量(值)
,成员变量(值)
,成员变量(值)
{}

📌

注意,并不是所有成员变量都需要写进初始化列表,没写入初始化列表的成员变量也会像写入初始化列表中的成员变量一样走一遍初始化列表,只是没有显式

1. 每个成员变量在初始化列表中只能出现一次(初始化只能初始化一次)

2. 类中包含以下成员,必须放在初始化列表位置进行初始化:

  1. 引用成员变量
#include <iostream>
#include <cstdlib>
#include <cassert>
using namespace std;

class Array
{
private:
    int* _arr;
    int _size;
    int& ref;
public:
    Array()
        :_arr(nullptr)
        , _size(4)
        , ref(_size)//引用类型必须初始化
    {
        _arr = (int*)malloc(sizeof(int) * _size);
        assert(_arr);
    }

    Array(const Array& data)
        :ref(_size)//拷贝构造中,引用类型也必须初始化
    {
        _arr = (int*)malloc(sizeof(int) * data._size);
        assert(_arr);

        for (int i = 0; i < data._size; i++)
        {
            _arr[i] = data[i];
        }
    }

    //重载[]
    int& operator[](int i)
    {
        return _arr[i];
    }

    //const类型的引用,不可以通过返回的引用改变数组中的值
    const int& operator[](int i) const
    {
        return _arr[i];
    }
};

int main()
{
    Array a;
    for (int i = 0; i < 4; i++)
    {
        a[i] = i + 1;
    }

    const Array p(a);
    for (int i = 0; i < 4; i++)
    {
        cout << p[i] << " ";
    }
    return 0;
}
输出结果:
1 2 3 4

  1. const成员变量
  2. 自定义类型成员(且该类没有默认构造函数时)
#include <iostream>
using namespace std;

class Time
{
private:
    int _time;
public:
    Time(int time)
    {

    }
};

class Date
{
private:
    int _year;
    int _month;
    int _day;
    Time _t;
public:
    Date()
        :_year(2023)
        ,_month(3)
        ,_day(21)
    {}
};

int main()
{
    Date d;

    return 0;
}
报错信息:
类 "Time" 不存在默认构造函数

在C++11标准规范中,可以在成员变量创建的同时给缺省值,此时如果给了这个缺省值,想使用缺省值时就不需要再将该成员变量写入初始化列表中,如果不想使用缺省值,则将成员变量写入初始化列表中并给定初始值,否则默认初始值为0

#include <iostream>
using namespace std;

//构造函数初始化列表
class Date
{
private:
    int _year = 2023;
    int _month = 2;
    int _day = 28;
public:
    Date()
        //_year没有写入初始化列表,使用缺省值
        : _month(3)//写入初始化列表中,给了初始值为3,使用初始值
        , _day()//写入初始化列表中,但是没给初始值,默认初始值为0
    {}

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
};

int main()
{
    Date d;
    d.print();
    return 0;
}
输出结果:
2023/3/0

在初始化列表中无法处理例如动态申请内存的行为,此时可以在函数体内完成,例如

#include <iostream>
#include <cstdlib>
#include <cassert>
using namespace std;

class Array
{
private:
    int* _arr;
    int _size;
public:
    Array()
        :_arr(nullptr)
        ,_size(4)
    {
        _arr = (int*)malloc(sizeof(int) * _size);//在构造函数体中分配空间
        assert(_arr);
    }
    
    Array(const Array& data)
    {
        _arr = (int*)malloc(sizeof(int) * data._size);
        assert(_arr);
        
        for (int i = 0; i < data._size; i++)
        {
            _arr[i] = data[i];
        }
    }

    //重载[]
    int& operator[](int i)
    {
        return _arr[i];
    }

    const int& operator[](int i) const
    {
        return _arr[i];
    }
};

int main()
{
    Array a;
    for (int i = 0; i < 4; i++)
    {
        a[i] = i + 1;
    }

    const Array& p(a);
    for (int i = 0; i < 4; i++)
    {
        cout << p[i] << " ";
    }
    return 0;
}
输出结果:
1 2 3 4

所以,如果不使用缺省值,尽量使用初始化列表初始化,因为不管是否使用初始化列表,对于自定义类型的成员变量,一定会先使用初始化列表初始化

📌

注意:成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

class A
{
public:
    A(int a)
        :_a1(a)
        ,_a2(_a1)
    {}
    
    void Print() {
        cout<<_a1<<" "<<_a2<<endl;
    }
private:
    int _a2;
    int _a1;
};
int main() {
    A aa(1);
    aa.Print();
}
输出结果:
1 -858993460

在上面的代码中,因为成员变量_a2_a1先声明,所以在初始化时先走_a2(_a1),所以_a2被初始化为随机值,接着再初始化_a1,所以_a1为1

赋值运算符重载函数

运算符重载函数介绍

在C++中,为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似

#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
};

int main()
{
    Date d1;
    Date d2(d1);
    cout << (d1 == d2) << endl;//自定义类型无法使用内置的关系运算符进行比较

    return 0;
}
报错信息:
二进制“==”:“Date”不定义该运算符或到预定义运算符可接收的类型的转换

为了自定义类型的对象之间可以进行关系运算,可以使用运算符重载,如下面代码

#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }

    int getYear()
    {
        return _year;
    }

    int getMonth()
    {
        return _month;
    }

    int getDay()
    {
        return _day;
    }
};

bool operator==(Date& d1, Date& d2)
{
    return d1.getYear() == d2.getYear() && d1.getMonth() == d2.getMonth() && d1.getDay() == d1.getDay();
}

int main()
{
    Date d1;
    Date d2(d1);
    cout << (d1 == d2) << endl;//有重载==的函数时可以比较

    return 0;
}
输出结果:
1

运算符重载函数的使用

对于运算符重载函数来说,其函数名为:operator+需要重载的运算符,而该函数的原型如下:

函数返回类型 operator运算符(参数列表)

定义运算符重载函数时,需要注意下面的问题

  1. 不能通过连接其他符号来创建新的操作符:比如operator@
  2. 重载操作符必须有一个类类型参数
  3. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  5. .* :: sizeof ?: . :注意以上5个运算符不能重载

对于下面的代码

#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }

    int getYear()
    {
        return _year;
    }

    int getMonth()
    {
        return _month;
    }

    int getDay()
    {
        return _day;
    }
};

//全局运算符重载函数
bool operator==(Date& d1, Date& d2)
{
    return d1.getYear() == d2.getYear() && d1.getMonth() == d2.getMonth() && d1.getDay() == d1.getDay();
}

int main()
{
    Date d1;
    Date d2(d1);
    cout << (d1 == d2) << endl;//有重载==的函数时可以比较

    return 0;
}
输出结果:
1

在上面的代码中,因为运算符重载函数不在类中,并且因为类的成员变量为private,所以需要调用获取函数来得到当前对象的成员变量中的值,并且因为在全局中,并不存在哪一个对象调用函数,所以没有this指针,此时形参的个数对应运算符的操作数的个数

📌

注意上面的全局运算符重载函数中形参不可以使用const修饰,因为如果使用了const修饰,那么就是d1d2都是const修饰的对象,而this只是*const,而不是const*,本来是d1d2const修饰不可以修改引用的对象的值,但是如果传递给了this可能会出现通过this改变d1d2引用的对象的值,所以此处涉及到将引用的权限放大

考虑到如果将运算符重载函数写在类外需要额外写三个函数来获取到指定的值,所以可以将运算符重载函数写进类中

#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }

    //类中的运算符重载函数
    bool operator==(const Date& d)
    {
        return _year == d._year && _month == d._month && _day == d._day;
    }
};

int main()
{
    Date d1;
    Date d2(d1);
    cout << (d1 == d2) << endl;//直接调用类中实现的运算符重载函数
    //上面的代码相当于:cout << d1.operator==(d2) << endl;
    return 0;
}
输出结果:
1

在上面的代码中,因为运算符重载函数在类中,所以存在this指针,所以只需要传递一个参数(加上this指针参数和额外的参数一共两个参数对应==操作符的操作数个数),并且形参对象引用d指的是第二个操作数,因为d1 == d2等价于d1.operator==(d2),因为是d1在调用运算符重载函数,所以this指针指向的对象即为d1

赋值运算符重载函数

赋值运算符重载函数也是运算符重载函数中的一种,因为重载的运算符为赋值运算符=,重载赋值运算符时,首先不能改变赋值运算符的特性,包括连续赋值

赋值运算符重载函数的使用

赋值运算符重载函数的格式

  • 参数类型:const T&,传递引用可以提高传参效率(T为类名)
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值,并且需要检测是否自己给自己赋值,以减少赋值次数
  • 返回*this :要复合连续赋值的含义
#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }

    void operator=(const Date& d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
};

int main()
{
    Date d(2024, 2, 28);
    Date d1;
    
    d1 = d;
    d1.print();

    return 0;
}
输出结果:
2024/2/28

在上面的代码中,类Date中对赋值运算符进行了重载,将引用指向的对象中的值给调用该运算符重载函数的对象,但是上面的代码无法实现赋值运算符的连续赋值,因为没有返回值

#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }

    void operator=(const Date& d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
};

int main()
{
    Date d(2024, 2, 28);
    Date d1;
    Date d2;
    
    d1 = d;
    d1.print();
    d2 = d1 = d;

    return 0;
}
报错信息:
二元“=”: 没有找到接受“void”类型的右操作数的运算符(或没有可接受的转换)

所以,为了解决这个问题,赋值运算符重载函数需要给定返回值为类类型

#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }

    Date& operator=(const Date& d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;

        return *this;
    }
};

int main()
{
    Date d(2024, 2, 28);
    Date d1;
    Date d2;

    d2 = d1 = d;
    d1.print();
    d2.print();
    return 0;
}
输出结果:
2024/2/28
2024/2/28

在上面的代码中,赋值运算符重载函数给了返回值为Date类型的引用,此时可以使用连续赋值,因为赋值运算符从右往左结合,所以具体过程为d对象赋值给d1,d1对象的值赋值给d2,从函数调用的角度理解为d2.operator=(d1.operator=(d));(注意不是d2.operator=(d1).operator=(d);,本句理解为d2被赋值为d1中的内容,然后再被赋值为d中的内容,相当于d2 = d1; d2 = d;

📌

赋值运算符重载函数的返回值也可以不用引用,但是此时在返回时会调用拷贝构造函数将返回值的内容拷贝给调用赋值运算符重载函数的对象,为了减少调用拷贝构造的次数,更推荐使用引用,该解释同样适用于形参

另外,还有一个小问题,如果两个相同的对象进行赋值,那么将产生额外的一次赋值,对于这个问题,在赋值时需要判断形参引用的对象和this指针指向的对象是否是同一个地址的对象

#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }

    Date& operator=(const Date& d)
    {
        if (this != &d)
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }

        return *this;
    }
};

int main()
{
    Date d(2024, 2, 28);
    d = d;
    return 0;
}

在上面的代码中,判断this指针指向的对象的地址和引用的对象地址是否相等,如果二者相等,则证明是同一个对象,不需要进行赋值直接返回即可,注意形参的Date &d为创建对象的引用,而if语句中的&d是取引用的地址

注意,赋值运算符重载函数必须作为成员函数,不能作为全局函数

#include <iostream>
using namespace std;

class Date
{
public:
    int _year;
    int _month;
    int _day;
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
};
Date& operator=(Date& d1, Date& d)
{    
    if (&d1 != &d)
    {
        d1._year = d._year;
        d1._month = d._month;
        d1._day = d._day;
    }

    return d1;
}
int main()
{
    Date d(2024, 2, 28);
    Date d1;
    d1 = d;

    return 0;
}
报错信息:
“operator=”必须是成员函数

赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,所以赋值运算符重载只能是类的成员函数

拷贝构造函数和赋值运算符重载函数

与拷贝构造函数类似,赋值运算符重载函数如果用户没有实现,编译器会自动实现。默认如果不自主实现还是按照字节拷贝,按照字节方式拷贝也会遇到像拷贝函数一样的问题(指对象中有资源申请时)

  1. 一个对象改变,另一个对象也会跟着改变,严重者会数据覆盖
  2. 释放资源时会因为多次释放一个空间导致程序崩溃
#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }

};

int main()
{
    Date d(2024, 2, 28);
    Date d1;
    d1 = d;//编译器自动生成的默认赋值运算符重载函数
    Date d2(d);//编译器自动生成的拷贝构造函数
    d1.print();
    d2.print();
    return 0;
}
输出结果:
2024/2/28
2024/2/28

与拷贝构造函数一样,如果类对象中有涉及到资源申请,那么需要自己实现赋值运算符重载函数,否则直接使用默认的即可

重载前置++和后置++

前置++

对于运算符重载函数的使用规则,那么可以很容易写出++的重载函数,如下:

#include <iostream>
using namespace std;

class Date
{
private:
    int _day;
    int _month;
    int _year;
public:
    Date(int year = 2024, int month = 3, int day = 22)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }

    void operator++()
    {
        _day += 1;
    }
};

int main()
{
    Date d;
    ++d;
    d.print();
    return 0;
}
输出结果:
2024/3/23

因为前++相当于计算+=1,而因为前面实现过获取X天后的日期的函数GetAfterXDays_plusEqual,所以可以直接用该函数进行复用,从而实现++操作

#include <iostream>
using namespace std;

class Date
{
private:
    int _day;
    int _month;
    int _year;
public:
    Date(int year = 2024, int month = 3, int day = 22)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
    
    int GetMonthDays(int year, int month)
    {
        int monthDays[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
        if (month == 2 && ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
        {
            return monthDays[month] + 1;
        }   
        else
        {
            return monthDays[month];
        }
    }

    Date& GetAfterXDays_plusEqual(int days)
    {
        _day += days;
        while (_day > GetMonthDays(_year, _month))
        {
            _day -= GetMonthDays(_year, _month);
            _month++;
            if (_month == 13)
            {
                _year++;
                _month = 1;
            }
        }
        return *this;
    }

    Date GetAfterXDays_plus(int days)
    {
        Date tmp(*this);
        tmp._day += days;
        while (tmp._day > GetMonthDays(tmp._year, tmp._month))
        {
            tmp._day -= GetMonthDays(tmp._year, tmp._month);
            tmp._month++;
            if (tmp._month == 13)
            {
                tmp._year++;
                tmp._month = 1;
            }
        }
        return tmp;
    }

    void operator++()
    {
        //因为++相当于+=1,所以可以直接复用GetAfterXDays_plusEqual(1)
        *this = GetAfterXDays_plusEqual(1);
    }
};

int main()
{
    Date d;
    ++d;
    d.print();
    return 0;
}
输出结果:
2024/3/23

注意到上面实现的++是无返回值的++运算符重载函数,但是如果函数没有返回值,将无法将++后的值给另外一个对象

#include <iostream>
using namespace std;

class Date
{
private:
    int _day;
    int _month;
    int _year;
public:
    Date(int year = 2024, int month = 3, int day = 22)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
    
    int GetMonthDays(int year, int month)
    {
        int monthDays[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
        if (month == 2 && ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
        {
            return monthDays[month] + 1;
        }   
        else
        {
            return monthDays[month];
        }
    }

    Date& GetAfterXDays_plusEqual(int days)
    {
        _day += days;
        while (_day > GetMonthDays(_year, _month))
        {
            _day -= GetMonthDays(_year, _month);
            _month++;
            if (_month == 13)
            {
                _year++;
                _month = 1;
            }
        }
        return *this;
    }

    Date GetAfterXDays_plus(int days)
    {
        Date tmp(*this);
        tmp._day += days;
        while (tmp._day > GetMonthDays(tmp._year, tmp._month))
        {
            tmp._day -= GetMonthDays(tmp._year, tmp._month);
            tmp._month++;
            if (tmp._month == 13)
            {
                tmp._year++;
                tmp._month = 1;
            }
        }
        return tmp;
    }

    void operator++()
    {
        //_day += 1;
        //因为++相当于+=1,所以可以直接复用GetAfterXDays_plusEqual(1)
        *this = GetAfterXDays_plusEqual(1);
        //return *this;
    }

    Date& operator=(const Date& d)
    {
        if (this != &d)
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
        return *this;
    }
};

int main()
{
    Date d;
    ++d;
    d.print();
    Date d1;
    d1 = ++d;
    return 0;
}
报错信息:
二元“=”: 没有找到接受“void”类型的右操作数的运算符(或没有可接受的转换)

所以为了解决这种问题将考虑为++运算符重载函数加上返回值为类对象引用类型

#include <iostream>
using namespace std;

class Date
{
private:
    int _day;
    int _month;
    int _year;
public:
    Date(int year = 2024, int month = 3, int day = 22)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
    
    int GetMonthDays(int year, int month)
    {
        int monthDays[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
        if (month == 2 && ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
        {
            return monthDays[month] + 1;
        }   
        else
        {
            return monthDays[month];
        }
    }

    Date& GetAfterXDays_plusEqual(int days)
    {
        _day += days;
        while (_day > GetMonthDays(_year, _month))
        {
            _day -= GetMonthDays(_year, _month);
            _month++;
            if (_month == 13)
            {
                _year++;
                _month = 1;
            }
        }
        return *this;
    }

    Date GetAfterXDays_plus(int days)
    {
        Date tmp(*this);
        tmp._day += days;
        while (tmp._day > GetMonthDays(tmp._year, tmp._month))
        {
            tmp._day -= GetMonthDays(tmp._year, tmp._month);
            tmp._month++;
            if (tmp._month == 13)
            {
                tmp._year++;
                tmp._month = 1;
            }
        }
        return tmp;
    }

    Date& operator++()
    {
        //_day += 1;
        //因为++相当于+=1,所以可以直接复用GetAfterXDays_plusEqual(1)
        *this = GetAfterXDays_plusEqual(1);
        return *this;
    }

    Date& operator=(const Date& d)
    {
        if (this != &d)
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
        return *this;
    }
};

int main()
{
    Date d;
    ++d;
    d.print();
    Date d1;
    d1 = ++d;
    return 0;
}
输出结果:
2024/3/23

后置++

上面的函数中实现了前置++,但是并没有实现后置++,如果在没有实现后置++时,使用后置++,则会出现下面的情况

#include <iostream>
using namespace std;

class Date
{
private:
    int _day;
    int _month;
    int _year;
public:
    Date(int year = 2024, int month = 3, int day = 22)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }

    int GetMonthDays(int year, int month)
    {
        int monthDays[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
        if (month == 2 && ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
        {
            return monthDays[month] + 1;
        }
        else
        {
            return monthDays[month];
        }
    }

    Date& GetAfterXDays_plusEqual(int days)
    {
        _day += days;
        while (_day > GetMonthDays(_year, _month))
        {
            _day -= GetMonthDays(_year, _month);
            _month++;
            if (_month == 13)
            {
                _year++;
                _month = 1;
            }
        }
        return *this;
    }

    Date GetAfterXDays_plus(int days)
    {
        Date tmp(*this);
        tmp._day += days;
        while (tmp._day > GetMonthDays(tmp._year, tmp._month))
        {
            tmp._day -= GetMonthDays(tmp._year, tmp._month);
            tmp._month++;
            if (tmp._month == 13)
            {
                tmp._year++;
                tmp._month = 1;
            }
        }
        return tmp;
    }

    Date& operator++()
    {
        //_day += 1;
        //因为++相当于+=1,所以可以直接复用GetAfterXDays_plusEqual(1)
        *this = GetAfterXDays_plusEqual(1);
        return *this;
    }

    Date& operator=(const Date& d)
    {
        if (this != &d)
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
        return *this;
    }
};

int main()
{
    Date d;
    d++;
    return 0;
}
报错信息:
二进制“++”:“Date”不定义该运算符或到预定义运算符可接收的类型的转换

所以有前置++的实现并不能同时应用于后置++,对于后置++来说,编译器为了与前置++作区分,需要在形参位置添加一个整型占位形参,如下

#include <iostream>
using namespace std;

class Date
{
private:
    int _day;
    int _month;
    int _year;
public:
    Date(int year = 2024, int month = 3, int day = 22)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }

    int GetMonthDays(int year, int month)
    {
        int monthDays[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
        if (month == 2 && ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0))
        {
            return monthDays[month] + 1;
        }
        else
        {
            return monthDays[month];
        }
    }

    Date& GetAfterXDays_plusEqual(int days)
    {
        _day += days;
        while (_day > GetMonthDays(_year, _month))
        {
            _day -= GetMonthDays(_year, _month);
            _month++;
            if (_month == 13)
            {
                _year++;
                _month = 1;
            }
        }
        return *this;
    }

    Date GetAfterXDays_plus(int days)
    {
        Date tmp(*this);
        tmp._day += days;
        while (tmp._day > GetMonthDays(tmp._year, tmp._month))
        {
            tmp._day -= GetMonthDays(tmp._year, tmp._month);
            tmp._month++;
            if (tmp._month == 13)
            {
                tmp._year++;
                tmp._month = 1;
            }
        }
        return tmp;
    }

    //前置++
    Date& operator++()
    {
        //_day += 1;
        //因为++相当于+=1,所以可以直接复用GetAfterXDays_plusEqual(1)
        *this = GetAfterXDays_plusEqual(1);
        return *this;
    }

    //后置++,但是为了不同于前置++,在形参处加入int形参作为占位便于编译器区分
    Date operator++(int)
    {
        //后置++满足先使用再++,所以返回值为原值
        Date tmp(*this);
        *this = GetAfterXDays_plusEqual(1);
        return tmp;
    }

    Date& operator=(const Date& d)
    {
        if (this != &d)
        {
            _year = d._year;
            _month = d._month;
            _day = d._day;
        }
        return *this;
    }
};

int main()
{
    Date d;
    d++;
    d.print();
    return 0;
}
输出结果:
2024/3/23

在上面的代码中,对于后置++重载函数来说,在形参处添加了一个int类型形参作为占位符,这个形参可以不给形参名,因为只是编译器用于区分

重载流插入<<与流提取>>

在C++标准库中,coutcin是属于iostreamostreamistream的对象,对于流插入<<运算符,之所以cout输出可以不用指定占位符编译器可以自动匹配的原因是ostream<<的运算符重载和函数重载,对于内置类型来说,有下面的函数重载

同样对于流提取运算符>>来说也是如此,如下图所示

但是流插入和流提取运算符并没有对自定义类型有函数重载,所以可以对流提取运算符和流插入运算符进行函数重载

流插入运算符<<重载

按照前面的运算符重载思路可以写出下面的代码

#include <iostream>
using namespace std;

class Date
{
private:
    int _day;
    int _month;
    int _year;
public:
    Date(int year = 2024, int month = 3, int day = 22)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void operator<<(ostream& cout)
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
};

int main()
{
    Date d;
    cout << d;
    return 0;
}
报错信息:
二元“<<”: 没有找到接受“Date”类型的右操作数的运算符(或没有可接受的转换)

在上面的代码中,虽然重载了<<,但是形参是ostream流的对象,而隐含的形参是this,而在运算符重载函数形参列表的规则中,对于有两个操作数的运算符重载来说,第一个参数为左操作数,第二个参数为右操作数,所以上面的代码调用应该为d << cout

#include <iostream>
using namespace std;

class Date
{
private:
    int _day;
    int _month;
    int _year;
public:
    Date(int year = 2024, int month = 3, int day = 22)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void operator<<(ostream& cout)
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
};

int main()
{
    Date d;
    //cout << d;
    d << cout;
    return 0;
}
输出结果:
2024/3/22

但是和正常的输出刚好顺序相反,所以这种方法需要改变,但是因为不能改变this在形参的位置,所以考虑到将<<重载放置到全局中,此时可以决定两个操作数的顺序,但是这个方法就不能使用this指针,并且需要考虑到成员变量的private属性,本处给出一种解决方案是使用get函数

#include <iostream>
using namespace std;

class Date
{
private:
    int _day;
    int _month;
    int _year;
public:
    Date(int year = 2024, int month = 3, int day = 22)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    int getYear()
    {
        return _year;
    }

    int getMonth()
    {
        return _month;
    }

    int getDay()
    {
        return _day;
    }
};

void operator<<(ostream& cout, Date& d)
{
    cout << d.getYear() << "/" << d.getMonth() << "/" << d.getDay() << endl;
}

int main()
{
    Date d;
    cout << d;
    return 0;
}
输出结果:
2024/3/22

在上面的代码中,将流插入运算符重载函数放置到全局中可以控制coutd的顺序,此时即可写为cout << d,但是因为上面的<<并没有返回值,所以不可以连续插入,所以可以改进为如下

#include <iostream>
using namespace std;

class Date
{
private:
    int _day;
    int _month;
    int _year;
public:
    Date(int year = 2024, int month = 3, int day = 22)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    int getYear()
    {
        return _year;
    }

    int getMonth()
    {
        return _month;
    }

    int getDay()
    {
        return _day;
    }
};

ostream& operator<<(ostream& cout, Date& d)
{
    cout << d.getYear() << "/" << d.getMonth() << "/" << d.getDay() << endl;
    return cout;
}

int main()
{
    Date d;
    Date d1;
    cout << d << d1 << endl;
    return 0;
}
输出结果:
2024/3/22
2024/3/22

流提取运算符>>重载

对于流提取运算符>>的重载类似于流插入运算符<<,但是此时不能使用获取函数,所以对于流提取运算符的重载来说,考虑用友元解决,使用友元可以让全局函数中的对象获取到private属性的变量

#include <iostream>
using namespace std;

class Date
{
private:
    int _day;
    int _month;
    int _year;
public:
    Date(int year = 2024, int month = 3, int day = 22)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    //友元
    friend istream& operator>>(istream& cin, Date& d);

    int getYear()
    {
        return _year;
    }

    int getMonth()
    {
        return _month;
    }

    int getDay()
    {
        return _day;
    }
};

ostream& operator<<(ostream& cout, Date& d)
{
    cout << d.getYear() << "/" << d.getMonth() << "/" << d.getDay() << endl;
    return cout;
}

istream& operator>>(istream& cin, Date& d)
{
    cin >> d._year >> d._month >> d._day;
    return cin;
}

int main()
{
    Date d;
    Date d1;
    cin >> d >> d1;
    cout << d << d1 << endl;
    return 0;
}
输入:
2024 2 28 2024 3 31
输出结果:
2024/2/28
2024/3/31

使用友元解决流插入运算符的重载函数

#include <iostream>
using namespace std;

class Date
{
private:
    int _day;
    int _month;
    int _year;
public:
    Date(int year = 2024, int month = 3, int day = 22)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    //友元
    friend istream& operator>>(istream& cin, Date& d);
    friend ostream& operator<<(ostream& cout, Date& d);
};

ostream& operator<<(ostream& cout, Date& d)
{
    cout << d._year << "/" << d._month << "/" << d._day << endl;
    return cout;
}

istream& operator>>(istream& cin, Date& d)
{
    cin >> d._year >> d._month >> d._day;
    return cin;
}

int main()
{
    Date d;
    Date d1;
    cin >> d >> d1;
    cout << d << d1 << endl;
    return 0;
}
输入:
2024 2 23 2022 3 31
输出结果:
2024/2/23
2022/3/31

const成员函数

const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改

在前面的运算符重载函数中,当运算符重载函数在全局时不可以使用const修饰形式参数,因为const成员变量传递给成员函数时涉及到引用权限放大,那么const成员函数就是用来解决这种权限放大问题,可以将权限保持不变或者缩小,例如在下面的代码中

#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }

    int getYear() const
    {
        return _year;
    }

    int getMonth() const
    {
        return _month;
    }

    int getDay() const
    {
        return _day;
    }
};

//全局运算符重载函数,const形参
bool operator==(const Date& d1, const Date& d2)
{
    return d1.getYear() == d2.getYear() && d1.getMonth() == d2.getMonth() && d1.getDay() == d1.getDay();
}

int main()
{
    Date d1;
    Date d2(d1);
    cout << (d1 == d2) << endl;//有重载==的函数时可以比较

    return 0;
}
输出结果:
1

在上面的代码中,使用const修饰了==运算符重载函数的两个形式参数,此时d1d2不可以被修改,当对象d1d2调用get系列函数时,成员函数的形式参数需要保证获得的权限不被放大,所以需要修饰形式参数,但是因为this指针不可以直接显式做形式参数,所以不可以使用const显式对this指针进行修饰,此时就需要将const放置到函数名后,作为修饰this指针的const以满足指针及引用权限不被放大

但是,如果==运算符重载函数中的两个形式参数并不是const修饰的变量,此时调用const成员函数也不会有错,因为此时是权限的缩小(从可修改到成员函数的不可修改),如下面的代码

#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    void print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }

    int getYear() const
    {
        return _year;
    }

    int getMonth() const
    {
        return _month;
    }

    int getDay() const
    {
        return _day;
    }
};

//全局运算符重载函数,非const形参
bool operator==(Date& d1, Date& d2)
{
    return d1.getYear() == d2.getYear() && d1.getMonth() == d2.getMonth() && d1.getDay() == d1.getDay();
}

int main()
{
    Date d1;
    Date d2(d1);
    cout << (d1 == d2) << endl;//有重载==的函数时可以比较

    return 0;
}

对于const成员函数和非const成员函数之间也是如此

#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    //非const成员函数
    void printYear()
    {
        cout << _year;
    }    

    int getYear() const
    {
        printYear();
        return _year;
    }
};

int main()
{
    Date d1;
    Date d2(d1);

    return 0;
}
报错信息:
“void Date::printYear(void)”: 不能将“this”指针从“const Date”转换为“Date &”

而对于const成员函数和const成员函数之间可以直接调用

#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    //非const成员函数
    void printYear() const
    {
        cout << _year;
    }    

    int getYear() const
    {
        printYear();
        return _year;
    }
};

int main()
{
    Date d1;
    Date d2(d1);

    return 0;
}

同样非const成员函数可以调用const成员函数

#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    //非const成员函数
    void printYear() const
    {
        cout << _year;
    }    

    int getYear() 
    {
        printYear();
        return _year;
    }
};

int main()
{
    Date d1;
    Date d2(d1);

    return 0;
}

取地址操作符重载与const成员取地址操作符重载

在C++中,这两个运算符重载函数可以不用显式定义,编译器会默认生成

#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

};

int main()
{
    Date d1;
    Date d2;
    const Date d3;

    cout << &d1 << endl;
    cout << &d2 << endl;
    cout << &d3 << endl;

    return 0;
}
输出结果:
00000031E9FEF628
00000031E9FEF658
00000031E9FEF688

也可以显式定义

#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    Date* operator&()
    {
        return this;
    }

    const Date* operator&()const
    {
        return this;
    }

};

int main()
{
    Date d1;
    Date d2;
    const Date d3;

    cout << &d1 << endl;
    cout << &d2 << endl;
    cout << &d3 << endl;

    return 0;
}
输出结果:
0000006B086FFC38
0000006B086FFC68
0000006B086FFC98

只有特殊情况,才需要重载这两个函数,比如想让别人获取到指定的内容,让其访问非法地址

#include <iostream>
using namespace std;

class Date
{
private:
    int _year;
    int _month;
    int _day;
public:
    Date(int year = 2024, int month = 3, int day = 21)
    {
        _year = year;
        _month = month;
        _day = day;
    }

    Date* operator&()
    {
        return (Date*)0;
    }

    const Date* operator&()const
    {
        return (Date*)0;
    }

};

int main()
{
    Date d1;
    Date d2;
    const Date d3;

    cout << &d1 << endl;
    cout << &d2 << endl;
    cout << &d3 << endl;

    return 0;
}
输出结果:
0000000000000000
0000000000000000
0000000000000000

实现日期类练习

//头文件
#pragma once

#include <iostream>
using namespace std;

class Date
{
private:
    int _day;
    int _month;
    int _year;

public:
    //构造函数
    Date(int year = 2024, int month = 3, int day = 23)
        :_year(year)
        ,_month(month)
        ,_day(day)
    {}

    friend inline istream& operator>>(istream& cin, Date& d);
    friend inline ostream& operator<<(ostream& cout, Date& d);

    //获取月份日期函数
    int GetMonthDays(int year, int month);

    //+=运算符重载
    Date& operator+=(int days);

    //+运算符重载
    Date operator+(int days)
    {
        Date tmp(*this);
        //复用+=重载
        tmp += days;
        return tmp;
    }

    //赋值运算符重载
    Date& operator=(const Date& d);

    //前置++运算符重载
    Date& operator++()
    {
        //复用+=重载
        *this += 1;
        return *this;
    }

    //后置++运算符重载
    Date operator++(int)
    {
        //复用+=函数
        Date tmp(*this);
        tmp += 1;
        return tmp;
    }

    //>=运算符重载
    bool operator>=(const Date& d) const;

    //<=运算符重载
    bool operator<=(const Date& d) const;

    //<运算符重载
    bool operator<(const Date& d) const
    {
        //<的对立事件为>=,故直接对>=取反
        return !(*this >= d);
    }

    //>运算符重载
    bool operator>(const Date& d) const
    {
        //>的对立事件为<=,故直接对<=取反
        return !(*this <= d);
    }

    //==运算符重载
    bool operator==(const Date& d) const
    {
        return _year == d._year && _month == d._month && _day == d._day;
    }

    //!=运算符重载
    bool operator!=(const Date& d) const
    {
        //!=的对立事件为==,故直接对==取反
        return !(*this == d);
    }

    //-=运算符重载
    Date& operator-=(int days);

    //-运算符重载
    Date operator-(int days)
    {
        Date tmp(*this);
        tmp._day -= days;
        return tmp;
    }

    //前置--运算符重载
    Date& operator--()
    {
        //复用-=重载函数
        *this -= 1;
        return *this;
    }

    //后置--运算符重载
    Date operator--(int)
    {
        //复用-=重载
        Date tmp(*this);
        *this -= 1;
        return tmp;
    }

    //日期-日期
    int operator-(const Date& d);
};

inline ostream& operator<<(ostream& cout, Date& d)
{
    cout << d._year << "/" << d._month << "/" << d._day << endl;
    return cout;
}

inline istream& operator>>(istream& cin, Date& d)
{
    cin >> d._year >> d._month >> d._day;
    return cin;
}

//实现文件
#define _CRT_SECURE_NO_WARNINGS 1
#include "Date_class.h"

//获取月份日期函数
int Date::GetMonthDays(int year, int month)
{
    int monthDays[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
    if (month == 2 && (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)))
    {
        return monthDays[month] + 1;
    }
    return monthDays[month];
}

//+=运算符重载
Date& Date::operator+=(int days)
{
    _day += days;
    while (_day > GetMonthDays(_year, _month))
    {
        _day -= GetMonthDays(_year, _month);
        _month++;
        if (_month == 13)
        {
            _year++;
            _month = 1;
        }
    }
    return *this;
}

//赋值运算符重载
Date& Date::operator=(const Date& d)
{
    if (this != &d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    return *this;
}

//>=运算符重载
bool Date::operator>=(const Date& d) const
{
    //如果年大就直接返回true
    if (_year>d._year)
    {
        return true;
    }
    else if(_year == d._year && _month > d._month)//年相等时比较月份,月份大就直接返回true
    {
        return true;
    }
    else if (_year == d._year && _month == d._month && _day > d._day)//年相等,月份相等时,天大就直接返回true
    {
        return true;
    }
    else//其他情况均返回false
    {
        return false;
    }
}

//<=运算符重载
bool Date::operator<=(const Date& d) const
{
    //如果年小就直接返回true
    if (_year < d._year)
    {
        return true;
    }
    else if (_year == d._year && _month < d._month)//年相等时比较月份,月份小就直接返回true
    {
        return true;
    }
    else if (_year == d._year && _month == d._month && _day < d._day)//年相等,月份相等时,天小就直接返回true
    {
        return true;
    }
    else//其他情况均返回false
    {
        return false;
    }
}

//-=运算符重载
Date& Date::operator-=(int days)
{
    _day -= days;
    while (_day <= 0)
    {
        _month--;
        if (_month == 0)
        {
            _year--;
            _month = 12;
        }
        _day += GetMonthDays(_year, _month);
    }
    return *this;
}

//日期-日期
int Date::operator-(const Date& d)
{
    Date maxYear = *this;
    Date minYear = d;
    int flag = 1;

    if (maxYear < minYear)
    {
        maxYear = d;
        minYear = *this;
        flag = -1;
    }

    int count = 0;
    while (maxYear != minYear)
    {
        count++;
        ++minYear;
    }

    return count * flag;
}

//测试文件
#define _CRT_SECURE_NO_WARNINGS 1
#include "Date_class.h"

int main()
{
    Date d;
    Date d1(d);

    Date d2(2023, 1, 1);
    d--;
    cout << d;
    cout << (d != d1) << endl;
    cout << (d >= d1) << endl;
    Date d3;
    d3 = --d2;
    cout << d3;
    Date d4(2023, 2, 7);
    d4 -= 100;
    cout << d4;
    cout << d4 - d1 << endl;

    Date d5;
    Date d6;
    cin >> d5 >> d6;
    cout << d5 << d6 << endl;
    return 0;
}
输入:
2024 3 23
2023 2 23
输出结果:
2024/3/22
1
0
2022/12/31
2022/10/30
-510
2024/3/23
2023/2/23

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1539072.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ 其它

1、内存四区-代码区 2、内存四区-全局区 生成exe后&#xff0c;运行前是代码区和全局区 3、内存四区-栈区 4、内存四区-堆区 5、new *new一个整型10&#xff0c;返回的是该数据类型的指针&#xff0c;所以用int p 所以是int [10]&#xff0c;所以new的是int[10]&#x…

3.23项目:聊天室

1、 基于UDP的网络聊天室 项目需求&#xff1a; 如果有用户登录&#xff0c;其他用户可以收到这个人的登录信息如果有人发送信息&#xff0c;其他用户可以收到这个人的群聊信息如果有人下线&#xff0c;其他用户可以收到这个人的下线信息服务器可以发送系统信息 服务器 #inc…

东方博宜 1469. 数的统计

东方博宜 1469. 数的统计 #include<iostream> using namespace std; int main() {int n ;cin >> n ;int x ;cin >> x ;int cnt ;cnt 0;for (int i 1 ; i < n ; i){int num ;num i ;while(num!0){int g ;g num % 10 ;if (g x)cnt 1 ;num num / 10…

springboot294基于java的火车票订票系统的设计与实现

火车票订票系统设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装火车票订票系统软件来发挥其高效…

ElasticSearch首次启动忘记密码,更改密码(Windows 10)

先启动ElasticSearch 启动方式cmd到lasticsearch-8.12.2\bin目录下输入elasticsearch 启动成功后新开一个窗口输入elasticsearch-reset-password -u elastic

《剑指 Offer》专项突破版 - 面试题 88 : 动态规划的基础知识(C++ 实现)

目录 前言 面试题 88 : 爬楼梯的最少成本 一、分析确定状态转移方程 二、递归代码 三、使用缓存的递归代码 四、空间复杂度为 O(n) 的迭代代码 五、空间复杂度为 O(1) 的迭代代码 前言 动态规划是目前算法面试中的热门话题&#xff0c;应聘者经常在各大公司的面试中遇到…

C++ —— 日期计算器

1. 头文件 #pragma once #include <iostream> using namespace std;class Date { public:Date(int year 1, int month 1, int day 1);int GetMonthDay();bool operator>(const Date& d) const;bool operator>(const Date& d)const;bool operator<(c…

机器学习--jupyter-matplotlib使用中无法显示中文

jupyter使用中无法显示中文 在jupyter中&#xff0c;通过matplotlib作图时可能会添加中文标题&#xff0c;但有时候会不显示中文 import numpy as np import matplotlib.pyplot as pltx np.arange(0, 6, 0.1) # 以0.1为单位&#xff0c;成0到6的数据 y1 np.sin(x) y2 np.c…

ubuntu安装多个gcc并设置可切换

测试环境&#xff1a; Ubuntu16.04 1. 查看当前有几个gcc&#xff0c;g ls /usr/bin/gcc* ls /usr/bin/g* 有两个版本&#xff0c;5和7. 2. 安装特定gcc/g 版本 可以用sudo apt install gcc-version安装&#xff0c;比如说我想安装gcc-7&#xff0c;则命令为sudo apt instal…

第5章 数据建模和设计

思维导图 5.1 引言 最常见的6种模式&#xff1a;关系模式、多维模式、面向对象模式、 事实模式、时间序列模式和NoSQL模式 每种模式分为三层模型&#xff1a;概念模型、逻辑模型和物理模型 每种模型都包含一系列组件&#xff1a;如实体、关系、事实、键和属性。 5.1.1 业务驱…

【Flink】窗口实战:TUMBLE、HOP、SESSION

窗口实战&#xff1a;TUMBLE、HOP、SESSION 1.TUMBLE WINDOW1.1 语法1.2 标识函数1.3 模拟用例 2.HOP WINDOW2.1 语法2.2 标识函数2.3 模拟用例 3.SESSION WINDOW3.1 语法3.2 标识函数3.3 模拟用例 4.更多说明 在流式计算中&#xff0c;流通常是无穷无尽的&#xff0c;我们无法…

C++第十弹---类与对象(七)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、再谈构造函数 1.1、构造函数体赋值 1.2、初始化列表 1.3、explicit关键字 2、static成员 2.1、概念 2.2、特性 2.3、面试题 总结 1、再…

鸿蒙Harmony应用开发—ArkTS(@Prop装饰器:父子单向同步)

Prop装饰的变量可以和父组件建立单向的同步关系。Prop装饰的变量是可变的&#xff0c;但是变化不会同步回其父组件。 说明&#xff1a; 从API version 9开始&#xff0c;该装饰器支持在ArkTS卡片中使用。 概述 Prop装饰的变量和父组件建立单向的同步关系&#xff1a; Prop变量…

leetcode 2617. 网格图中最少访问的格子数【单调栈优化dp+二分】

原题链接&#xff1a;2617. 网格图中最少访问的格子数 题目描述&#xff1a; 给你一个下标从 0 开始的 m x n 整数矩阵 grid 。你一开始的位置在 左上角 格子 (0, 0) 。 当你在格子 (i, j) 的时候&#xff0c;你可以移动到以下格子之一&#xff1a; 满足 j < k < gri…

【单元测试】一文读懂java单元测试

目录 1. 什么是单元测试2. 为什么要单元测试3. 单元测试框架 - JUnit3.1 JUnit 简介3.2 JUnit 内容3.3 JUnit 使用3.3.1 Controller 层单元测试3.3.2 Service 层单元测试3.3.3 Dao 层单元测试3.3.4 异常测试3.3.5 测试套件测多个类3.3.6 idea 中查看单元测试覆盖率3.3.7 JUnit …

Excel使用VLOOKUP函数

VLOOKUP(lookup_value,table_array,col_index_num,range_lookup) 释义&#xff1a; lookup_value&#xff1a;要查找的值&#xff0c;包括数字&#xff0c;文本等 table_array&#xff1a;要查找的值以及预期返回的内容所在的区域 col_index_num&#xff1a;查找的区域的列…

安装mysql8.0.36遇到的问题没有developer default 选项问题

安装mysql8.0.36的话没有developer default选项&#xff0c;直接选择customer就好了&#xff0c;点击next之后通过点击左边Available Products里面的号和中间一列的右箭头添加要安装的产品&#xff0c;最后会剩下6个 安装完成后默认是启动了&#xff0c;并且在电脑注册表注册了…

机器学习——决策树剪枝算法

机器学习——决策树剪枝算法 决策树是一种常用的机器学习模型&#xff0c;它能够根据数据特征的不同进行分类或回归。在决策树的构建过程中&#xff0c;剪枝算法是为了防止过拟合&#xff0c;提高模型的泛化能力而提出的重要技术。本篇博客将介绍剪枝处理的概念、预剪枝和后剪…

《优化接口设计的思路》系列:第九篇—用好缓存,让你的接口速度飞起来

一、前言 大家好&#xff01;我是sum墨&#xff0c;一个一线的底层码农&#xff0c;平时喜欢研究和思考一些技术相关的问题并整理成文&#xff0c;限于本人水平&#xff0c;如果文章和代码有表述不当之处&#xff0c;还请不吝赐教。 作为一名从业已达六年的老码农&#xff0c…

vue2 自定义 v-model (model选项的使用)

效果预览 model 选项的语法 每个组件上只能有一个 v-model。v-model 默认会占用名为 value 的 prop 和名为 input 的事件&#xff0c;即 model 选项的默认值为 model: {prop: "value",event: "input",},通过修改 model 选项&#xff0c;即可自定义v-model …