【C++】3.类和对象(中)

news2024/11/25 16:27:41

1.类的6个默认成员函数

一个类什么都没有不是空类 我们没有写相关函数 但编译器会自动生成6个默认函数

2.构造函数

1°概念

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,保证每个数据成员都有一个合适的初始值,并且在对象的生命周期内只调用一次。

#include <iostream>
using namespace std;

class Date
{
public:
    //构造函数->初始化函数
    //构造函数->在对象构造时调用的函数 这个函数完成初始化工作
    //构造函数可以重载
    Date(int year, int month, int day)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    //不想传参 默认初始化
    Date()
    {
        _year = 0;
        _month = 1;
        _day = 1;
    }
    void Print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    //对象实例化时自动调用
    Date d1(2023,2,10);//有参
    d1.Print();
    Date d2;//无参 不能加括号 
    d2.Print();
    return 0;
}

这样写有参和无参就很傻 能不能合并?

可以 利用缺省函数 当你不传参就可以用缺省值进行初始化

Date(int year = 0, int month = 1, int day = 1)
{
    _year = year;
    _month = month;
    _day = day;
}

2°特性

构造函数是特殊的成员函数,需要注意的是,构造函数的虽然名称叫构造,但是需要注意的是构造函数的主要任务并不是开空间创建对象,而是初始化对象

  • 函数名与类名相同。
  • 无返回值。
  • 对象实例化时编译器自动调用对应的构造函数。
  • 构造函数可以重载。(无参和带参)
  • 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

调用默认构造函数:1.无参 2.全缺省 3.编译器自动生成的

特点:不用传参 三个只能有一个

无参初始化和全缺省不能同时存在 调用时会有歧义

  • 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数。

  • 成员变量的命名风格 有_ 初始化时year = year就很傻 需要区分

我们会自己实现构造函数 那么编译器的构造函数是不是没有用?

有用 存在即合理

#include <iostream>
using namespace std;

class Time
{
public:
    Time()
    {
        _hour = 0;
        _minute = 0;
        _second = 0;
        cout << "Time()" << endl;
    }
private:
    int _hour;
    int _minute;
    int _second;
};

class Date
{
public:
    //Date(int year, int month, int day)
    //{
    //	_year = year;
    //	_month = month;
    //	_day = day;
    //}
    //Date()
    //{
    //	_year = 0;
    //	_month = 1;
    //	_day = 1;
    //}
    void Print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
    Time _t;
};

int main()
{
    Date d1;//调用编译器自动生成的
    d1.Print();//随机值 并不是什么都没有干
    //1°针对内置类型的成员变量没有做处理
    //2°针对自定义类型的成员变量 调用它的构造函数初始化/析构
    //自己定义了一个初始化的 编译器不会再帮你定义
    //比如定义了一个要传参的 不传参的也要自己定义
    return 0;
}

3.析构函数

1°概念

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

2°特性

  • 析构函数名是在类名前加上字符 ~
  • 无参数无返回值。
  • 一个类有且只有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。
  • 对象生命周期结束时,C++编译系统系统自动调用析构函数。
  • 编译器生成的默认析构函数,对会自定类型成员调用它的析构函数。
#include <iostream>
using namespace std;

class Date
{
public:
    Date(int year = 0, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
        cout << "Date()" << endl;
    }
    void Print()
    {
        cout << _year << "/" << _month << "/" << _day << endl;
    }
    //~析构
    ~Date()
    {
        cout << "~Date()" << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};

class Stack
{
public:
    Stack(int n = 10)
    {
        _a = (int*)malloc(sizeof(int) * n);
        _size = 0;
        _capacity = n;
        cout << "Stack()" << endl;
    }
    //清理动态开辟空间
    ~Stack()
    {
        free(_a);
        _a = nullptr;
        _size = 0;
        _capacity = 0;
        cout << "free:_a" << endl;
    }
private:
    int* _a;
    int  _size;
    int  _capacity;
};

int main()
{
    //析构:对象生命周期到了以后 自动调用 完成对象的资源清理工作 不是完成d1和d2的销毁
    Date d1;
    Date d2;
    Stack s1;
    Stack s2;
    //析构是倒着析构的 后进去的先析构
    return 0;
}

4.拷贝构造函数

1°概念

构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

2°特征

