【C++】类和对象(中)
文章目录
- 1. 类的6个默认成员函数
- 2. 构造函数
- 概念
- 特性
- 3. 析构函数
- 概念
- 特性
- 4. 拷贝构造函数
- 概念
- 特征
- 5. 运算符重载
- 5.1 前置++和后置++重载
- 5.2 赋值运算符重载
- 6. 日期类的实现
- 7. const成员
- 8. 取地址及const取地址操作符重载
1. 类的6个默认成员函数
如果一个类中什么成员都没有,简称为空类。
class Date {};
其实在空类中并不是什么都没有,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。

默认成员函数: 用户没有显式实现,编译器会生成的成员函数称为默认成员函数。
当我们平时在写数据结构的代码时,例如在写栈的代码时,可能会忘记初始化就去进行增删查改操作,在我们写完代码后可能忘记将我们初始化好的栈释放掉。这样会导致我们的代码出现一系列的问题,但我们又无法避免,所以C++在C语言的基础上增加了类的默认成员函数。其中构造函数、析构函数、拷贝构造和赋值运算符重载是我们重点讲解的内容。剩下两个平常用的很少,遇到的时候我们在后续讲解。
2. 构造函数
概念
当我们在使用类来初始化一个对象时,通常都需要调用他的Init公有方法给对象进行初始化。未免有点麻烦,那能否在对象创建时,就将信息设置进去呢?
构造函数是一个特殊的成员函数,
名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。
构造函数有如下特性:
- 函数名与类名相同。
- 无返回值。
- 对象实例化时编译器自动调用对应的构造函数。
- 构造函数可以重载。
- 如果类中没有显式定义构造函数,则C++编译器会自动生成一个
无参的默认构造函数,一旦用户显式定义编译器将不再生成。- 构造函数对内置类型不做处理,对于自定义类型会调用他的默认构造函数。
- 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。
我们先来上手一个构造函数,来看一下他如何用。

我们可以看到编译器帮助我们自动调用了构造函数,而构造函数的功能就是帮助我们初始化对象,下面我们来看一下构造函数的几大特性。
特性
💕 (1) 构造函数支持重载和缺省参数
class Date
{
public://无参构造函数Date(){_year = 1949;_month = 10;_day = 1;}//有参构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}void Print(){cout << _year << "-" << _month << "-" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;d1.Print();Date d2(2023,2,5);d2.Print();return 0;
}

这里我们可以看到构造函数是支持重载的,但是我们也需要注意一点:无参构造和有参全缺省的构造函数不能同时出现,因为在调用的时候会出现歧义。

最后我们还需要注意的是,当我们在调用无参构造或者全缺省构造来初始化对象时,不要再对象后面带括号,因为这会导致编译器分不清我们是在实例化对象还是再进行函数声明。
💕 (2) 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

虽然编译器会给我们自动生成一个默认构造函数来完成初始化,但是为什么初始化的结果是一个随机值呢?这是因为构造函数对内置类型不做处理。下面我们会讲。
💕 (3) 构造函数对内置类型不做处理,对于自定义类型会调用他的默认构造函数。
C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自定义的类型。
这里我们使用stack类、MyQueue类、Date类三者进行一下对比:
class Date
{
public:void Print(){cout << _year << "年" << _month << "月" << _day << "日" << endl;}
private:int _year;int _month;int _day;
};class MyQueue {
public:// 默认生成构造函数,对自定义类型成员,会调用他的默认构造函数// 默认生成析构函数,对自定义类型成员,会调用他的析构函数void push(int x) {}//....Stack _pushST;Stack _popST;int _size = 0;
};
int main()
{Date d1;d1.Print();MyQueue q;return 0;
}

