【浅尝C++】类和对象第二弹=>类的6个默认成员函数/运算符重载详谈

在这里插入图片描述

🏠专栏介绍:浅尝C++专栏是用于记录C++语法基础、STL及内存剖析等。
🚩一些备注:之前的文章有点杂乱,这里将前面的知识点重新组织了,避免了过多冗余的废话。
🎯每日努力一点点,技术变化看得见。

文章目录

  • 类的6个默认成员函数概述
  • 构造函数
    • 概念
    • 特性
  • 析构函数
    • 概念
    • 特性
  • 拷贝构造函数
    • 概念
    • 特性
  • 赋值运算符重载
    • 运算符重载
    • 赋值运算符重载
    • 前置++与后置++
  • const成员
  • 取地址及const取地址操作符重载


类的6个默认成员函数概述

如果我们写一个不含任何成员函数、成员变量的类,这个类什么成员都没有,则称这个类为空类

class Date{};

空类真的什么都没有吗?并不是!任何类在什么都不写的情况下,编译器会自动生成下图所示的6个默认成员函数。

在这里插入图片描述
上面说的默认成员函数,就是用户没有显示实现,编译器会生成的成员函数称为默认成员函数。

构造函数

概念

首先,我们来看一个日期类代码↓↓↓

class Date
{
public:void Init(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.Init(2024, 5, 5);d1.Print();return 0;
}

上面代码中,我们在实例化一个Date对象d1后,如果想对它的3个成员变量进行初始化,需要显示调用Init函数进行初始化。我们在定义一个类型/一个对象时,经常都需要对它进行初始化,如果每次都需要显示调用初始化函数显然有点麻烦。

那能否在对象创建的时候,直接将初始值设置进去呢?这就需要谈谈C++类和对象中的析构函数了。

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

特性

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

其特性如下:

  1. 函数名与类名相同
  2. 无返回值
  3. 对象实例化时编译器自动调用对应的构造函数
  4. 构造函数可以重载【也就是说,一个类可以有多个构造函数】

下面定义一个提供默认构造函数的日期类,演示上面的4个特性↓↓↓

#include <iostream>
using namespace std;class Date
{
public://无参构造函数 --> 函数名与类名相同,无返回值Date(){}//有参构造函数 --> 与无参构造函数构成重载Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024,5,5);//编译器在对象创建时自动调用有参构造函数Date d2;//编译器在对象创建时自动调用无参构造函数return 0;
}

上面代码中需要特别注意的是,在调用无参构造函数时,需要在对象的后面添加括号。否则编译器会将这种代码看作函数的声明。

void test()
{Date d3();//编译器会将该行代码看作函数名为d3,返回值为Date,没有的参数的函数声明Date d4;//调用无参构造不用在对象后面加括号
}
  1. 如果类没有定义构造函数,则C++编译器会自动生成一个无参的默认构造函数。但只要用户显示定义构造函数数(不管是有参构造还是无参构造),编译器将不再生成默认构造函数。
#include <iostream>
using namespace std;class Date
{
public://用户定义了构造函数,编译器将不再提供默认构造函数Date(int year, int month, int day){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;
};int main()
{Date d1(2024,5,5);//代码执行正确Date d2;//error!!return 0;
}

上面代码中由于用户自定义了构造函数,则编译器不再提供无参的默认构造函数。此时创建d2时将会报错,因为此时并没有默认构造函数。

