【C++】类和对象练习——日期类的实现
文章目录
- 前言
- 1. 日期的合法性判断
- 2. 日期+天数(+/+=)
- 2.1 +和+=的重载
- 2.2 对于两者复用的讨论
- 3. 前置++和后置++重载
- 4. 日期-天数(-/-=)
- 5. 前置- -和后置- -的重载
- 6. 日期-日期
- 7. 流插入<<重载
- 8. 流提取>>重载
- 9. 总结
- 10. 源码展示
前言
在上一篇文章我们学习类和对象的过程中,我们不是写了一个日期类嘛。
但是我们之前实现的日期类并不是很完整,我们只是借助它来帮大家学习类和对象的知识。
那这篇文章呢,我们就在之前的基础上,再增添一些功能,实现一个比较完整的日期类,作为一个练习,来帮助我们更好的理解我们之前学过的知识。
这是我们之前一起写的不太完整的日期类:
class Date
{
public://构造函数Date(int year = 1, 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;}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;return false;}bool operator<=(const Date& d){return *this == d || *this < d;}bool operator>(const Date& d){return !(*this <= d);}bool operator>=(const Date& d){return !(*this < d);}bool operator!=(const Date& d){return !(*this == d);}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}bool operator==(const Date& d){return _year == d._year&& _month == d._month&& _day == d._day;}//赋值重载(对于Date类用默认生成的就行)//d1=d2(this就是d1,d就是d2)/*Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}*/
private:int _year;int _month;int _day;
};
那这些实现过的函数我们就不再一一讲解了,大家不熟悉的话可以回顾一下上一篇文章。
另外呢,我们最终实现的是一个完整的日期类,那方便对代码进行维护和管理,以及对实现好的日期类进行测试,我们还是像之前写数据结构一样,放在多个文件中。
1. 日期的合法性判断
我们之前呢已经给该日期类写好了构造函数:
Date(int year = 1, int month = 1, int day = 1)
{_year = year;_month = month;_day = day;
}
并且还指定了缺省参数,那这样的话在实例化一个对象时我们就可以自己给对象指定初值,我们输入的日期是啥,该对象就会被初始化为对应的日期。
那现在有一个问题,如果我们实例化对象时给的日期不合法呢?比如:
void Datetest1()
{Date d1(2023, 2, 30);d1.Print();
}
不合法是不是也判断不出来啊。
那我们就对原来的构造函数做一些补充好吧,让它在给对象初始化的时候可以去判断一下对应的日期合不合法。
那要怎么判断呢?
给我们一个年月日,要判断是否合法,是不是要判断月在不在【1,12】之内以及天数有没有超过当前月的总天数啊。
但是某个月的天数是不是不好确定啊,不同月的天数不一样,而且要考虑平闰年。
那我们先来写一个获取某年某月天数的函数:
int Date::GetMonthDay(int year, int month)
{assert(month > 0 && month < 13);int MonthArr[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;}else{return MonthArr[month];}
}
不过多解释了,相信大家都能看懂。
那有了这个函数,我们就可以在构造函数中去判断了:
Date::Date(int year, int month, int day)
{if (month > 0 && month < 13 && day>0 && day <= GetMonthDay(year, month)){_year = year;_month = month;_day = day;}else{cout << "日期非法" << endl;_year = 1;_month = 1;_day = 1;}
}
如果合法了,我们再去赋值,不合法就还全搞成1,要不然就是随机值了。
那这时我们再用非法日期去初始化对象:
这样输入的日期不合法就提示了。
2. 日期+天数(+/+=)
我们说日期+日期是不是没啥意义啊,但是日期+一个天数是不是还有点意义,可以理解成这么多天之后是几几年几月几日。
2.1 +和+=的重载
所以接下来,我们要实现一个功能就是计算一个日期加了一个天数之后得到的日期:
那具体实现的思路呢可以这样搞:
首先我们想让自定义类型Date的对象直接和整型相加,这里肯定要对+
进行运算符重载。
我们拿到一个日期一个天数之后,先把天数加到日上,然后,判断此时的日是否超过了当前月的总天数(获取月份天数的函数我们之前已经实现过了),超过的话,就减去当前月的天数,然后月份++,那月份++有可能会超过12啊,一旦超过,就让年++,然后月置成1。
那当然这肯定是一个循环,当我们的日(day)不再大于当前月的天数,则此时得到的就是最终的日期。
我们来写一下代码:
Date& Date::operator+(int day)
{_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_year++;_month = 1;}}return *this;
}
这里*this出了作用域还在,所以可以返回引用。
那我们来测试一下:
我们看这个计算出来的日期确实是没问题的,但是d1+100,这里是+而不是+=,所以d1是不是不应该变啊,我们只是把d1+100得到的日期赋给了d2,但是现在d1也变了。
所以我们这样写其实实现的是啥,是不是+=啊。
所以我们这里重载的应该是+=:
Date& Date::operator+=(int day)
{_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_year++;_month = 1;}}return *this;
}
那+=还需要有返回值吗,🆗,最好还是带返回值,带返回值的话就可以支持这种情况:
d2 = d1 += 100;
那刚才实现的是+=,那+要这么搞?
是不是借助一个临时对象就可以搞定了啊,我们只要不改变*this就行了嘛。
Date Date::operator+(int day)
{Date tmp(*this);tmp._day += day;while (tmp._day > GetMonthDay(tmp._year, tmp._month)){tmp._day -= GetMonthDay(tmp._year, tmp._month);tmp._month++;if (tmp._month == 13){tmp._year++;tmp._month = 1;}}return tmp;
}
那就这样,测试一下:
🆗,这次d1没有被改变。
答案也没问题。
那对于+的重载:
大家有没有注意到我们没有返回引用,为什么?
因为我们返回的是啥,是不是tmp,而tmp是一个局部的对象,出了作用域就销毁了,所以我们不能返回引用。
那有时候呢,有的人为了这个地方能够返回引用,对tmp加了一个static
修饰,然后就返回引用。
大家思考一下这样可以吗?
我们试一下会发现:
第一次没问题,但我们第二次在调用+,是不是就错了啊。
第二次我们还是给d1+1000天,但结果却是2000天之后的。
为什么会这样?
原因就在于我们用了static。
我们创建的局部对象tmp被static修饰后,就不存在栈区了,而是放在静态区了,所以静态局部变量出作用域不会被销毁,而是保留在静态区,因此我们确实可以返回引用了。
但是,静态局部变量的初值是在编译期间就指定的,所以运行期间,不管我们再去调用几次函数,tmp都不会被重新初始化,而是保留上次的值,所以我们第二次的结果才会变成2000天之后。
之前有篇文章详解static的作用,大家可以看——链接: link
所以呢,这个地方我们不要为了能够返回引用而去随便加static。
2.2 对于两者复用的讨论
那除此之外:
大家看,+的重载与+=的重载,除了多一个临时的局部对象,其它的逻辑是不是一样啊,所以+里面是不是可以复用+=啊。
Date Date::operator+(int day)
{Date tmp(*this);tmp += day;return tmp;
}
这样是不是就行了啊:
那既然+可以复用+=,我们是不是也可以考虑先实现+,然后+=复用+呢?
当然可以。
Date Date::operator+(int day)
{Date tmp(*this);tmp._day += day;while (tmp._day > GetMonthDay(tmp._year, tmp._month)){tmp._day -= GetMonthDay(tmp._year, tmp._month);tmp._month++;if (tmp._month == 13){tmp._year++;tmp._month = 1;}}return tmp;
}
那这是我们自己写的+,现在我们+=来复用+。
Date& Date::operator+=(int day)
{*this = *this + day;return *this;
}
这样不就行了。
那再来思考:
到底是+复用+=好呢,还是+=复用+好呢?
🆗,那这里呢,其实是我们的第一种即+复用+=更好一点。
因为+里面创建临时对象有一次拷贝,返回的是值而不是引用又是一次拷贝。
那如果是+复用+=的话就只有+里有拷贝,但如果是+=复用+的话,是不是+=和+里都有拷贝了。
3. 前置++和后置++重载
刚重载了+和+=,那是不是还有前置++和后置++啊,那我们也来实现一下。
先来前置++吧:
来回忆一下前置++的特性是什么?
是不是先++,后使用啊,即返回的是++之后的值。
举个例子:
int a=5;
int b=++a;
首先不管前置++,还是后置++,a的值肯定都会+1,那现在
++a
前置,先++后使用,返回++之后的值,所以b也是6。
那我们来重载一下:
//前置++
Date& Date::operator++()
{*this += 1;return *this;
}
很简单,直接复用+=。
那我们再来重载后置++:
后置++呢,是先使用,后++,即返回++之前的值。那我们还是可以借助一个临时对象。
但是呢?
这里会发现有一个问题:
前置++和后置++没法区分啊,它们的参数和函数名是不是一样啊。
欸,那这怎么解决啊?
🆗,那当然是有办法的。
前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载。C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递(它的作用就是为了构成重载),编译器自动传递。
所以呢,这样搞就行了。
我们来实现一下:
//后置++
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}
🆗,我们就搞定了。
那实现完了,大家看一下:
前置++和后置++那个效率会高一点。
是不是前置啊,因为与后置相比,前置没有拷贝啊对不对。
所以,对于内置类型来说,大家可以认为前置后置没有区别;但是对于自定义类型来说,能用前置,尽量用前置++。
我们的前置++和后置++就搞定了,那在调用的地方呢,我们正常去用就行了,编译器会自动识别匹配。
遇到前置++,它就会自动去调用前置++;遇到后置++,编译器会自动传一个整型参数,去调用后置++。
4. 日期-天数(-/-=)
上面我们通过重载+进而实现计算一个日期+一个天数之后是什么日期,那是不是还可以搞一下日期-天数,计算它的前多少天是什么日期。
那我们先来重载-=:
那思路和上面加一个天数也是类似的,我们先用日减去传过来的天数,如果减完之后<=0,说明当前日期是不合法的,那怎么办,就去加上一个月的天数,然后月–,当然要注意判断会不会减到上一年,然后还<=0的话,就继续,知道日>0循环结束。
Date& Date::operator-=(int day)
{_day -= day;while (_day <= 0){--_month;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}
测试一下:
没问题。
然后我们再重载一下-:
那是不是简单啊,直接复用-=。
Date Date::operator-(int day)
{Date tmp(*this);tmp -= day;return tmp;
}
测试 一下:
🆗,看起来好像没什么问题了,但是,如果这样呢:
这里减一个负数,会发现结果就错了。
为什么呢,大家可以调试观察:
会发现这里根本不会进入循环,直接就返回了。
所以针对负数的情况,我们要单独处理一下:
Date& Date::operator-=(int day)
{if (day < 0){*this += -day;return *this;}_day -= day;while (_day <= 0){--_month;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}
我们-是复用-=的,所以这里在-=里面处理。
-=一个-100就相当于+=一个100嘛。
那同样对于+和+=我们也要处理一下:
那我们来处理一下:
Date& Date::operator+=(int day)
{if (day < 0){*this -= -day;return *this;}_day += day;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month == 13){_year++;_month = 1;}}return *this;
}
这样是不是就行了,+=一个-100就相当于-=一个100嘛。
5. 前置- -和后置- -的重载
那有了上面的练习,再实现前置- -和后置- -不是soeasy嘛。
前置- -:
Date& Date::operator--()
{*this -= 1;return *this;
}
先- -,后使用,返回- -之后的值。
后置- -:
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}
先使用,后- -,返回- -之前的值。
6. 日期-日期
上面我们搞了日期加减天数,那两个日期相减是不是也有意义啊,可以得出这两个日期之间差了多少天。
那如何计算两个日期之间相差的天数呢?
🆗,那这里给大家提供一种比较好实现的思路:
我们拿到两个日期之后,先把较小的那个日期找出来,然后用一个计数器,统计较小日期++的次数,当两个日期相等时停止,++的次数就是相差的天数。
另外如果是第一个日期大于第二个,结果应该是整数;如果第二个大于第一个,应该是负数。
我们来实现一下:
//日期-日期
int Date::operator-(Date d)
{Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;
}
没有问题。
7. 流插入<<重载
那我们现在打印一个日期类对象的时候是不是都是去调用我们写的
使用我们之前学的cout
+<<
去打印。
大家记不记得:
我们之前文章里学习C++输入输出的时候,其实只是浅浅的提了一下如何去用,并没有对cout、<<和>>进行过多讲解。
因为以我们那时的知识储备还不能很好的理解:
那我们现在就可以再尝试多理解一点东西:
我们发现我们的自定义类型想用这些运算符是不是都要重载啊,除了赋值运算符以及取地址运算符,因为赋值重载和取地址重载是类的6个默认成员函数之中的,我们不写编译器会自动生成一个。但是在有些情况下根据实际情况我们还是需要自己写。
🆗,这是我们上一篇学习的知识,不用过多说明了。
然后呢我们还说C++里这样输入输出可以自动识别类型。
那为啥可以自动识别类型,其实是因为它这里对
<<
进行了函数重载。
为什么我们的内置类型可以直接用,因为库里面已经对这些类型都进行了重载,所以可以自动识别类型,是根据操作数的不同类型进行匹配。
那我们现在自定义类型想用怎么办,是不是需要自己重载啊。
那我们接下来就重载一下<<
好吧:
void operator<<(ostream& out);
那想用
<<
是不是得使用cout
,<<
两个操作数,一个cout,一个是我们要打印得对象,cout
是啥?
🆗,上面说了它是一个ostream
类型得对象,所以我们把cout
作为参数传过去。
那这里out就用来接收cout,this指针指向得就是我们要打印得类对象。
来实现一下:
void Date::operator<<(ostream& out)
{out << _year << _month << _day << endl;
}
这样是不是就行了啊,它的成员变量都是内置类型,我们就可以使用<<直接打印了。
我们测试一下:
但是我们使用的时候发现报错了。
这里操作数是不是写反了,为什么?
对于函数重载来说,两个参数的话,第一个参数是左操作数,第二个参数是右操作数。
但是对于类成员函数来说,第一个参数是不是隐藏的this指针啊,它指向我们调用该函数的对象,所以这里第一个参数是Date对象的地址。
那我们要调用的话,应该这样写:
但是这样写是不是好别扭啊。
怎么让它像内置类型那样使用啊。
那我们就要想办法让cout成为第一个参数,怎么做呢?
把函数重载写到类外面不就行了是吧。
没有说运算符重载不能写到类外面啊,我们写到类里面是为了啥,是不是只是为了可以访问private修饰的成员变量啊
那我们现在重载到外面:
那现在面临的问题是啥?
是在类外不能访问私有的成员变量,那怎么办?
可以把成员变量变成共有的public,但这样是不是就不能很好的保证封装性了;
或者可以提供Get方法,但C++一般不喜欢这样搞。
那还有什么方法呢?
🆗,就是用友元函数。
那怎么做呢?
我们把函数声明再放到类里面一份,但在前面加一个friend就行了:
那这下我们就可以正常使用<<了:
但是呢:
这里不支持连续的流插入。为什么呢?
因为我们重载的没有返回值
那应该返回啥?
是不是返回cout呀,让它能够继续打印后面的。
我们看其实库里面就是这么搞的:
ostream& operator<<(ostream& out, Date& d)
{out << d._year << "-" << d._month << "-" << d._day << endl;return out;
}
这样就行了。
8. 流提取>>重载
那紧接着,我们再来重载一下流提取:
istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}
测试一下:
就完成了。
9. 总结
那最后呢,还要给大家说明一点:
我们在之前的类和对象第一篇其实就提到了:
就是类的成员函数如果直接定义在类里面,编译器是会将其当成内联函数的,不管你有没有加inline关键字。
那我们在学习内联函数的时候也说了:
一般建议将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数实现成内联函数。
所以说,类里面的成员函数如果规模较小,适合做内联函数的,直接定义在类里面。
不过我们今天写的这个日期类,里面我是所有成员函数的声明和定义都分离了,大家以后可以根据实际情况,有些成员函数直接定义在类里面。
但是我们说了内联函数不是只是对编译器的一个建议嘛,如果规模较大的函数就算我们实现成内联函数编译器也不一定按照内联函数处理。
那在类里面也是这样,就算我们把全部的类成员函数都直接定义在类里面,也不一定会全部当做内联函数处理,编译器也还是会看它具体适不适合做内联。
另外还有一点:
上一篇文章我们不是还学习了const成员函数嘛,大家还可以看一看我们日期类的这么多成员函数,哪一个在函数内部不需要改变调用它的对象,是不是把它实现成const成员函数也是比较好的。
10. 源码展示
最后,把完整的源码给大家展示一下:
- Date.h
#pragma once
#include <iostream>
#include <stdbool.h>
#include <assert.h>
using namespace std;class Date
{//友元friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);public:int GetMonthDay(int year, int month);//构造函数Date(int year = 1, int month = 1, int day = 1);//拷贝构造函数//Date(const Date& d);bool operator<(const Date& d);bool operator<=(const Date& d);bool operator>(const Date& d);bool operator>=(const Date& d);bool operator!=(const Date& d);void Print();bool operator==(const Date& d);Date& operator+=(int day);Date operator+(int day);//前置++Date& operator++();//后置++Date operator++(int);Date& operator-=(int day);Date operator-(int day);Date& operator--();Date operator--(int);//日期-日期int operator-(Date d);//赋值重载(对于Date类用默认生成的就行)//d1=d2(this就是d1,d就是d2)/*Date& operator=(const Date& d){if (this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;}*/
private:int _year;int _month;int _day;
};
//<<
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
- Date.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Date.h"int Date::GetMonthDay(int year, int month)
{assert(month > 0 && month < 13);int MonthArr[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;}else{return MonthArr[month];}
}
//构造函数
Date::Date(int year, int month, int day)
{if (month > 0 && month < 13 && day>0 && day <= GetMonthDay(year, month)){_year = year;_month = month;_day = day;}else{cout << "日期非法" << endl;_year = 1;_month = 1;_day = 1;}
}
//拷贝构造函数
//Date::Date(const Date& d)
//{
// _year = d._year;
// _month = d._month;
// _day = d._day;
//}
bool Date::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;return false;
}
bool Date::operator<=(const Date& d)
{return *this == d || *this < d;
}
bool Date::operator>(const Date& d)
{return !(*this <= d);
}
bool Date::operator>=(const Date& d)
{return !(*this < d);
}
bool Date::operator!=(const Date& d)
{return !(*this == d);
}
void Date::Print()
{cout << _year << "-" << _month << "-" << _day << endl;
}
bool Date::operator==(const Date& d)
{return _year == d._year&& _month == d._month&& _day == d._day;
}//Date Date::operator+(int day)
//{
// Date tmp(*this);
// tmp._day += day;
// while (tmp._day > GetMonthDay(tmp._year, tmp._month))
// {
// tmp._day -= GetMonthDay(tmp._year, tmp._month);
// tmp._month++;
// if (tmp._month == 13)
// {
// tmp._year++;
// tmp._month = 1;
// }
// }
// return tmp;
//}
//
//Date& Date::operator+=(int day)
//{
// *this = *this + day;
// return *this;
//}Date& Date::operator+=(int day)
{if (day < 0){*this -= -day;return *this;}_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)
{Date tmp(*this);tmp += day;return tmp;
}//前置++
Date& Date::operator++()
{*this += 1;return *this;
}
//后置++
Date Date::operator++(int)
{Date tmp(*this);*this += 1;return tmp;
}
Date& Date::operator-=(int day)
{if (day < 0){*this += -day;return *this;}_day -= day;while (_day <= 0){--_month;if (_month == 0){--_year;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}
Date Date::operator-(int day)
{Date tmp(*this);tmp -= day;return tmp;
}Date& Date::operator--()
{*this -= 1;return *this;
}
Date Date::operator--(int)
{Date tmp(*this);*this -= 1;return tmp;
}
//日期-日期
int Date::operator-(Date d)
{Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int n = 0;while (min != max){++min;++n;}return n * flag;
}
//<<
ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "-" << d._month << "-" << d._day << endl;return out;
}
istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}
- Test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Date.h"void Datetest1()
{Date d1(2023, 2, 15);d1.Print();Date d2 = d1 + (-100);d2.Print();/*Date d3 = ++d1;d3.Print();*/
}
void Datetest2()
{Date d1(2023, 2, 16);d1.Print();Date d2 = d1--;d1.Print();d2.Print();}
void Datetest3()
{Date d1;cin >> d1;cout << d1 << endl;}
int main()
{//Datetest3();return 0;
}
🆗,那这篇文章就到这里,欢迎大家指正!!!
下一篇文章,我们会对类和对象再做一些补充和收尾!!!
相关文章:

【C++】类和对象练习——日期类的实现
文章目录前言1. 日期的合法性判断2. 日期天数(/)2.1 和的重载2.2 对于两者复用的讨论3. 前置和后置重载4. 日期-天数(-/-)5. 前置- -和后置- -的重载6. 日期-日期7. 流插入<<重载8. 流提取>>重载9. 总结10. 源码展示前…...

[LeetCode周赛复盘] 第 333 场周赛20230219
[LeetCode周赛复盘] 第 333 场周赛20230219 一、本周周赛总结二、 [Easy] 6362. 合并两个二维数组 - 求和法1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6365. 将整数减少到零需要的最少操作数1. 题目描述2. 思路分析3. 代码实现四、[Medium] 6364. 无平方子集计数1. 题目描…...

数字化时代,如何做好用户体验与应用性能管理
引言 随着数字化时代的到来,各个行业的应用系统从传统私有化部署逐渐转向公有云、行业云、微服务,这种变迁给运维部门和应用部门均带来了较大的挑战。基于当前企业 IT 运维均为多部门负责,且使用多种运维工具,因此,当…...

Python爬虫(7)selenium3种弹窗定位后点击操作,解决点击登录被隐藏iframe无法点击的登陆问题
之前的文章有关于更多操作方式详细解答,本篇基于前面的知识点进行操作,如果不了解可以先看之前的文章 Python爬虫(1)一次性搞定Selenium(新版)8种find_element元素定位方式 Python爬虫(2)-Selenium控制浏览…...