  • 拷贝构造函数是构造函数的一个重载形式。
  • 拷贝构造函数的参数只有一个且必须使用引用传参使用传值方式会引发无穷递归调用。(传参的过程是一个赋值过程 传值的话会再次调用构造函数)

  • 若未显示定义,系统生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝我们叫做浅拷贝,或者值拷贝。
  • 既然有了默认生成的拷贝函数 那么我们还需要自己实现拷贝构造函数吗? 需要 有的地方需要深拷贝
#include <iostream>
using namespace std;

class Date
{
public:
    //构造函数
    Date(int year = 0, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    //Date d2(d1) 拷贝构造函数
    //Date(Date d) 无穷递归(画图)
    //调用的时候调用的是拷贝构造
    //调用的过程中先传参 
    //传参的过程是一个初始化过程 赋值
    //赋值完后又再次进行拷贝构造

    //没有引用
    //Date d = d1;(然后再次进行拷贝构造)
    //引用解决这个问题
    //Date& d = d1;
    //d是d1的别名
    Date(const Date& d)//const可保护 不可改变原来的d1
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1(2023,2,10);
    Date d2(d1);//拷贝构造
    d1.Print();
    d2.Print();
    return 0;
}

5.赋值运算符重载

1°运算符重载

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

函数名字为:关键字operator后面接需要重载的运算符符号。

函数原型:返回值类型 operator操作符(参数列表)

注意:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型或者枚举类型的操作数
  • 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的操作符有一个默认的形参this,限定为第一个形参
  • .* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

编译器会自己做一些事

比如说实现了operator==这个功能 最后调用函数的时候

直接d1 == d2;判断即可 编译器会转化成operator(d1,d2);

#include <iostream>
using namespace std;

class Date
{
public:
    Date(int year = 0, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    Date(const Date& d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
//private:
    int _year;
    int _month;
    int _day;
};

//运算符有几个操作数:operator重载的函数就有几个参数
bool operator==(const Date& d1, const Date& d2)
{
    //这里需要去掉private
    return d1._year  == d2._year
        && d1._month == d2._month
        && d1._day    == d2._day;you
}

//自定义类型是不能用运算符的 要用就实现重载函数
//自定义类型用的时候等价于调用这个重载函数

int main()
{
    Date d1(2023, 2, 10);
    Date d2(2023, 2, 11);
    //比如Date的对象想比较大小相等
    //自定义类型 还是要自己来比
    //运算符函数重载
    d1 == d2;//编译如何调用->这里编译器会转换成opeartopr==(d1,d2); 就是一个函数调用
    //operator==(d1, d2);//可读性差
    //这两个是一样的
    return 0;
}

如何不去掉private并且实现功能?

写一个成员函数 注意有一个隐含的this指针 所以参数只用写一个

外面可以访问public 里面public可以访问private

解决在外面访问不了private的问题

写:bool operator==(const Date& d)

隐含:bool operator==(Date*this,const Date& d)

写:d1 == d2;//d1给到this指针 d2给到别名d进行判断

隐含:d1.opertor==(&d1,d2);

真正:operator==(d2)

#include <iostream>
using namespace std;

class Date
{
public:
    Date(int year = 0, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    Date(const Date& d)
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    //d1 == d2;
    //隐含:d1.operator==(&d1,d2);
    //&d1就是this指针 d2给到别名d
    //真正写:operator==(d2);
    bool operator==(const Date& d)//(Date* this,const Date& d)
    {
        //这里需要去掉private
        return _year  == d._year
            && _month == d._month
            && _day   == d._day;
    }
    //d1 > d2
    bool operator>(const Date& d)
    {
        if (_year > d._year)
            return true;
        else if (_year == d._year && _month > d._month)
            return true;
        else if (_year == d._year && _month == d._month && _day > d._day)
            return true;
        else
            return false;
    }
private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1(2023, 2, 10);
    Date d2(2023, 2, 11);
    cout << (d1 == d2) << endl;
    cout << (d1 > d2) << endl;
    return 0;
}

2°赋值运算符重载

class Date
{ 
public:
  Date(int year = 1900, int month = 1, int day = 1)
  {
    _year = year;
    _month = month;
    _day = day;
  }
 
  Date (const Date& d)
  {
    _year = d._year;
    _month = d._month;
    _day = d._day;
  }
 
  Date& operator=(const Date& d)
  {
    if(this != &d)
    {
      _year = d._year;
      _month = d._month;
      _day = d._day;
    }
  }
private:
  int _year;
  int _month;
  int _day;
};
  • 参数类型
  • 返回值
  • 检测是否自己给自己赋值
  • 返回*this
  • 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。

和拷贝构造一样 编译器默认生成的都是值拷贝 有时候需要深拷贝 我们还是要自己实现

6.浅拷贝vs深拷贝

我们如果没有实现默认函数

编译器会默认生成相关函数

  • 编译器生成的默认构造函数和析构函数

    针对成员变量:内置类型就不处理 自定义类型会调这个成员的构造和析构

  • 编译器生成拷贝构造和operator= 会完成按字节的值拷贝(浅拷贝)

    浅拷贝:将对象按一个一个字节拷贝过去 也就是说有些类

    我们是不需要去实现拷贝构造和operate=的 因为编译器默认生成的就够用

所以 我们写拷贝构造的意义是什么?

下面程序会崩溃 原因是什么?

#include <iostream>
using namespace std;

class Stack
{
public:
    //构造
    Stack(int n = 10)
    {
        _a = (int*)malloc(sizeof(int) * n);
        _size = 0;
        _capacity = n;
        cout << "Stack()" << endl;
    }
    //析构
    ~Stack()
    {
        if (_a != nullptr)//还是会崩溃 制空的时st2这个指针 还是会找到st1
        {
            free(_a);
            _a = nullptr;
            _size = 0;
            _capacity = 0;
            cout << "free:_a" << endl;
        }
    }
private:
    int* _a;
    int  _size;
    int  _capacity;
};

int main()
{
    Stack st1(10);
    Stack st2(st1);//拷贝构造
    Stack st3(30);//构造
    st1 = st3;//赋值 operator=
    //浅拷贝问题
    //当赋值和拷贝构造的时候 指针赋给指针 此时两个指针指向同一块空间
    //出作用域后开始析构
    //st2析构 free(st2._a) st1析构 free(st1._a) 再次释放同一块空间 崩溃
    //崩溃原因:同一块空间释放两次
    //如果不写析构 内存泄漏
    //解决:深拷贝 值拷贝过来并且指向一块新的空间
}

浅:p1 p2指向同一块空间 当调用析构函数的时候导致多次释放空间

深:多次释放空间也不会受影响 两块空间不同

所以我们有时候要自己实现拷贝构造或者operator=

7.const成员

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

  1. const对象可以调用非const成员函数吗?
  2. 非const对象可以调用const成员函数吗?
  3. const成员函数内可以调用其它的非const成员函数吗?
  4. 非const成员函数内可以调用其它的const成员函数吗?

1:可以 2:不可以 3:可以 4:不可以

const->const 不能放大权限

非const->非const/const

  • const Date* p1
  • Date const* p2
  • Date* const p3

三者const修饰的分别是什么

1和2的const修饰指针指向的对象

3的const修饰的是指针本身

看const后面是什么即可

  • const该如何使用呢?

当this指向的内容在函数里面不再修改 就可以加上const

8.取地址和const取地址操作符重载

#include <iostream>
using namespace std;

class Date
{
public:
    Date(int year = 0, int month = 1, int day = 1)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    void Print() const
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }
    //&重载
    Date* operator&()
    {
        cout << "operator&()" << endl;
        return this;
    }
    //const&
    const Date* operator&() const //最前面const保持类型一样才能返回
    {
        cout << "operator&() const" << endl;
        return this;
    }
private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1;
    Date d2;
    const Date d3;
    //取地址默认可以用 自己写也可以
    cout << &d1 << endl;
    cout << &d2 << endl;
    cout << &d3 << endl;//放大了 const到非const
    //实际上不用写 编译器默认写
    //返回空可以写
    return 0;
}

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容

9.实现一个完整的日期类

声明和定义分开

Date.h

#include <iostream>
using namespace std;

class Date
{
public:
    //得到每个月的天数(自己写)
    int GetMonthDay(int year, int month) const;
    //构造(有默认的)
    Date(int year = 0, int month = 1, int day = 1);
    //拷贝构造(有默认的)
    Date(const Date& d);
    //赋值重载(有默认的)
    Date& operator=(const Date& d);
    //运算操作符重载(自己写)
    bool operator<(const Date& d) const;
    bool operator==(const Date& d) const;
    bool operator<=(const Date& d) const;
    bool operator>(const Date& d) const;
    bool operator>=(const Date& d) const;
    bool operator!=(const Date& d) const;
    Date& operator+=(int day);
    Date operator+(int day) const;
    Date& operator-=(int day);
    Date operator-(int day) const;
    Date& operator++();
    Date operator++(int);
    Date& operator--();
    Date operator--(int);
    //日期-日期(自己写)
    int operator-(const Date& d) const;
    //打印(自己写)
    void Print() const;
private:
    int _year;
    int _month;
    int _day;
};

1°GetMonthDay

int Date::GetMonthDay(int year, int month) const
{
    static int monthDays[13] = { 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 29;
    }
    return monthDays[month];
}

利用数组来获取

2°构造函数

Date::Date(int year, int month, int day) //缺省参数声明给了就行
{
    if (year >= 0
        && month >= 1 && month <= 12
        && day >= 1 && day <= GetMonthDay(year, month))
    {
        _year = year;
        _month = month;
        _day = day;
    }
    else
    {
        cout << "非法日期" << endl;
    }
}

要在范围内初始化

3°拷贝构造

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

this指针不能加const 对应的年月日会被修改

4°operator=

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

引用返回 提高效率 加一个判断条件 防止自己给自己赋值

5°operator<

bool Date::operator<(const Date& d) const
{
    if (_year < d._year)
        return true;
    else if (_year == d._year && _month < d._month)
        return true;
    else if (_year == d._year && _month == d._month && _day < d._day)
        return true;
    else
        return false;
}

年小就是小

年等就比月 月小就是小

年等月等就比天 天小就是小

6°operator==

bool Date::operator==(const Date& d) const
{
    return _year == d._year && _month == d._month && _day == d._day;
}

7°operator<=

bool Date::operator<=(const Date& d) const
{
    return *this < d || *this == d;
}

小于等于->小于或者等于->复用已经实现的<和==

注意是看*this this指向的年月日

8°operator>

bool Date::operator>(const Date& d) const
{
    return !(*this <= d);
}

复用<= 加上取反

9°operator!=

bool Date::operator!=(const Date& d) const
{
    return !(*this == d);
}

复用== 加上取反

10°operator+=

Date& Date::operator+=(int day)
{
    if (day < 0)
    {
        return *this -= -day;
    }
    _day += day;
    while (_day > GetMonthDay(_year, _month))
    {
        _day -= GetMonthDay(_year, _month);
        ++_month;
        if (_month == 13)
        {
            ++_year;
            _month = 1;
        }
    }
    return *this;
}

先加天 再减去本月的天 月++ 如果月==13 那就年++ 然后月变为1

循环条件是天数>本月的天数

11°operator+

Date Date::operator+(int day) const
{
    Date ret(*this);
    ret += day;
    return ret;
}

+和+=/-和-=的区别:

+/-是在d1上加减 但d1不变 需要中间变量

+=/-=直接在d1上加减

拷贝构造给ret+直接复用

12°operator-=

Date& Date::operator-=(int day)
{
    if (day < 0)
    {
        return *this += -day;
    }
    _day -= day;
    while (_day < 0)
    {
        --_month;
        if (_month == 0)
        {
            --_year;
            _month = 12;
        }
        _day += GetMonthDay(_year, _month);
    }
    return *this;
}

先减天 天如果小于0 就月-- 如果月==0 就减年 再加上这个月的天数

循环条件就是天<0

13°operator-

Date Date::operator-(int day) const
{
    Date ret(*this);
    ret -= day;
    return ret;
}

复用即可

注意operator+和operator-都不能引用返回

本身ret这个变量在栈上 出了这个函数就销毁了 不能再使用这块空间

14°opeartor++

Date& Date::operator++()//前置++
{
    *this += 1;
    return *this;
}

Date Date::operator++(int)//后置++
{
    Date tmp(*this);
    *this += 1;
    return tmp;
}

后置加一个int参数 区分前置++和后置++

15°operator--

Date& Date::operator--()
{
    *this -= 1;
    return *this;
}

Date Date::operator--(int)
{
    Date tmp = *this;
    *this -= 1;
    return tmp;
}

16°日期-日期

看成小的日期不断++并计数

int Date::operator-(const Date& d) const//const Date* this
{
    int flag = 1;
    Date max = *this;
    Date min = d;
    if (*this < d)//这里小于开始调不动 const <那里是非const
    {
        max = d;
        min = *this;
        flag = -1;
    }
    int n = 0;
    while (min != max)
    {
        ++min;
        ++n;
    }
    return n * flag;
}

17°打印

void Date::Print() const
{
    cout << _year << "-" << _month << "-" << _day << endl;
}

18°const添加情况

结论:什么时候会给成员函数加const-> 只要成员函数中不需要改变成员变量最好都加上const 好处:const对象和非const对象都可以调用 加在参数后面的const修饰的是隐含的*this

10.日期类代码

Date.h

#include <iostream>
using namespace std;

class Date
{
public:
    //得到每个月的天数(自己写)
    int GetMonthDay(int year, int month) const;
    //构造(有默认的)
    Date(int year = 0, int month = 1, int day = 1);
    //拷贝构造(有默认的)
    Date(const Date& d);
    //赋值重载(有默认的)
    Date& operator=(const Date& d);
    //运算操作符重载(自己写)
    bool operator<(const Date& d) const;
    bool operator==(const Date& d) const;
    bool operator<=(const Date& d) const;
    bool operator>(const Date& d) const;
    bool operator>=(const Date& d) const;
    bool operator!=(const Date& d) const;
    Date& operator+=(int day);
    Date operator+(int day) const;
    Date& operator-=(int day);
    Date operator-(int day) const;
    Date& operator++();
    Date operator++(int);
    Date& operator--();
    Date operator--(int);
    //日期-日期(自己写)
    int operator-(const Date& d) const;
    //打印(自己写)
    void Print() const;
private:
    int _year;
    int _month;
    int _day;
};

Date.cpp

#define _CRT_SECURE_NO_WARNINGS 1

#include "Date.h"

//加一个Date::类
int Date::GetMonthDay(int year, int month) const
{
    static int monthDays[13] = { 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 29;
    }
    return monthDays[month];
}

Date::Date(int year, int month, int day) //缺省参数声明给了就行
{
    if (year >= 0
        && month >= 1 && month <= 12
        && day >= 1 && day <= GetMonthDay(year, month))
    {
        _year = year;
        _month = month;
        _day = day;
    }
    else
    {
        cout << "非法日期" << endl;
    }
}

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

bool Date::operator<(const Date& d) const//*this不修改
{
    if (_year < d._year)
        return true;
    else if (_year == d._year && _month < d._month)
        return true;
    else if (_year == d._year && _month == d._month && _day < d._day)
        return true;
    else
        return false;
}

Date& Date::operator=(const Date& d)
{
    if (this != &d)//防止自己给自己赋值
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }
    return *this;//返回的是对应的年月日 *this
}

bool Date::operator==(const Date& d) const
{
    return _year == d._year && _month == d._month && _day == d._day;
}

bool Date::operator<=(const Date& d) const
{
    return *this < d || *this == d;
}

bool Date::operator>(const Date& d) const
{
    return !(*this <= d);
}

bool Date::operator>=(const Date& d) const
{
    return !(*this < d);
}
bool Date::operator!=(const Date& d) const
{
    return !(*this == d);
}
Date& Date::operator+=(int day)
{
    if (day < 0)
    {
        return *this -= -day;
    }
    _day += day;
    while (_day > GetMonthDay(_year, _month))
    {
        _day -= GetMonthDay(_year, _month);
        ++_month;
        if (_month == 13)
        {
            ++_year;
            _month = 1;
        }
    }
    return *this;
}

Date Date::operator+(int day) const
{
    Date ret(*this);
    ret += day;
    return ret;
}

Date& Date::operator-=(int day)
{
    if (day < 0)
    {
        return *this += -day;
    }
    _day -= day;
    while (_day < 0)
    {
        --_month;
        if (_month == 0)
        {
            --_year;
            _month = 12;
        }
        _day += GetMonthDay(_year, _month);
    }
    return *this;
}

Date Date::operator-(int day) const
{
    Date ret(*this);
    ret -= day;
    return ret;
}

Date& Date::operator++()
{
    *this += 1;
    return *this;
}

Date Date::operator++(int)
{
    Date tmp(*this);
    *this += 1;
    return tmp;
}

Date& Date::operator--()
{
    *this -= 1;
    return *this;
}

Date Date::operator--(int)
{
    Date tmp = *this;
    *this -= 1;
    return tmp;
}

int Date::operator-(const Date& d) const//const Date* this
{
    int flag = 1;
    Date max = *this;
    Date min = d;
    if (*this < d)//这里小于开始调不动 const <那里是非const
    {
        max = d;
        min = *this;
        flag = -1;
    }
    int n = 0;
    while (min != max)
    {
        ++min;
        ++n;
    }
    return n * flag;
}

void Date::Print() const
{
    cout << _year << "-" << _month << "-" << _day << endl;
}

Test.cpp

#include "Date.h"
int main()
{
    Date d1(2022, 2, 20);
    d1.Print();
    Date d2(2023, 2, 10);
    d2.Print();
    Date d3(2023, 4, 20);
    //只要实现一个<=或者>= 其他就可以进行复用
    cout << (d1 < d2) << endl;
    cout << (d1 > d2) << endl;
    cout << (d1 == d2) << endl;
    cout << (d1 != d2) << endl;
    cout << (d1 <= d2) << endl;
    cout << (d1 >= d2) << endl;
    
    //+
    Date d4 = d1 + 1000;
    d4.Print();
    
    //+=
    d2 += 1000;
    d2.Print();
    
    //=
    d3 = d1;//这是operator= d3已经存在 赋值操作
    d3.Print();

    //-
    Date d5 = d1 - 10;
    d5.Print();

    //-=
    Date d6(d1);
    d6 -= 10;
    d6.Print();

    //++
    Date d7= ++d1;
    d7.Print();
    Date d8= d1++;
    d8.Print();
    d1.Print();
    
    //--
    Date d9 = --d1;
    Date d10 = d1--;
    d9.Print();
    d10.Print();
    d1.Print();

    //日期-日期
    d1.Print();
    d2.Print();
    cout << d1 - d2 << endl;
    cout << d2 - d1 << endl;

    return 0;
}

【C++】3.类和对象(中) 完

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

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

相关文章

微服务架构中的多级缓存设计还有人不懂?

今天我们来聊聊缓存这个话题&#xff0c;看看在微服务环境下如何设计有效的多级缓存架构。主要涉及三方面内容&#xff1a; Web 应用的客户端缓存&#xff1b;应用层静态资源缓存&#xff1b;服务层多级缓存。 首先&#xff0c;咱们先讲解微服务架构的多级缓存设计。 微服务…

ElasticSearch 学习笔记总结(一)

文章目录一、 数据的 分类二、 ElasticSearch 介绍三、 ElasticSearch 搭建四、正排索引 和 倒排索引五、ES HTTP 索引 操作六、ES HTTP 文档 操作七、ES HTTP 查询数据1. 条件查询2. 分页查询3. 排序查询4. 多条件查询5. 全文检索 完全匹配 高亮显示6. 聚合查询八、 ES HTTP 映…

2.22JVM

一.学习目标1)JVM内存区域划分2)JVM的类加载机制3)JVM的垃圾回收1.JVM执行流程程序在执行之前先要把Java代码转换为字节码(.class),JVM首先需要通过一定的方式类加载器把文件加载到运行时数据区,而字节码文件是JVM的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特…

