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

目录

构造函数补充

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

赋值运算符重载函数

运算符重载函数介绍

运算符重载函数的使用

赋值运算符重载函数

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

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

重载前置++和后置++

前置++

后置++

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

流插入运算符<<重载

流提取运算符>>重载

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.mfbz.cn/a/482326.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ngrok实现内网穿透

在使用jenkins进行自动化部署时&#xff0c;需要设置github的webhook钩子来触发构建&#xff0c;由于jenkins运行在自己的电脑上&#xff0c;因此需要通过内网穿透来接受http请求。 Install ngrok via Homebrew with the following command: brew install ngrok/ngrok/ngrokP…

微信小程序开发学习笔记——4.2showModal和showLoading界面交互操作

>>跟着b站up主“咸虾米_”学习微信小程序开发中&#xff0c;把学习记录存到这方便后续查找。 课程连接&#xff1a;https://www.bilibili.com/video/BV19G4y1K74d?p27&vd_source9b149469177ab5fdc47515e14cf3cf74 一、showModal 显示模态对话框 1、属性 https:/…

电商爬虫系统|电商数据采集|电商API商品数据采集

1、基本的说明 当初为了在几个电商网站抓取商品信息数据搭建的系统。该系统主要用来抓取电商网站上面的一百个左右品类的商品的价格信息、商品信息和折扣信息等。抓取的电商网站主要是某宝和某东。其他的电商网站抓取信息的方式无外乎这两种。跟其他的示例代码不同&#xff0c…

【redis】服务器架构演进

架构演进 单机架构应用数据分离架构应⽤服务集群架构读写分离 / 主从分离架构冷热分离架构垂直分库微服务架构 单机架构 所有的应用服务、业务所需的数据、业务处理等都在一台服务器上。 在初期&#xff0c;用户访问量很少&#xff0c;对服务器的的性能和安全没有很高的要求&am…

抖音视频无水印批量下载软件|爬虫视频采集工具

抖音视频无水印批量下载软件&#xff0c;轻松实现视频提取和下载 概述&#xff1a; 想要快速、方便地提取和下载抖音视频无水印&#xff1f;我们的抖音视频无水印批量下载软件将是您的得力助手&#xff01;不仅支持通过关键词批量提取视频&#xff0c;还可以针对特定视频进行提…

Linux安装Nacos

安装前必要准备 准备Java环境 &#xff0c;8以上的版本&#xff0c;mysql&#xff08;集群相关信息&#xff09;&#xff0c;nginx&#xff08;进行代理&#xff09; 安装Nacos 首先我们要有一个nacos的包&#xff0c;我们可以在线下载&#xff0c;也可以提前下载好&#xf…

IRIS 和 Caché 是什么关系

我们都知道真正一个数据库通常是 2 个部分组成的&#xff0c;存储和进程。 Cach Cach 的定义就是一个数据库&#xff0c;在这个数据库中有存储和进程。 与我们常用的 MySQL 来说&#xff0c;我们安装好 MySQL 后我们就可以通过客户端进行连接了&#xff0c;同时我们还可以通过…

二叉树的链式结构和遍历(下)

又见面了&#xff0c;小伙伴们。今天我们继续来学习二叉树&#xff0c;今天的内容相对来说比较容易理解&#xff0c;前提是需要你们自己动手画图才会好理解。眼过千遍不如手过一遍。所以小伙伴们要多动手哦。直接开始今天的学习吧 1.二叉树链式结构的实现 1.1 前置说明 在学习…

Stability AI发布Stable Video 3D模型:可从单张图像创建多视图3D视频,视频扩散模型史诗级提升!

Stability AI发布了Stable Video 3D (SV3D)&#xff0c;这是一种基于稳定视频扩散的生成模型&#xff0c;推动了3D技术领域的发展&#xff0c;并大大提高了质量和视图一致性。 该版本有两个版本: SV3D_u:该变体基于单图像输入生成轨道视频&#xff0c;无需相机调节。 SV3D_p:扩…

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