如何对项目健康度进行测量?评估项目健康状况
项目驱动变革,大部分公司逐步由运营驱动转变为项目驱动,带来更多重新和商业价值。对组织而言,从商业角度看,项目旨在推动组织从一个状态转到另一个状态,从而达成特定目标。项目的健康情况如何关乎项目和变革的成本&…...

美国原装二手keysight E4980A(安捷伦)2MHZ LCR表
Agilent E4980A、Keysight E4980A、LCR 表,20 Hz - 2 MHz E4980A 是 Agilent 的 2 MHz LCR 表。LCR表是一种电子测试设备,用于测量电子元件的电感(L)、电容(C)和电阻(R)。LCR 表可…...
《clean coder》:关于摆烂,争论和心态
“凡是不能在五分钟之内解决的争论,都不能依靠辩论解决” ---- Kent Beck 作为一个码农,我并不是一个喜欢争论的角色。很长一段时间会陷入一种摆烂的,被动的状态。“既然其他人想要这么做,就这么办吧”。这可能是非专业的行为中最…...

jenkins下载与简单使用
1.jenkins下载 因为我仍然使用的是jdk1.8进行开发,所以我下载的是jenkins2.332.1版本(jenkins2.346.1版本在2022年末不再支持java8,如果项目使用的是jdk11可以继续使用该jenkins版本),更多版本下载请点击jenkins下载 …...