【11-JVM面试专题-说说你知道的垃圾回收算法?垃圾回收器你知道吗?CMS、G1和ZGC垃圾回收器你有过了解吗?】

JVM面试专题-说说你知道的垃圾回收算法&#xff1f;垃圾回收器你知道吗&#xff1f;CMS、G1和ZGC垃圾回收器你有过了解吗&#xff1f; JVM面试专题-说说你知道的垃圾回收算法&#xff1f;垃圾回收器你知道吗&#xff1f;CMS、G1和ZGC垃圾回收器你有过了解吗&#xff1f;你掌握的…

Wincc Flexible smart V4触摸屏软件中批量导入PLC变量的具体方法示例(无需单个添加)

Wincc Flexible smart V4触摸屏软件中批量导入PLC变量的具体方法示例(无需单个添加) 具体操作步骤可参考以下例子中的内容: 打开STEP7-MicroWin smart软件,编辑自己的PLC程序(这里以一个简单的启保停程序为例), 如下图所示,打开Wincc Flexible smart V4触摸屏软件,新建…

双指针 (C/C++)

1. 双指针 双指针算法的核心思想&#xff1a;将暴力解法的时间复杂度&#xff0c;通常是O(N*N)&#xff0c;通过某种特殊的性质优化到O(N)。 做题思路&#xff1a;先想想暴力解法的思路&#xff0c;然后分析这道题的特殊性质&#xff0c;一般是单调性。然后得出双指针算法的思路…