  1. 在用户不是实现构造函数时,编译器实现的默认构造函数的作用:对于内置类型(C++语言提供的默认类型,如int/char等),不会对它们做任何处理;对于自定义类型(使用class/struct/union等定义的类型),会调用它们的默认构造函数(也称为无参构造函数)。
#include <iostream>
using namespace std;class Time
{
public:Time(){cout << "Time()正在被调用" << endl;_hour = _minute = _second = 0;}
private:int _hour;int _minute;int _second;
};class Date
{
public:void Print(){cout << _year << "/" << _month << "/" << _day << endl;cout << _t._hour << "/" << _t._minute << "/" << _t._second << endl;}
private://基本类型(内置类型)int _year;int _month;int _day;//自定义类型Time _t;
};int main()
{Date d;d.Print();return 0;
}

在这里插入图片描述

上面代码执行结果如上图所示。可以看到Date提供的默认构造函数对C++内置类型没有做任何操作,所以它们都是一些随机值;对于自定义类型,Date会调用它们的默认构造函数。

★ps:C++11中为了解决默认构造函数不会初始化内置类型的问题,打了如下补丁:内置类型的成员变量在声明时可以给出默认值。给出默认值后,类在创建时会给内置类型的成员变量赋予默认值

#include <iostream>
using namespace std;class Time
{
public:Time(){cout << "Time()正在被调用" << endl;_hour = _minute = _second = 0;}
private:int _hour;int _minute;int _second;
};class Date
{
public:void Print(){cout << _year << "/" << _month << "/" << _day << endl;cout << _t._hour << "/" << _t._minute << "/" << _t._second << endl;}
private://基本类型(内置类型)int _year = 0;int _month = 0;int _day = 0;//自定义类型Time _t;
};int main()
{Date d;d.Print();return 0;
}

在这里插入图片描述
7. 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认函数只能有一个。
★ps:我们不能在同一个类中同时提供无参构造函数和全缺省的构造函数。

下面代码中由于同时提供了无参构造和全缺省的构造函数,在main定义调用默认构造的类对象时,编译器无法确定调用无参构造还是全缺省的构造函数。(程序存在二义性)

#include <iostream>
using namespace std;class Date
{
public:Date(){_year = 2024;_month = 6;_day = 1;}Date(int year = 0, int month = 0, int day = 0){_year = year;_month = month;_day = day;}
private:int _year;int _month;int _day;Time _t;
};int main()
{Date d1;return 0;
}

析构函数

概念

上面我们介绍了用于对象创建时的成员函数,那有没有用于对象销毁的成员函数呢?

析构函数:与构造函数功能相反,析构函数用于清理对象的存储空。但析构函数不是完成对对象本身的销毁,它主要用于销毁对象申请的堆空间,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

特性

析构函数是特殊的成员函数,其特征如下:

  1. 析构函数名是在类名前加上字符 ~
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构
    函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

析构函数主要用于清理对象申请的堆空间,对于局部变量的释放工作,由编译器完成。下面代码中演示了析构函数的定义和调用情况↓↓↓

#include <iostream>
using namespace std;class Stack
{
public:Stack(int n = 5){_arr = (int*)malloc(sizeof(int) * n);_size = 00;_capacity = 0;}//析构函数~Stack(){cout << "~Stack()被调用" << endl;free(_arr);_arr = nullptr;_size = _capacity = 0;}
private:int* _arr;int _size;int _capacity;
};int main()
{Stack s;//在s的声明周期结束时,s的析构函数将被自动调用return 0;
}

在这里插入图片描述
5. 关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员变量调用它的析构函数。

#include <iostream>
using namespace std;class Time
{
public:~Time(){cout << "~Time()被调用" << endl;}
private:int _hour;int _minute;int _second;
};class Date
{
public:
private://基本类型(内置类型)int _year = 0;int _month = 0;int _day = 0;//自定义类型Time _t;
};int main()
{Date d;//在d生命周期结束后,将会调用析构函数,d的默认析构函数会调用自定义类型的析构函数return 0;
}

在这里插入图片描述
6. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如上面定义的Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如上面定义的Stack类。

拷贝构造函数

概念

在程序中,优势需要对每个对象做备份。那在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?

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

特性

拷贝构造函数也是特殊的成员函数,其特征如下:

  1. 拷贝构造函数是构造函数的一个重载形式。
#include <iostream>
using namespace std;class Date
{
public:Date(int year = 0, int month = 0, int day = 0){_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 = 0;int _month = 0;int _day = 0;
};int main()
{Date d1(2024,6,1);Date d2(d1);d1.Print();d2.Print();return 0;
}

在这里插入图片描述

  1. 拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用

如果使用值传递调用拷贝函数,在给拷贝构造函数传参时也是一次拷贝,这次拷贝需要调用拷贝构造函数;调用拷贝构造又是值传递,拷贝构造函数传参时也是一次拷贝,这次拷贝需要调用拷贝构造函数…(引发无穷递归调用)

在这里插入图片描述
3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。(被拷贝对象的各个成员变量存什么值,新创建的对象的各个成员变量也存什么值)。

注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的

#include <iostream>
using namespace std;class Time
{
public:Time(){}Time(const Time& t){cout << "Time(const Time& t)被调用" << endl;}
private:int _hour;int _minute;int _second;
};class Date
{
public:
private://基本类型(内置类型)int _year = 0;int _month = 0;int _day = 0;//自定义类型Time _t;
};int main()
{Date d1;Date d2(d1);return 0;
}

在这里插入图片描述
★ps:编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?

#include <iostream>
using namespace std;class Stack
{
public:Stack(int n = 5){_arr = (int*)malloc(sizeof(int) * n);_size = 00;_capacity = 0;}//析构函数~Stack(){cout << "~Stack()被调用" << endl;free(_arr);_arr = nullptr;_size = _capacity = 0;}
private:int* _arr;int _size;int _capacity;
};int main()
{Stack s1;Stack s2(s1);return 0;
}

在这里插入图片描述
为什么上面的代码会引发错误呢?

由于编译器自动生成的构造函数是按字节挨个拷贝的,导致s2中_arr和s1的_arr指向同一块内存空间。当s2生命周期结束后会调用了析构函数将这片内存空间释放,s1在生命周期结束也会释放这片空间。一块内存空间被重复释放从而导致错误。这种现象称为浅拷贝。
在这里插入图片描述
因此,如果类中有向堆申请空间,需要我们自己重新编写拷贝构造函数。在新创建的对象的构造函数中开辟一片新的内存空间,将待拷贝对象堆中的数据挨个拷贝进来。这种重写拷贝的行为称为深拷贝

#include <iostream>
#include <cstring>
using namespace std;class Stack
{
public:Stack(int n = 5){_arr = (int*)malloc(sizeof(int) * n);_size = 00;_capacity = 0;}//重写拷贝构造函数Stack(const Stack& s){_arr = (int*)malloc(sizeof(int) * s._capacity);memcpy(_arr, s._arr, sizeof(int) * s._capacity);_size = s._size;_capacitu = s._capacity;}//析构函数~Stack(){cout << "~Stack()被调用" << endl;free(_arr);_arr = nullptr;_size = _capacity = 0;}
private:int* _arr;int _size;int _capacity;
};int main()
{Stack s1;Stack s2(s1);return 0;
}

★ps:注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

5.拷贝构造函数典型调用场景:
①使用已存在对象创建新对象
②函数参数类型为类类型对象
③函数返回值类型为类类型对象

#include <iostream>
using namespace std;class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}Date(const Date& d){cout << "拷贝构造" << endl;_year = d._year;_month = d._month;_day = d._day;}
private:int _year = 0;int _month = 0;int _day = 0;
};Date func(Date d)
{return d;
}int main()
{Date d1;Date d2 = func(d1);return 0;
}

上面代码中一共调用了3次构造函数,如下图所示。但由于第2次和第3次拷贝连续发生,可能会被编译器优化成一次。(编译器具体如何优化,随编译器的不同而不同)
在这里插入图片描述
★ps:为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用尽量使用引用。

赋值运算符重载

运算符重载

如果我们想实现两个日期类的比较,我们可以使用一个isequal函数来实现↓↓↓

bool isequal(Date& d1, Date& d2)
{return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
}
int main()
{Date d1(2023, 1, 1);Date d2(2024, 5, 1);cout << isequal(d1, d2) << endl;
}

虽然上面的代码能够实现我们需要的功能。但我们在比较两个内置类型是否相等时,都会使用==运算符。为了让自定义类型能和内置类型一样使用常用的运算符,C++引入运算符重载。

运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

函数名字为:operator+需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

我们来看一下运算符重载如何实现两个日期类对象的比较↓↓↓

#include <iostream>
using namespace std;
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}//运算符重载//返回值+operator+需要重载的运算符+参数列表bool operator==(const Date& d){return _year == d._year && _month == d._month && _day == d._day;}
private:int _year = 0;int _month = 0;int _day = 0;
};int main()
{Date d1(2023,1,1);Date d2(2024,5,1);cout << (d1 == d2) << endl;return 0;
}

注意:
①不能重载C++中不存在的运算符:比如operator@
②重载操作符必须有一个类类型参数
③内置类型(如char、int等)的运算符不能被重载
④作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this指针
.*::sizeof?:. 注意以上5个运算符不能重载。