3|物联网控制|计算机控制-刘川来胡乃平版|第3章:计算机总线技术 补充串行总线部分|课堂笔记|ppt
2022年 10月 10日 3.3 外部总线 3.3.2 RS-232-C总线 机械特性...

Blazor入门100天 : 身份验证和授权 (3) - DB改Sqlite
目录 建立默认带身份验证 Blazor 程序角色/组件/特性/过程逻辑DB 改 Sqlite将自定义字段添加到用户表脚手架拉取IDS文件,本地化资源freesql 生成实体类,freesql 管理ids数据表初始化 Roles,freesql 外键 > 导航属性完善 freesql 和 bb 特性 本节源码 https://github.com/…...

阅读源码和查看官方文档,是解决问题最高效的办法。
作为一个工作8年的老程序员告诉你:阅读源码和查看官方文档,是解决问题最高效的办法。不信你来看,这个困扰了读者半天的问题我查了源码和文档后瞬间解决。 前言 上周五有位读者私信我一个问题,说困扰了他半天,研究了一…...
云原生流量管理系统中 Service , Ingress 和 Endpoint 的关系
摘要 Kubernetes(简称 K8s)是一个用于容器编排和管理的开源平台,其中流量管理是 K8s 的重要功能之一。K8s 提供了多种流量管理方式,以便对不同场景下的流量进行控制和管理。以下是 K8s 中常用的流量管理系统: Service:Service 是 K8s 中最基本的流量管理方式,用于提供…...