子组件中被Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。 说明&#xff1a; 从API version 9开始&#xff0c;该装饰器支持在ArkTS卡片中使用。 概述 Link装饰的变量与其父组件中的数据源共享相同的值。 限制条件 Link装饰器不能在Entry装饰的自定义组件中使用…

伊理威科技:抖音开网店新手刚做选啥品

在数字浪潮中&#xff0c;抖音不仅是展示才艺的舞台&#xff0c;更是创业者的新天地。新手若想在这片热土上开垦网店&#xff0c;选品便是首要课题。选择产品如同种下希望的种子&#xff0c;既要考量土壤肥沃度&#xff0c;也得预测风雨适宜期。 兴趣与专长是选品的罗盘。热爱所…

STM32之HAL开发——RCC外设CubeMX配置时钟

RCC外设介绍 RCC是Reset and Clock Control (复位和时钟控制)的缩写&#xff0c;它是STM32内部的一个重要外设&#xff0c;负责管理各种时钟源和时钟分频&#xff0c;以及为各个外设提供时钟使能。RCC模块可以通过寄存器操作或者库函数来配置。 RCC是复位和时钟控制模块&#…

GeoAI 简明教程

想象一下&#xff0c;能够在野火发生后立即发现它&#xff0c;可视化全球人口变化&#xff0c;或者立即从地图中提取线条。 GeoAI&#xff0c;即地理空间人工智能&#xff0c;是指地理信息系统 (GIS)、人工智能 (AI) 和机器学习 (ML) 的交叉点。 这个领域正在彻底改变我们与世界…

数据结构 - 二叉树非递归遍历

文章目录 前言一、前序二、中序三、后序 前言 本文实现二叉树的前中后的非递归遍历&#xff0c;使用栈来模拟递归。 文字有点简略&#xff0c;需要看图和代码理解 树节点&#xff1a; typedef char DATA; //树节点 typedef struct Node {DATA data; //数据struct Node* left…

基于springboot+vue的物资仓储物流管理系统(源码+论文)

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;spring…

数据治理的迷失:揭开“屎上雕花”现象的真相

数据治理是企业信息化建设的核心环节&#xff0c;它直接关系到数据的质量、安全性和价值实现。然而&#xff0c;在实际操作中&#xff0c;不少企业却陷入了“屎上雕花”的误区&#xff0c;即在数据本身存在问题的情况下&#xff0c;试图通过表面的修饰来提升数据的外在表现&…

QT:三大特性

QT的三大特性&#xff1a; 1、信号与槽 2、内存管理 3、事件处理 1、信号与槽 当信号产生时&#xff0c;就会自动调用绑定的槽函数。 自定义信号: 类中需要添加O_OBJECT宏 声明: signals标签之下进行声明 定义&#xff1a; 信号不需要定义 …

使用 PyOpenGL 进行 2D 图形渲染总结

一、说明 OpenGL是一个广泛使用的开放式跨平台实时 3D 图形库&#xff0c;开发于二十多年前。它提供了一个低级API&#xff0c;允许开发人员以统一的方式访问图形硬件。在开发需要硬件加速且需要在不同平台上运行的复杂 2D 或 3D 应用程序时&#xff0c;它是首选平台。它可以在…

Day 14 JDBC

JDBC 1、简单入门 Statement2、preparedStatement3、主键回显4、批量操作5、事务6、Druid6.1 工具类V16.2 工具类V26.3 1、简单入门 Statement 步骤: 1、注册驱动 2、创建连接 3、创建 Statement对象 4、编写sql语句 并且发送sql语句获得结果集 5、解析结果集 6、释放资源 注意…

1、Dev软件的安装

预先善其事,必先利其器,想要学习编程语言的第一步就是学会使用编译软件,在这里我们所使用的编译软件为 Dev-cpp 5.11 ,在这一章节,我们将讲述如何下载并安 Dev-cpp 5.11。 一、下载 首先,我们要先学会下载 Dev-cpp 5.11,这里我们点击:Dev-cpp 5.11,即可完成下载,注…
最新文章