★ps:由于运算符重载的左操作数必须用this指针传入,故运算符重载函数只能在类内实现,而不能定义在类外。在类外定义如下下代码是不能编译通过的,因为编译器不会给它传入隐藏的this指针。

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

赋值运算符重载

1.赋值运算符重载格式↓↓↓

参数类型:const T&,传递引用可以提高传参效率,const可以防止在重载函数中修改传入的对象
返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
返回*this :因为要满足连续赋值,所以要返回自身(用于将自身给下一个对象赋值)
注意:函数体内需要检测是否自己给自己赋值

#include <iostream>
using namespace std;
class Date
{
public:Date(int year, int month, int day){_year = year;_month = month;_day = day;}//赋值运算符重载Date& operator=(const Date& d){_year = d._year;_month = d._month;_day = d._day;return *this;}void Print(){cout << _year << "/" << _month << "/" << _day << endl;}
private:int _year = 0;int _month = 0;int _day = 0;
};int main()
{Date d1(2023,1,1);Date d2 = d1;d1.Print();d2.Print();return 0;
}

在这里插入图片描述
2. 赋值运算符只能重载成类的成员函数不能重载成全局函数

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

  1. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。

既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实现吗?当然像日期类这样的类是没必要的。那么下面的类呢?验证一下试试?

#include <iostream>
using namespace std;class Stack
{
public:Stack(int n = 5){_arr = (int*)malloc(sizeof(int) * n);_size = 00;_capacity = 0;}~Stack(){cout << "~Stack()被调用" << endl;free(_arr);_arr = nullptr;_size = _capacity = 0;}
private:int* _arr;int _size;int _capacity;
};int main()
{Stack s1;Stack s2 = s1;return 0;
}

在这里插入图片描述
这里发生的错误和拷贝构造函数一样。由于编译器自动形成operator=函数是按直接进行拷贝的,导致两个Stack指向同一片内存空间,该内存空间被重复释放导致出错。因此,上面的代码应该修改成这样↓↓↓

#include <iostream>
#include <cstring>
using namespace std;class Stack
{
public:Stack(int n = 5){_arr = (int*)malloc(sizeof(int) * n);_size = 00;_capacity = 0;}Stack& operator=(const Stack& s){_arr = (int*)malloc(sizeof(int) * s._capacity);memcpy(_arr, s._arr, size(int) * s._size);_size = s._size;_capacity = s._capacity;}~Stack(){cout << "~Stack()被调用" << endl;free(_arr);_arr = nullptr;_size = _capacity = 0;}
private:int* _arr;int _size;int _capacity;
};int main()
{Stack s1;Stack s2 = s1;return 0;
}

如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。

★ps:注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

#include <iostream>
using namespace std;class Time
{
public:Time(){}Time& operator=(const Time& t){cout << "Time& operator=(const Time& t)" << endl;_hour = t._hour;_minute = t._minute;_second = t._second;return *this;}
private:int _hour;int _minute;int _second;
};class Date
{
public:
private://基本类型(内置类型)int _year = 0;int _month = 0;int _day = 0;//自定义类型Time _t;
};int main()
{Date d1;Date d2;d2 = d1;return 0;
}

在这里插入图片描述
★ps:如下代码是拷贝构造而不是赋值。因为此时d2并未构造,编译器会将Date d2 = d1;转化成Date d2 = Date(d1);完成拷贝构造。

void test()
{Date d1(2024,5,1);Date d2 = d1;
}

前置++与后置++

通过上面的了解,我们可以理解下面的代码↓↓↓

class NumSet
{
public:Numset(int num = 0){_num = num;}NumSet& operator++(){++_num;return *this;}
private:int _num;
}

上面代码实现的是前置++。对于重载++运算符,如果重载函数的参数列表没有参数,则是实现的是前置++。完成前置++要将自身返回给调用处(也就是++后的结果)。

那如何实现后置++呢?C++中规定,如果要实现后置++,要在对应函数的参数列表添加一个int类型的占位参数。(这里没有为什么,这只是C++的规定)

class NumSet
{
public:Numset(int num = 0){_num = num;}NumSet& operator++(int){Numset tmp = Numset(*this);++_num;return tmp;}
private:int _num;
}

如果我们在调用后置++时,编译器默认会给operator++函数传递一个整型数,从而调用后置++。我们可以使用NumSet n; n.operator++(0);显示调用后置++。