给你安利几款好用的谷歌浏览器插件
给你安利几款好用的谷歌浏览器插件前言一 Octotree 插件二 GitCodeTree 插件三 SourceGraph 插件四 GitZip 插件五 Enhanced GitHub 插件六 插件下载安装6.1 谷歌应用商店下载6.2 离线安装6.2.1 下载插件6.2.2 安装插件七 移除、启用、停用插件小结前言 GitHub是全球最大的代码…...

JDK定时器Timer原理
前言 前些时间想到利用redis实现延时队列,但是底层的定时器不止如何实现好些,故此研究了一下jdk的Timer。 Timer是一个用于执行定时任务的类,可以单次执行或按指定时间间隔循环执行(直到主动cancel或线程被杀掉)。Ti…...
vue3中使用swiper完整版教程
介绍 在 vue3 中使用 swiper, 实现轮播图的效果;如果组件样式等模块引入不当,很有可能导致,页面无效果;或者想要的箭头或者切换效果异常问题。具体使用方式如下所示: 使用方式 使用命令 npm install swiper 安装 sw…...
某个div的滚动条样式
.item-body,.chart2{/*滚动条整体样式*/&::-webkit-scrollbar {/*高宽分别对应横竖滚动条的尺寸*/width: 10px;height: 1px;}/*滚动条里面小方块*/&::-webkit-scrollbar-thumb {border-radius: 10px;-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);background:…...

Spring Boot框架基础介绍
Spring Boot 是一款基于 Spring 框架的开源应用程序开发工具,它旨在简化 Spring 应用程序的配置和开发过程。Spring Boot 提供了一种简单的方式来创建可独立运行的、生产级别的应用程序,并在需要时进行部署。Spring Boot 在微服务架构和云计算环境下得到…...

Git - 在主分支上创建分支并提交代码
拉取最新代码 因为当前在 master 分支下,你必须拉取最新代码,保证当前代码与线上同步(最新),执行以下命令: git pull origin master创建分支 目前我们在 master 主分支上,需要执行以下命令&…...

第三届无线通信AI大赛分享交流会暨颁奖典礼顺利举办,大赛圆满收官
2月16日,第三届无线通信AI大赛分享交流会暨颁奖典礼在北京顺利举行,宣告大赛圆满收官。 分享交流会暨颁奖典礼以线上线下结合的形式展开,邀请无线通信领域的多位专家、学者与「基于AI的信道估计与信道状态信息反馈联合设计」、「基于AI的高精…...
程序的本质与类的说明
摘自《深入浅出WPF完整版》P132程序的本质就是“数据算法”,,这一本质一直没有改变一一类的作用只是把散落在程序中的变量和函数进行归档封装并控制对它们的访问而已。被封装在类里的变量称为字段 (Fied)它表示的是类或实例的状态:被封装在类里的函数称为…...

19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...

GO协程(Goroutine)问题总结
在使用Go语言来编写代码时,遇到的一些问题总结一下 [参考文档]:https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现: 今天在看到这个教程的时候,在自己的电…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...