我们可以看到,编译器默认生成的构造函数对不会对内置类型进行处理,但是我们可以看到编译器调用了两次构造和两次析构函数对MyQueue类中的自定义类型进行了处理,而编译器调用的,恰恰是自定义类型中的默认构造函数。
所以,我们不必纠结什么时候自己提供构造函数,什么时候使用编译器提供的默认构造函数,当编译器提供的默认构造函数能够满足我们的需求的时候,我们自己就不用提供构造函数,否则的话我们就需要自己提供构造函数。
注意: C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。
class Date
{
public:void Print(){cout << _year << "年" << _month << "月" << _day << "日" << endl;}
private://在类中声明时为内置类型提供默认值int _year= 1;int _month = 1;int _day = 1;
};
💕 (4) 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
我们知道,默认构造函数有三种:编译器自动提供的无参构造函数、自己定义的无参构造函数、自己定义的全缺省的构造函数。
但是,如果我们提供了有参构造函数,编译器就不会提供默认构造函数。
3. 析构函数
概念
与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
这里我们先来简单看一下析构函数的用法:

我们可以看到,析构函数和构造函数一样,都是由编译器帮助我们自动调用。
析构函数的特性:
- 析构函数名是在类名前加上字符 ~。
- 无参数无返回值类型。
- 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载。
- 对象生命周期结束时,C++编译系统系统自动调用析构函数
- 和构造函数一样,编译器生成的默认析构函数,对自定类型成员调用它的析构函数,对内置类型不做处理。
特性
那么我们什么时候需要使用析构函数呢?很显然,如果我们的类中没有进行资源的申请(动态开辟内存,打开文件等操作时),那么这个类在函数调用自动销毁时不需要我们做任何处理。所以,我们不需要写析构函数。
但如果我们定义的类中的成员变量指向了一块动态开辟的内存空间,那么我们就必须自己手动写析构函数,如果不处理就会造成内存泄露。
这里我们以MyQueue类作为例子,可以看到和构造函数一样,编译器生成的默认析构函数,对自定类型成员调用它的析构函数。

4. 拷贝构造函数
概念
在创建对象时,我们可以创建一个与已存在的对象一摸一样的新对象,那么这个功能就由我们的拷贝构造函数来完成。
拷贝构造函数:只有单个形参,该形参是
对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
下面我们先来看一下拷贝构造函数的基本用法:

拷贝构造函数的特性:
- 拷贝构造函数是构造函数的一个重载形式。 当我们使用拷贝构造实例话一个对象时,编译器不在调用构造函数。
- 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
- 若未显示定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
- 默认的拷贝构造函数对内置类型以字节为单位直接进行拷贝(浅拷贝),对自定义类型调用其自身的拷贝构造函数。
特征
💕 (1) 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。

int main()
{Date d1(2023, 2, 5);d1.Print();Date d2(d1);d2.Print();return 0;
}
当我们使用传值的方式写拷贝构造函数时,编译器会直接报错,这是因为可能会引发无穷递归。
那么为什么会引发无穷递归呢?原因是当我们创建d2对象时,编译器会自动调用拷贝构造函数,但是传值调用的时候形参是实参的一份临时拷贝,所以形参需要先拷贝一份实参,而拷贝的过程又需要调用拷贝构造函数,所以这样下去就会导致无穷递归。

所以我们必须使用引用来做形参,这样的话在调用拷贝构造的时候,形参就是实参自己,那么就不需要再拷贝一份实参,拷贝构造函数直接就可以完成对待初始化对象的初始化。当然,我们在写拷贝构造函数的时候,一般都在前面加const,这样就可以避免对原对象的修改。
💕 (2) 若未显示定义,编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
如果我们没有实现拷贝构造函数,编译器给我们自动生成一个默认拷贝构造函数。这个默认的拷贝构造函数对内置类型会完成浅拷贝,但如果我们的内置类型有进行资源的申请(例如:动态开辟内存)时,就会出现严重的问题,下面我们举例来看。
typedef int DataType;
class Stack {
public://构造函数Stack(){cout << "构造函数Stack()" << endl;_array = (DataType*)malloc(sizeof(DataType) * 3);if (!_array){perror("malloc fail::");exit(-1);}_capacity = 3;_top = 0;}//...//析构函数~Stack(){cout << "析构函数~Stack()" << endl;if (_array){free(_array);_array = nullptr;}_top = _capacity = 0;}
private:DataType* _array;int _top;int _capacity;
};
int main()
{Stack st1;Stack st2(st1);return 0;
}