微信协议网页版微信协议解析

最近在做个微信机器人&#xff0c;所以研究了网页版的微信协议及相关接口&#xff0c;在这里简单总结一下。从表面上看&#xff0c;对于网页版微信我们的使用流程是这样的&#xff1a;很简单&#xff0c;只有四步&#xff0c;但如果细化到内里细节的话&#xff0c;上面这简单四…

一文带你快速入门zabbix6.0的日常操作

文章目录前言一. zabbix基本操作入门1.1 登录和配置用户1.1.1 登录zabbix1.1.2 防爆力破解机制1.1.3 创建用户1.1.4 创建报警媒介1.1.5 设置 权限选项卡1.1.6 设置用户的访问主机权限1.2 新建主机1.2.1 添加主机1.2.2 关于添加的信息注释1.3 新增监控项1.3.1 添加监控项1.3.2 配…

若依系统如何集成qq邮件发送【超详细,建议收藏】

若依系统的部署博主就不在这儿阐述了&#xff0c;默认大家的电脑已经部署好了若依系统&#xff0c;这里直接开始集成邮件系统&#xff0c;首先我们得需要对qq邮箱进行配置&#xff1b;一套学不会你来打我&#x1f600;&#xff1b; 一、开启我们的qq邮箱发送邮件的配置 1、先进…