后置–和前置–与上面的代码类似,这里不再介绍。

const成员

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
在这里插入图片描述
下面有几个问题,这里一起讨论一下:

  1. const对象可以调用非const成员函数吗?
    解答:const对象的不能修改对象的成员变量,但非const成员函数能修改成员变量。这答案很明显,const对象不能调用非const成员函数,因为这属于权限放大。
  2. 非const对象可以调用const成员函数吗?
    解答:非const对象的成员变量是可以修改的,const成员函数不可以修改成员函数。这里属于权限的缩小,因此非const对象可以调用const成员函数。
  3. const成员函数内可以调用其它的非const成员函数吗?
    解答:这属于权限放大,故const成员函数内不可以调用其它的非const成员函数
  4. . 非const成员函数内可以调用其它的const成员函数吗?
    解答:这属于权限缩小,故非const成员函数内可以调用其它的const成员函数。

取地址及const取地址操作符重载

这两个操作符的重载与上面介绍的其他运算符重载类似,这里不做赘述,直接给出代码示例↓↓↓

class Date
{
public :Date* operator&(){return this;}const Date* operator&() const{return this;}
private:int _year = 0;int _month = 0;int _day = 0;
};

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载。

🎈欢迎进入浅尝C++专栏,查看更多文章。
如果上述内容有任何问题,欢迎在下方留言区指正b( ̄▽ ̄)d

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

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

相关文章

Appium Inspector 展示设备当前页面

定位元素需要使用appium inspector&#xff0c;之前每次都是从登录页开始&#xff0c;后来发现连接设备的时候只需要去掉appPackage、appActivity即可。 { "platformName": "Android", "platformVersion": "6", "deviceNa…

35.网络游戏逆向分析与漏洞攻防-游戏网络通信数据解析-登录成功数据包内容分析

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;34.登录数据包的…

LGBM算法 原理

简介 GBDT (Gradient Boosting Decision Tree) 是机器学习中一个长盛不衰的模型&#xff0c;其主要思想是利用弱分类器&#xff08;决策树&#xff09;迭代训练以得到最优模型&#xff0c;该模型具有训练效果好、不易过拟合等优点。GBDT不仅在工业界应用广泛&#xff0c;通常被…

伪装目标检测之注意力CBAM:《Convolutional Block Attention Module》

论文地址&#xff1a;link 代码&#xff1a;link 摘要 我们提出了卷积块注意力模块&#xff08;CBAM&#xff09;&#xff0c;这是一种简单而有效的用于前馈卷积神经网络的注意力模块。给定一个中间特征图&#xff0c;我们的模块依次推断沿着两个独立维度的注意力图&#xff…

10.2024

使用选择排序将{2,9,5,0,1,3,6,8}排序 代码&#xff1a; public class 第十题 {public static void main(String[] args) {int a[]{2,9,5,0,1,3,6,8};int begin0;int enda.length-1;while (begin<end){int mina[begin];int tbegin;for (int ibegin;i<end;i){if(min>…

Selenium 自动化 —— 定位页面元素

更多内容请关注我的 Selenium 自动化 专栏&#xff1a; 入门和 Hello World 实例使用WebDriverManager自动下载驱动Selenium IDE录制、回放、导出Java源码浏览器窗口操作切换浏览器窗口 使用 Selenium 做自动化&#xff0c;我们不仅仅是打开一个网页&#xff0c;这只是万里长…

Python私有属性和私有方法

私有属性和私有方法 在实际开发中&#xff0c;对象的某些属性或者方法只希望在对象内部被使用&#xff0c;而不希望在外界被访问。 私有属性&#xff1a;对象不希望公开的属性 私有方法&#xff1a;对象不希望公开的方法 定义方式&#xff1a;在属性名或者方法名前添加两个下划…

flask_restful规范返回值之类型设置

大型的互联网项目中&#xff0c;返回的数据格式&#xff0c;有时是比较复杂的结构。 如&#xff1a;豆瓣电影 https://movie.douban.com/j/chart/top_list?type24&interval_id 100%3A90&action&start20&limit20 返回的值里有 json 或者列表数据&#xff0c…

解决sqlalchemy执行语句提示Not an executable object