当编译器自动生成的默认拷贝构造函数进行初始化栈时,我们发现两个对象中的_array指针竟然指向了同一块空间。这是因为编译器默认生成的拷贝构造函数拷贝数据时,是按照byte进行拷贝的,那么拷贝的时候自然也会将st1的地址拷贝给st2,这就导致了st2和st1指向了同一块空间。当主函数结束的时候,需要调用析构函数来销毁两个对象,但是由于两个对象中的_array都指向了同一块空间,所以导致了同一块空间被释放了两次。

这种情况不仅析构两次有问题,而且在插入数据时,一个对象的改变必然会覆盖另一个对象的数据,所以这种浅拷贝的问题会造成严重的后果,那么我们应该如何避免这种情况的发生呢?这个时候我们就得写一个深拷贝拷贝构造函数了。
Stack(const Stack& s){_array = (DataType*)malloc(sizeof(DataType) * s._capacity);if (!_array){perror("malloc fail::");exit(-1);}_top = s._top;_capacity = s._capacity;}

当然了,如果一个类的成员变量都是自定义类型的话,那么默认成员函数就会去调用成员变量的拷贝构造函数。

所以我们什么时候需要写拷贝构造函数呢?很简单,如果类中有资源申请我们就必须手动实现拷贝构造函数。如果没有的话那就直接使用类提供的默认拷贝构造即可。
拷贝构造函数典型调用场景:
- 使用已存在对象创建新对象
- 函数参数类型为类类型对象
- 函数返回值类型为类类型对象
5. 运算符重载
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
- 函数名字:关键字
operator后面接需要重载的运算符符号。 - 函数原型:返回值类型 operator操作符(参数列表)
对于运算符的重载我们需要注意几点:
- 不能通过连接其他符号来创建新的操作符:比如operator@
- 重载操作符必须有一个类类型参数
- 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
- 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
.*::sizeof?:.注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
我们知道,类中的成员变量一般都是私有的,我们并不能在类外直接修改他们,所以当我们使用重载后的运算符去进行类实例化的对象的操作时,一般都会把运算符在类内进行重载。当我们在类内进行运算符重载时,只需要传递一个一个参数(右操作数),因为左操作数就是编译器给我们自动传递的this指针。这里我们需要注意,当我们将函数放在类内时,不管操作数有几个,this都默认指向第一个操作数。
5.1 前置++和后置++重载
class Date
{
public:Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}//重载前置++Date& operator++(){_day += 1;return *this;}//重载后置++Date operator++(int){Date temp(*this);_day += 1;return temp;}
private:int _year;int _month;int _day;
};
这里我们需要注意的是:
- 前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载,C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递。
- 后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,然后给this + 1,而temp是临时对象,因此只能以值的方式返回,不能返回引用。
5.2 赋值运算符重载
赋值运算符重载作为类的六个默认成员函数之一,如果用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,所以像日期类这样的类是没有必要自己在去实现赋值运算符重载的了。

这里我们需要注意的是,Date d2 = d1是调用拷贝构造给d2完成初始化操作的,在创建对象时就会自动调用,而 Date d2;d2 = d1;这是调用赋值重载函数来给已经创建好的对象d2来完成初始化操作的。
所以,对于没有资源申请的类来说,并不需要写赋值重载函数,而对于有资源申请的类来说,我们必须显示的去调用赋值重载函数。否则不仅会导致赋值对象中动态分配的内存空间没有被释放,而且还会导致被赋值的对象指向的空格键析构两次,也就是造成了同一块空间被释放两次和内存泄漏两个严重的问题。
赋值运算符重载格式
- 参数类型: const T&,传递引用可以提高传参效率
- 返回值类型: T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
- 检测是否自己给自己赋值
- 返回*this : 要复合连续赋值的含义
当然了,如果赋值的对象是被赋值对象本身的话,我们还可以加一个特殊判定,因为虽然程序不会出现任何的问题,但是赋值本身就是一次拷贝构造,会造成消耗。同时,如果需要实现连续赋值,就必须将赋值重载函数返回对象本身。
这里我们可以实现一下日期类的赋值重载函数:
Date& operator=(const Date& d)
{if(this != &d){_year = d._year;_month = d._month;_day = d._day;}return *this;
}
赋值运算符只能重载成类的成员函数不能重载成全局函数
原因是,赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right)
{if (&left != &right){left._year = right._year;left._month = right._month;left._day = right._day;}return left;
}

