【浅尝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.mfbz.cn/a/487427.html

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

相关文章

电阻器标记方法全解析:如何正确标注电阻器的阻值?

电阻器是一种用来限制电流流动的电子元件。它的主要作用是产生电阻&#xff0c;以控制电流的大小&#xff0c;从而保护其他电子元件不受过大的电流损害。电阻器通常由导电材料制成&#xff0c;电流在流过电阻器时会遇到阻力而产生电压降&#xff0c;使得电流被限制在一个较低的…

水牛社五大赚钱栏目概览:轻松了解项目核心与赚钱原理

很多新用户首次访问水牛社官网时&#xff0c;可能会感到有些迷茫。由于软件介绍相对较长&#xff0c;部分朋友可能缺乏耐心细读。然而&#xff0c;若您真心希望在网络上找到赚钱的机会&#xff0c;深入了解我们的发展历程将大有裨益。简而言之&#xff0c;本文旨在快速带您领略…

C++ assert()函数用法案例详解

参考:https://www.jb51.net/article/222176.htm assert宏的原型定义在<assert.h>中&#xff0c;其作用是如果它的条件返回错误&#xff0c;则终止程序执行。 原型定义&#xff1a; #include <assert.h> void assert( int expression );assert的作用是先计算表达…

【开发环境搭建篇】Redis服务器端安装和配置

作者介绍&#xff1a;本人笔名姑苏老陈&#xff0c;从事JAVA开发工作十多年了&#xff0c;带过大学刚毕业的实习生&#xff0c;也带过技术团队。最近有个朋友的表弟&#xff0c;马上要大学毕业了&#xff0c;想从事JAVA开发工作&#xff0c;但不知道从何处入手。于是&#xff0…

kubernetes最小调度单元Pod概述

Pod概述 一.Pod的概念1.Pod是什么2.Pod网络共享实现方式3.Pod存储共享方式4.创建Pod的流程 二.使用YAML文件定义Pod资源1.Pod资源清单YAML文件书写技巧1.YAML语法格式&#xff1a;2.配置Linux tab缩进两个空格3.使用kubectl explain帮助命令 2.创建Pod及Pod常用命令1.创建Pod资…

(免费领源码)Java#SSM#MYSQL学生信息管理系统的设计与实现70168-计算机毕业设计项目选题推荐

摘 要 从20年代开始&#xff0c;计算机疯狂的出现在人们的生活以及工作当中&#xff0c;成为人们生活、工作的好帮手&#xff0c;计算机深入到每家每户当中&#xff0c;网络办公&#xff0c;网络教学更是替换了传统手工记录管理的方式&#xff0c;使用计算机办公可以不必局限于…

Map中的computeIfAbsent()方法

调用java集合Map.computeIfAbsent()方法 java版本JDK1.8中&#xff0c;Map是我们经常使用的&#xff0c;在面对复杂Map时&#xff0c;我们怎么更好的去维护呢&#xff1f; 比如&#xff0c;这里的复杂Map&#xff0c;即<K,V>中V是一个Collection集合&#xff1a; 我们先…

Linux常用操作命令(清单快查版)

Linux常用操作命令&#xff0c;今日先给出快查清单&#xff0c;后续出带命令参数及不同OS的区别语法的相关示例 1. 文件与目录操作 命令描述ls列出目录内容cd切换目录pwd显示当前工作目录mkdir创建目录rmdir删除空目录cp复制文件或目录mv移动或重命名文件或目录rm删除文件或目…

Linux 的 app :一般到哪里下载 ?(**)

利用 appimagetool 工具对开发好的项目进行打包 &#xff08;***带笔记*&#xff09; https://blog.csdn.net/ken2232/article/details/131313613 1. 首选&#xff0c;直接通过 OS发行版的官网仓库&#xff1a;简单、方便&#xff1b;可能相对最可靠。 如&#xff1a; sudo a…

详解华为软件研发管理IPD