问题&#xff1a; from sqlalchemy import create_engine# 数据库的变量 HOST 127.0.0.1 PORT 3306 DATA_BASE itbz USERroot PWD123456 # DB_URL f数据库的名驱动名://{USER}:{PWD}{HOST}:{PORT}/{DATA_BASE} DB_URL fmysqlpymysql://{USER}:{PWD}{HOST}:{PORT}/{DATA_B…

分类模型评估:混淆矩阵与ROC曲线

1.混淆矩阵2.ROC曲线 & AUC指标 理解混淆矩阵和ROC曲线之前&#xff0c;先区分几个概念。对于分类问题&#xff0c;不论是多分类还是二分类&#xff0c;对于某个关注类来说&#xff0c;都可以看成是二分类问题&#xff0c;当前的这个关注类为正类&#xff0c;所有其他非关注…

政安晨:【Keras机器学习实践要点】(三)—— 编写组件与训练数据

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras实战演绎机器学习 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 介绍 通过 Keras&#xff0c;您可以编写自定…

位段详细解释

结构体位段的使用原则 在C语言中&#xff0c;结构体&#xff08;Struct&#xff09;是一种复合数据类型&#xff0c;它允许我们将多个不同类型的数据项组合成一个单一的实体。位段&#xff08;Bit Field&#xff09;是结构体中的一个特殊成员&#xff0c;它允许我们只取结构体…

常用中间件redis,kafka及其测试方法

常用消息中间件及其测试方法 一、中间件的使用场景引入中间件的目的一般有两个&#xff1a;1、提升性能常用的中间件&#xff1a;1) 高速缓存&#xff1a;redis2) 全文检索&#xff1a;ES3) 存日志&#xff1a;ELK架构4) 流量削峰&#xff1a;kafka 2、提升可用性产品架构中高可…

Spring Cloud 网关Gateway + 配置中心

网关 网络的接口&#xff0c;负责请求的路由、转发、身份校验 路由&#xff1a;告诉请求去哪找 转发&#xff1a;请求找不到直接带请求过去 路由及转发 判断前端请求的规则就这么配 当前情况下只需要访问8080端口 就可以完成对全部微服务的访问 路由属性 登录校验 没必要在每…

sonar+gitlab提交阻断 增量扫描

通过本文&#xff0c;您将可以学习到 sonarqube、git\gitlab、shell、sonar-scanner、sonarlint 一、前言 sonarqube 是一款开源的静态代码扫描工具。 实际生产应用中&#xff0c;sonarqube 如何落地&#xff0c;需要考虑以下四个维度&#xff1a; 1、规则的来源 现在规则的…

java一和零(力扣Leetcode474)

一和零 力扣原题 给定一个二进制字符串数组 strs 和两个整数 m 和 n&#xff0c;请你找出并返回 strs 的最大子集的长度&#xff0c;该子集中最多有 m 个 0 和 n 个 1。 示例 1&#xff1a; 输入&#xff1a;strs [“10”, “0001”, “111001”, “1”, “0”], m 5, n …

【msyql】mysqldump: 未找到命令...

使用mysqldump备份数据库出现错误提示&#xff1a; mysqldump: 未找到命令... 执行的命令如下&#xff1a; mysqldump -uroot -proot --databases db_user > /home/backups/databackup.sql 解决方法 确认mysql是否安装 查看mysql版本 mysql --version 查找mysql安装路…

php反序列化刷题1

[SWPUCTF 2021 新生赛]ez_unserialize 查看源代码想到robots协议 看这个代码比较简单 直接让adminadmin passwdctf就行了 poc <?php class wllm {public $admin;public $passwd; }$p new wllm(); $p->admin "admin"; $p->passwd "ctf"; ec…

极光笔记|极光消息推送服务的云原生实践

摘要 极光始终秉承“以开发者为中心”的战略导向&#xff0c;极光推送&#xff08;JPush&#xff09;是国内领先的消息推送服务。极光推送&#xff08;JPush&#xff09;本质上是一种软件付费应用程序&#xff0c;结合当前主流云厂商基础施设&#xff0c;逐渐演进成了云上SaaS…

Java后端设置服务器允许跨域

文章目录 1、实现2、一些问题关于各项请求头的作用关于预检请求 3、一些补充4、疑问点 1、实现 以下通过servlet的Filter给所有响应的header加了一些跨域相关的数据&#xff0c;以实现允许跨域。 import org.springframework.context.annotation.Configuration; import org.s…