6. 日期类的实现
💕 Date.h
#pragma once
#include<iostream>
using namespace std;
#include<cassert>class Date
{
public:friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);//打印函数void Print()const;//构造函数Date(int year = 1, int month = 1, int day = 1);//获取某个月的天数int GetMonthDay(int year, int month);//重载==运算符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 d);//重载+运算符Date operator+(int d)const;//重载-=运算符Date& operator-=(int d);//重载-运算符Date operator-(int d)const;//重载d1-d2运算符int operator-(const Date& d)const;//重载前置++Date& operator++();//重载后置++Date operator++(int);//重载前置--Date& operator--();//重载后置--Date operator--(int);private:int _year;int _month;int _day;
};//重载左移运算符
inline ostream& operator<<(ostream& out, const Date& d)
{out << d._year << "年" << d._month << "月" << d._day << "日" << endl;return out;
}inline istream& operator>>(istream& in, Date& d)
{in >> d._year >> d._month >> d._day;return in;
}
💕 Date.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"Date.h"//构造函数
Date::Date(int year, int month, int day)
{assert(month < 13 && day <= GetMonthDay(year, month));_year = year;_month = month;_day = day;
}//获取某个月的天数
int Date::GetMonthDay(int year, int month)
{assert(month > 0 && month < 13);int arr[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 arr[month];
}//打印函数
void Date::Print()const
{cout << _year << "年" << _month << "月" << _day << "日" << endl;
}//重载==运算符
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);
}//重载<运算符
bool Date::operator<(const Date& d)const
{return (_year < d._year)|| ((_year == d._year) && (_month < d._month))|| ((_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);
}//重载+=运算符
Date& Date::operator+=(int d)
{if (d < 0){return *this -= -d;}_day += d;while (_day > GetMonthDay(_year, _month)){_day -= GetMonthDay(_year, _month);_month++;if (_month > 12){_year++;_month = 1;}}return *this;
}//重载+运算符
Date Date::operator+(int d)const
{Date tmp(*this);tmp += d;return tmp;
}//重载-=运算符
Date& Date::operator-=(int d)
{if (d < 0){return *this += -d;}_day -= d;while (_day <= 0){_month--;if (_month == 0){_year--;_month = 12;}_day += GetMonthDay(_year, _month);}return *this;
}//重载-运算符
Date Date::operator-(int d)const
{Date tmp(*this);tmp -= d;return tmp;
}//重载d1-d2运算符
int Date::operator-(const Date& d)const
{Date max = *this;Date min = d;int flag = 1;if (*this < d){max = d;min = *this;flag = -1;}int cout = 0;while (min != max){++min;++cout;}return cout * flag;
}//重载前置++
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;
}
这里我们还可以扩展两个函数:
(1)将一年中的第几天转换成日期
void DaysToDate()
{int year = 0;int days = 0;cin >> year >> days;static int Day[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };if (((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))&& (days > 60)){--days;}int month = 1;while (days > Day[month]){days -= Day[month];month++;}printf("%04d-%02d-%02d\n", year, month, days);
}
(2)计算日期是一年中的第几天
//计算日期是一年中的第几天
int Date::DayOfYear() const
{static int Day[13] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };int n = Day[_month - 1] + _day;if (_month > 2 && ((_year % 4 == 0 && _year % 100 != 0) || (_year % 400 == 0)))++n;return n;
}
7. const成员
将
const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