IPD,即集成产品开发(Integrated Product Development),是一种综合多种管理模型和理论、企业最佳实践的管理体系。旨在帮助企业快速适应市场变化,缩短产品上市时间,减少资源浪费,并提高生产力,以实现商业成功。 IPD的核心是跨部门团队的合作,涉及市场、研发、制造、服…

基于Redis实现短信登录

1.7.3、整体访问流程 当注册完成后&#xff0c;用户去登录会去校验用户提交的手机号和验证码&#xff0c;是否一致&#xff0c;如果一致&#xff0c;则根据手机号查询用户信息&#xff0c;不存在则新建&#xff0c;最后将用户数据保存到redis&#xff0c;并且生成token作为red…

Java项目:73 ssm档案管理系统

作者主页&#xff1a;源码空间codegym 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 角色&#xff1a;管理员、用户 用户点击进入到系统操作界面&#xff0c;可以对个人中心、警察信息管理、事故信息管理、申诉信息管理等功能模…

Android开发 --- Android12外部存储权限问题

1.问题 Android12使用如下权限&#xff0c;将不会获得读写文件的权限 <uses-permission android:name"android.permission.WRITE_EXTERNAL_STORAGE" /> 2.解决 if (!Environment.isExternalStorageManager()) {Intent intent new Intent(Settings.ACTION_M…

pikachu靶场第十四关——XSS(跨站脚本)之js输出(附代码审计)

源代码&#xff1a; //这里讲输入动态的生成到了js中,形成xss //javascript里面是不会对tag和字符实体进行解释的,所以需要进行js转义//讲这个例子主要是为了让你明白,输出点在js中的xss问题,应该怎么修? //这里如果进行html的实体编码,虽然可以解决XSS的问题,但是实体编码后…

redis在docker安装并启动流程

1、启动server docker run -d -p 6379:6379 --name redis01 redis:7.2.4以上命令&#xff0c;每次启动新的Redis容器&#xff0c;数据会丢失。 我们需要挂载数据文件&#xff0c;在宿主机上面&#xff0c;这样就可以持久化数据. 2、挂载数据文件&#xff08;可根据需求选择…

Schemdraw小白从入门到放弃---原理工具书

文章目录 序版本最简单的例子一、总体思路二、元件2.1 color习题 2.2 label2.3 length 三、元件的连接3.1 延续性习题 3.2 方向习题 3.3 接线点习题3.3.1 默认激活anchor与沉默anchor3.3.2 切换鼠标焦点机制3.3.2.1 at函数规定元件的start接在哪个anchor上3.3.2.2 to函数规定元…

SpringBoot请求WebService服务接口

目录 确定请求接口是否为webService接口 请求对方的接口地址获取对方的wsdl文件 将wsdl文件转换成Java类 请求对方接口 请求方式一 请求方式二 确定请求接口是否为webService接口 接口地址类似于&#xff1a;http://172.0.0.1:8080/webservice/baseService?wsdl 这样的就…

智能合约 之 ERC-721

ERC-721&#xff08;Non-Fungible Token&#xff0c;NFT&#xff09;标准 ERC-721是以太坊区块链上的一种代币标准&#xff0c;它定义了一种非同质化代币&#xff08;Non-Fungible Token&#xff0c;NFT&#xff09;的标准。NFT是一种加密数字资产&#xff0c;每个代币都具有独…

Acwing_799最长连续不重复子序列 【双指针 快慢指针】

题目&#xff1a; 代码&#xff1a; #include <bits/stdc.h> #define int long long #define INF 0X3f3f3f3f #define endl \n using namespace std; const int N 100010; int arr[N];int n; signed main(){std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)…

2.3 Mac OS安装Python环境

Mac OS安装Python环境 和 Linux 发行版类似&#xff0c;最新版的 Mac OS X 也会默认自带 Python 2.x。 我们可以在终端&#xff08;Terminal&#xff09;窗口中输入python命令来检测是否安装了 Python 开发环境&#xff0c;以及安装了哪个版本&#xff0c;如下所示&#xff1…
最新文章