Qt音视频开发16-通用悬浮按钮工具栏的设计

一、前言 通用悬浮按钮工具栏这个功能经过了好几个版本的迭代&#xff0c;一开始设计的时候是写在视频控件widget窗体中&#xff0c;当时功能简单就放一排按钮在顶部悬浮widget中就好&#xff0c;随着用户需求的变化&#xff0c;用户需要自定义悬浮条的要求越发强烈&#xff0…

K_A12_031 基于STM32等单片机驱动TEMT6000环境光传感器 串口与OLED0.96双显示

K_A12_031 基于STM32等单片机驱动TEMT6000环境光传感器 串口与OLED0.96双显示一、资源说明二、基本参数参数引脚说明三、驱动说明IIC地址/采集通道选择/时序对应程序:四、部分代码说明1、接线引脚定义1.1、STC89C52RCTEMT6000环境光传感器模块1.2、STM32F103C8T6TEMT6000环境光…

ZCMU--5009: 龙虎斗

轩轩和开开正在玩一款叫《龙虎斗》的游戏&#xff0c;游戏的棋盘是一条线段&#xff0c;线段上有n个兵营(自左至右编号1~n)&#xff0c;相邻编号的兵营之间相隔1厘米&#xff0c;即棋盘为长度为n-1厘米的线段。i号兵营里有ci位工兵。 下面图1为n 6的示例: 轩轩在左侧&#xf…