💕 const对象不能调用非const成员函数:

aa是一个const修饰的对象,他只有可读的权限,但是成员函数Print()却不被const修饰,所以它既具有可读,又具有可写的权限,const A* 类型的this指针传递给一个A* 类型的this指针时,会导致权限的放大,所以编译器会报错。我们可以在Print函数括号的右边加const,将this指针修改为const A*类型。
💕 非const对象可以调用const成员函数:

非const对象具有可读和可写的权限,A* 类型的this指针传递给一个const A* 类型的this指针时,是属于权限的缩小,所以没有任何问题。
💕 非const成员函数内可以调用其它的const成员函数

_Print()函数在调用Print()函数时,A* 类型的this指针被转换成了const A* 类型的this指针,属于权限的缩小,所以可以成功,但是,如果要是const成员函数调用其它的非const成员函数时,const A* 的this指针向A*类型的this指针转换时,属于权限的放大,编译器会报错。
8. 取地址及const取地址操作符重载
取地址操作符重载也属于我们类的六个默认成员函数之一,所以平常当我们访问对象的地址时,类也会给我们提供对应的的默认成员函数,所以一般情况下我们都不需要对取地址操作符重载。
当然了我们也可以写一下:
class A
{
public:void Print() const{cout << _a << endl;}A* operator&(){return this;}const A* operator&() const{return this;}private:int _a = 10;
};
这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需
要重载,比如想让别人获取到指定的内容!
相关文章:
【C++】类和对象(中)
文章目录1. 类的6个默认成员函数2. 构造函数概念特性3. 析构函数概念特性4. 拷贝构造函数概念特征5. 运算符重载5.1 前置和后置重载5.2 赋值运算符重载6. 日期类的实现7. const成员8. 取地址及const取地址操作符重载1. 类的6个默认成员函数 如果一个类中什么成员都没有&#x…...
js下载文件
url为文件的src地址 url必须符合同源策略或者url的接口地址允许跨域,否则浏览器会报跨域错误 axios.get(data.url ,{ responseType: ‘blob’, }) .then( response>{ let blob new Blob([response.data]); let url window.URL.createObjectURL(blob); // 创建 …...
ESP8266 + STC15+ I2C OLED带网络校时功能的定时器时钟
ESP8266 + STC15+ I2C OLED带网络校时功能的定时器时钟 📍相关篇《ESP8266 + STC15基于AT指令通过TCP通讯协议获取时间》 📌ESP8266 AT固件基于安信可AT固件,相关刷AT固件可以参考《NodeMCU-刷写AT固件》 🔖STC15 单片机采用的是:STC15F2K60S2 晶振频率采用内部:22.11…...
计算机入门基础知识大全
♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的,绽…...
Python程序出现错误怎么办?
Python 异常处理 python提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误。你可以使用该功能来调试python程序。 异常处理: 本站Python教程会具体介绍。 断言(Assertions):本站Python教程会具体介绍。 python标准异常 异常名称 描述 BaseException 所有异常…...
【Vue3】v-if和v-for优先级
🎈博客主页:🌈我的主页🌈 🎈欢迎点赞 👍 收藏 🌟留言 📝 欢迎讨论!👏 🎈本文由 【泠青沼~】 原创,首发于 CSDN🚩…...
Windows上实现 IOS 自动化测试
本文介绍如何使用tideviceWDAairtest/facebook-wda实现在Windows上进行IOS APP自动化测试 环境准备 Windows Python环境 Python 3.6 WebDriverAgent安装 下载最新的项目到Mac:https://github.com/appium/WebDriverAgent $ git clone https://github.com/appiu…...
Linux云服务器下怎么重置MySQL8.0数据库密码
文章目录一、修改my.cnf配置文件为mysql免登陆二、免密登陆mysql三.给root用户重置密码1、首先查看当前root用户相关信息,在mysql数据库的user表中2、把root密码置为空3、退出mysql,删除/etc/my.cnf文件中添加进去的skip-grant-tables 重启mysql服务4、使…...
JVM调优
JVM调优-VisualVmVisualVm/ Jconsule远程连接第一种方式第二种方式:java 11开启远程GC连接如果还连不上考虑防火墙拦截了端口firewall-cmd --list-all,查看一下并暴露对应端口连接配置VisualVm界面简介采集GC信息的一些命令垃圾回收器切换VisualVm/ Jconsule远程连接…...
【配电网规划】SOCPR和基于线性离散最优潮流(OPF)模型的配电网规划( DNP )(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
锦正茂EM3电磁铁的技术参数
产品特点: ※U形结构、视野开阔、磁场强度高、磁场强度大小调节方便 ※体积小、重量轻、占空比小、结构紧凑、磁场性能更佳 ※电磁铁的工作气隙调节轻便灵活,极头处设有螺纹,更换极头装卸方便 ※可选配工作间隙刻度指示 ※小气隙时用于铁…...
Go最新版下载 Go1.20版新特性
Go官方正式发布了Go1.20稳定版 该版本依然保持 Go1 兼容性,可以升级到 Go1.20,而不需要做任何代码改动。 可以使用你任何喜欢的方式升级: 比如: go install golang.org/dl/go1.20latest 具体的可以参考官网教程: ht…...
Pywirt:一款基于Python的Windows安全应急响应工具
关于Pywirt Pywirt是一款基于Python开发的网络安全工具,该工具专门针对Windows操作系统设计,可以帮助广大研究人员使用winrm并通过在Windows操作系统上收集各种信息来加快安全事件应急响应的速度。 该工具已在Windows 10操作系统上进行过完整测试。 功…...
KDZD832 智能蓄电池活化仪
一、产品概述 KDZD832 智能蓄电池活化仪(2V-24V 一体机,适用于 2V、6V、12V/24V 蓄电池,以下简称活化仪),是专用于日常维护中对落后蓄电池处理的便携式产品,它具有四种独立的使用方式:电池放电…...
纯css实现loading加载中(多种展现形式)
前言 现如今网页越来越趋近于动画,相信大家平时浏览网页或多或少都能看到一些动画效果,今天我们来做一个有意思的动画效果,纯 css 实现 loading 加载中(多种展现形式),下面一起看看吧。 1. 常规 loading 实…...
【面试题】2023 vue高频面试知识点汇总
一、MVVM原理在Vue2官方文档中没有找到Vue是MVVM的直接证据,但文档有提到:虽然没有完全遵循MVVM模型,但是 Vue 的设计也受到了它的启发,因此在文档中经常会使用vm(ViewModel 的缩写) 这个变量名表示 Vue 实例。为了感受MVVM模型的…...
跨境电商选品重要吗?
选品很重要!跨境电子商务选择的核心要求:优质商品,价格优势,符合跨境销售特点,满足目标海外市场需求,突出自身特色竞争优势。跨境电商是如何选择产品的?这个问题也很流行,应该考虑以…...
SpringBoot
这里写目录标题1.入门程序1.1 spring-boot-starter-parent1.2 启动器1.3 EnableAutoConfiguration(重要)1.4 如何注册多个Controller?1.5 引导类2.完整的SpringBoot项目2.1 启动类2.1.1 创建一个启动类2.1.2 扩展: SpringBootConfiguration2.2 使用配置类定义组件2.3 SpringBo…...
python--turtle
前言 就随便练练,学习一下turtle库的使用 正文 1.语法学习 import turtle #导入库 turtle.showturtle() #画笔显示箭头 turtle.write("我是大帅逼") #写下字符串 turtle.forward(300) …...
NodeJS的后端Express项目部署到Ubuntu服务器,为前端提供API服务
之前参与的web3项目后端是用NodeJS开发的,因为可以共用NPM库,采用的Express框架,第一次弄,记录下大致的部署过程如下: 1、服务器上安装NodeJS sudo apt-get install nodejs 2、安装全局NPM工具,node_mod…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...
认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...
vue3 daterange正则踩坑
<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...