如何通过IP找到地址?

在我们印象中&#xff0c;我们都知道可以通过 IP 地址找到某个人。但当我们细想一下&#xff0c;我们会发现其实 IP 地址与地理位置并不是直接相关的。那我们到底是如何通过 IP 地址找到地址的呢&#xff1f;答案是&#xff1a;通过自治系统&#xff08;Autonomous System&…

大势前瞻!文旅还是短视频,你弯道超车风口在这了

三年前&#xff0c;新冠疫情的影响波及整个各行各业行业&#xff0c;互联网寒冬&#xff0c;房地产崩盘&#xff0c;教培团灭&#xff0c;在这样的背景下&#xff0c;行业都进入了发展“冰雪期”。老话说大疫后必有大变&#xff0c;如今风雪融化&#xff0c;万物复苏&#xff0…

day01_HTML常识

基础概念铺垫 认识网页 网页的组成&#xff1a;文字、图片、音频、视频、超链接一系列元素网页的本质&#xff1a;程序员写的代码通过浏览器转化&#xff08;解析和渲染&#xff09;成用户看到的网页网页是由网页元素组成的 &#xff0c; 这些元素是利用html标签描述出来&…

面试攻略,Java 基础面试 100 问(十八)

JAVA IO 包 JAVA NIO NIO 主要有三大核心部分&#xff1a;Channel(通道)&#xff0c;Buffer(缓冲区), Selector。 传统 IO 基于字节流和字 符流进行操作&#xff0c;而 NIO 基于 Channel 和 Buffer(缓冲区)进行操作&#xff0c;数据总是从通道读取到缓冲区 中&#xff0c;或者…

不可替代的测试人:一文解释探索性测试是什么

剧本式测试 随着软件技术的发展&#xff0c;软件开发过程中对软件测试的需求越来越庞大&#xff0c;从原来单纯的寻找软件缺陷&#xff0c;到后来的学习软件、挖掘软件中存在的缺陷、评估软件可用性、性能等等方面&#xff0c;软件测试发挥着重要作用。 考虑到软件测试如此的…

视频流截取保存到本地路径(打包jar包CMD运行)

需求&#xff1a;现在有一批https的监控视频流URL&#xff0c;需要对视频流进行每三秒截屏一次&#xff0c;并保存到本地路径&#xff0c;png格式&#xff0c;以当前时间命名。代码&#xff1a;import org.bytedeco.javacv.FFmpegFrameGrabber; import org.bytedeco.javacv.Fra…

微信小程序登录

小程序登录 | 微信开放文档 (qq.com)https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html 静默登录 首先需要在微信开发者工具中获取code(wx.login(Object object) | 微信开放文档 (qq.com)) 前端处理获取code其次&#xff1a;需要appid,se…