C++:类和对象(下)

news2024/11/23 17:25:23

文章目录

  • 1 再谈构造函数
    • 1.1 构造函数体赋值
    • 1.2 初始化列表
    • 1.3 explicit关键字
  • 2 static成员
    • 2.1 概念
    • 2.2 特性
  • 3 友元
    • 3.1 友元函数(流插入(<<)及流提取(>>)运算符重载)
    • 3.2 友元类
  • 4 内部类
  • 5 匿名对象
  • 6 拷贝对象时的一些编译器优化
  • 7 再次理解类和对象


1 再谈构造函数

1.1 构造函数体赋值

在创建对象时,编译器通过调用构造函数,给对象中的各个成员变量一个合适的初始值。如下:

class Date{
public:
	Date(int year, int month, int day){
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};

虽然上述构造函数调用后,对象中已经有了一个初始值,但是不能将其称之为对对象中成员变量的初始化,构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。


1.2 初始化列表

前言:

我们知道,当我们编写一个类时,只是对这个类整体进行了一个声明,只有当我们通过类的实例化创建出该类类型对象,才算是完成一个对象的定义,但实际上,这只能算是对对象整体的定义,那其中的每个成员变量又是在什么时候定义的呢?如下以一个类为例,这里类中没有主动编写的构造函数,在定义对象时调用编译器默认生成的构造函数。问题来了,当我们在类中增加const成员变量后,再运行程序会发现编译错误,原因是作为内置类型成员变量,编译器默认生成的构造函数不会对其进行处理,而const修饰的变量必须在定义的时候初始化(注意,在C++98时还不能在成员变量声明时给缺省值),此时直接定义对象,const成员变量没有实现初始化,因此会发生编译报错。

class A {
private:
	int _a1;
	int _a2;
	//const int _x; //const变量必须在定义的时候初始化
	//const int _x = 0; //C++98不支持缺省值
};

int main() {
	A aa; //对象整体的定义,每个成员变量什么时候定义?
	return 0;
}

为了解决成员变量初始化的问题,C++引入了初始化列表的概念。


初始化列表:以一个 冒号: 开始,接着是一个以逗号分割的数据成员列表,每个成员变量后面跟一个放在 括号() 中的初始值或表达式。

我们可以理解为初始化列表是调用该构造函数的对象的每个成员变量定义的地方。

示例:

class Date{
public:
	Date(int year, int month, int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
private:
	int _year;
	int _month;
	int _day;
};

初始化列表的相关注意事项:

  • 拷贝构造函数也有初始化列表。

  • 每个成员变量在初始化列表中只能出现一次(换句话说,初始化只能初始一次)。如果某个成员变量没有在初始化列表中,则再使用声明时的缺省值进行初始化,如果缺省值也没有,则以随机值初始化。

  • 类中如果包含以下成员,必须放在初始化列表位置进行初始化:

    • 引用成员变量(必须在定义时进行初始化)
    • const成员变量(必须在定义时进行初始化)
    • 没有默认构造函数的自定义类型成员

      示例:
class A{
public:
	//带参构造函数
	A(int a)
		:_a(a)
	{}
	
private:
	int _a;
};

class B{
public:
	B(int a, int ref)
		:_aobj(a)
		,_ref(ref)
		,_n(10)
	{}
	
private:
	A _aobj; //没有默认构造函数
	int& _ref; //引用
	const int _n; // const 
};
  • 尽量使用初始化列表初始化,因为不管你是否使用初始化列表,类中每个成员变量都会先使用初始化列表进行初始化。对于内置类型成员变量,如果没有显示使用初始化列表,则以缺省值进行初始化,如果缺省值也没有,则以随机值初始化;对于自定义类型成员,如果显示使用了初始化列表,则调用其对应构造函数,如果没有显示使用初始化列表,则调用其默认构造函数,如果默认构造函数也没有,则不能完成初始化,也就不能通过编译 。

    示例:
class Time{
public:
	Time(int hour = 0)
		:_hour(hour)
	{
		cout << "Time()" << endl;
	}

private:
	int _hour;
};

class Date{
public:
	Date(int day)
	{}

private:
	int _day;
	Time _t;
};

int main()
{
	Date d(1);
}
//输出:Time()
  • 成员变量在类中的声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关。
class A{
public:
	A(int a)
		:_a1(a) //先定义_a1
		, _a2(_a1)
	{}

	void Print() {
		cout << _a1 << " " << _a2 << endl;
	}

private:
	int _a2; //先声明_a2
	int _a1;
};

int main() {
	A aa(1);
	aa.Print();
}

//输出:1 -858993460
//即按声明顺序进行初始化,在_a1在_a2之后初始化,而_a2又以_a1的值进行初始化,此时_a1还是随机值,所以_a2为随机值

1.3 explicit关键字

构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值而其余参数均有默认值的构造函数,还可以进行隐式类型转换。而如果不想使其能进行隐式类型转换,可以用关键字 explicit 修饰构造函数。

示例:

class A {
public:
	//单参构造函数 - 支持隐式类型转换
	A(int a)
		:_a1(a)
	{
		cout << "A(int a)" << endl;
	}

	不支持隐式类型转换
	//explicit A(int a)
	//	:_a1(a)
	//{
	//	cout << "A(int a)" << endl;
	//}

	//多参构造函数 - C++98不支持隐式类型转换,C++11可以以{参数……}的方式支持
	A(int a1, int a2)
		:_a1(a1)
		,_a2(a2)
	{}

	不支持隐式类型转换
	//explicit A(int a1, int a2)
	//	:_a1(a1)
	//	, _a2(a2)
	//{}

	A(const A& aa) 
		:_a1(aa._a1)
	{
		cout << "A(const A& aa)" << endl;
	}

private:
	int _a2;
	int _a1;
};

int main() {
	A aa1(1); //单参构造函数
	
	//下面语句发生了隐式类型转换:通过创建一个临时变量将1转换成A类类型对象,再用临时的对象拷贝创建aa2对象
	//按理来说,运行程序,下面语句会调用一次构造函数加一次拷贝构造函数,
	//但实际输出表示只调用了一次构造函数,这是因为编译器进行了优化,直接用1构造了aa2对象
	A aa2 = 1; 
	
	//下面语句如果不加const,则无法通过编译,而加了const则编译通过,
	//正是因为隐式类型转换产生了临时对象,而临时对象具有常属性,
	//因此需要用const修饰才能进行引用,这也侧面说明了隐式类型转换的过程中产生了临时变量
	const A& ref = 2;

	A aa3(1, 2);//多参构造函数
	A aa4 = { 1, 2 };//隐式类型转换 - C++11支持,C++98不支持
	return 0;
}

2 static成员

2.1 概念

声明为 static 的类成员称为类的静态成员,用 static 修饰的成员变量,称之为静态成员变量;用 static 修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化。

示例:要求实现一个类,计算程序中创建出了多少个类对象。

① 方法一:使用全局变量计数

错误示例:

#include <iostream>

using namespace std;

int count = 0;

class A {
public:
	//创建对象要么使用构造函数,要么使用拷贝构造函数
	A() { ++count; } //只要调用构造函数就让计数值加一

	A(const A& t) { ++count; } //调用拷贝构造函数也让计数值加一

	~A() { --count; } //如果对象被析构,则计数值减一
};

int main() {
	cout << count << endl;
	A a1, a2;
	A a3(a1);
	cout << count << endl;
	return 0;
}

上述代码运行后报错,指出count是不明确的符号,这是因为在C++的 xutility(5263,45) 文件中有个与 count 的同名函数 std::count(const _InIt,const _InIt,const _Ty &) ,而我们选择将整个 std 命名空间展开,这就造成了命名冲突。基于此,可以做出以下修改:只将用到 cout 及 endl 展开。

正确写法:

#include <iostream>
using std::cout;
using std::endl;

int count = 0;

class A {
public:
	//创建对象要么使用构造函数,要么使用拷贝构造函数
	A() { ++count; } //只要调用构造函数就让计数值加一

	A(const A& t) { ++count; } //调用拷贝构造函数也让计数值加一

	~A() { --count; } //如果对象被析构,则计数值减一
};

int main() {
	cout << count << endl;
	A a1, a2;
	A a3(a1);
	cout << count << endl;
	return 0;
}

可以看到,使用这种方法统计创建的对象要稍微复杂一些,且因为全局变量可以在任意位置被修改,所以存在安全隐患。

② 方法二:使用静态成员变量计数

#include <iostream>

using namespace std;

class A{
public:
	//创建对象要么使用构造函数,要么使用拷贝构造函数
	A() { ++_scount; } //只要调用构造函数就让计数值加一

	A(const A & t) { ++_scount; } //调用拷贝构造函数也让计数值加一

	~A() { --_scount; } //如果对象被析构,则计数值减一

	//受访问限定符限制,为了保证封装性,提供GetACount()函数来获取静态成员变量值
	//静态成员函数
	static int GetACount1() { return _scount; }

	//非静态成员函数也可以调用静态成员函数
	int GetACount2() { return A::GetACount1(); }

	//静态成员函数只有在包含类类型对象参数时,才能用对象调用非静态成员函数
	static int GetACount3(A& a) { return a.GetACount2(); }

private:
	static int _scount;//静态成员变量
};

int A::_scount = 0;//受类域限制,需以 类名:: 的方式访问

int main(){
	cout << A::GetACount1() << endl; // 类名::静态成员方式调用
	A a1, a2;
	A a3(a1);
	cout << a3.GetACount1() << endl; // 对象.静态成员方式调用
	A a4[10];//对象数组

	cout << a3.GetACount2() << endl;//非静态成员函数调用静态成员函数
	cout << A::GetACount3(a3) << endl;//静态成员函数调用非静态成员函数
	return 0;
}

//输出:0 3 13 13

2.2 特性

  • 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区。
  • 静态成员变量必须在类外定义,定义时不添加 static 关键字,类中只是声明。
  • 类静态成员即可用 类名::静态成员 或者 对象.静态成员 的方式访问。
  • 静态成员函数没有隐藏的 this指针,不能访问任何非静态成员。
  • 静态成员也是类的成员,受public、protected、private访问限定符的限制。
  • 非静态成员函数可以调用静态成员函数,静态成员函数只有在包含类类型对象参数时采用通过对象调用非静态成员函数。

3 友元

友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以友元不建议过多使用。

友元分为:友元函数和友元类


3.1 友元函数(流插入(<<)及流提取(>>)运算符重载)

以下以实现对 流插入运算符>>流提取运算符<< 重载为例说明友元函数使用:

以日期类为例,可以看到,当我们想输出日期时,通常时会先编写一个成员函数,然后通过对象去调用成员函数来输出相应的年、月、日。我们知道,在C++中通常使用 cin >>cout << 进行内置类型的输入输出,那能不能也使用这种方式来对自定义类型对象进行输入输出呢?答案是当然可以。

通过 cplusplus 网站查看,可以发现,实际上 coutcin 分别是 ostreamistream 类型的对象,而之所以在C++中 cin >>cout << 输入输出可以自动识别类型,是因为在 istreamostream 类中分别实现了的对 流提取运算符 >>流插入运算符 << 的重载。也就是说,如果我们也能实现对这两个运算符的重载,那就可以采用 cin >>cout << 的方式进行自定义类型对象的输入输出了。

但这里需要注意的是,该运算符重载是实现在 istreamostream 这两个类中的,而这两个类是无法被修改的,因此我们只能选择在全局实现这两个运算符的重载,或是在需要用到的该运算符的自己编写的类中进行运算符重载。

考虑到为了能访问对象中的私有成员,我们通常会将运算符重载实现在对应的类中,如下:

class Date{
public:
	Date(int year = 2023, int month = 1, int day = 1){
		_year = year;
		_month = month;
		_day = day;
	}

	void operator<<(ostream& out) {
		out << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main() {
	Date d1(2023, 2, 1);
	//cout << d1; //编译报错
	d1 << cout; //输出2023-2-1
	d1.operator<<(cout); //输出2023-2-1
	return 0;
}

但运行程序发现:我们采用 cout << d1; 的方式输出日期时会发生编译报错,而采用 d1 << cout;d1.operator<<(cout); 的方式却可以正常输出。这是因为当我们在日期类中实现运算符重载时,默认第一个参数即为隐含参数 *this ,而对于双目操作符来说,第一个参数即为左操作数。但是这样的输出方式与我们平时的输出写法有所差异,那怎么能采用 cout << d1; 的方式进行输出呢?

于是我们考虑将运算符重载实现在类外,这样我们就可以主动使 ostream 类对象作为第一个参数,而日期类对象为第二个参数。但这样还面临一个问题,我们无法在类外访问私有成员,当然,我们也可以将成员变为公有,但这会失去封装性,一般不建议这样处理;此外,也可以通过设置对应的 Get_year() 等公有成员函数来在类外获取私有成员;而还有一种方法则是使用 friend 修饰函数(即友元函数),可以理解为经过修饰后该函数成为了对应类的朋友,因此可以访问类中私有成员。此外,还有一点需要注意,我们平常使用的流插入运算符是可以连续使用的,也就是说,还运算符重载应该要有返回值,返回 ostream 类型对象的引用。如下所示:

class Date{
	//友元函数声明
	friend ostream& operator<<(ostream& out, const Date& date);
	
public:
	Date(int year = 2023, int month = 1, int day = 1){
		_year = year;
		_month = month;
		_day = day;
	}

	//void operator<<(ostream& out) {
	//	out << _year << "-" << _month << "-" << _day << endl;
	//}

private:
	int _year;
	int _month;
	int _day;
};

ostream& operator<<(ostream& out, const Date& date) {
	out << date._year << "-" << date._month << "-" << date._day << endl;
	return out;
}

int main() {
	Date d1(2023, 2, 1);
	Date d2(2023, 2, 7);
	cout << d1 << d2; 
	//输出:
	//2023-2-1
	//2023-2-7
	return 0;
}

同样的,接下来我们也可以实现流提取运算符重载如下:

class{
	friend istream& operator>>(istream& in, Date& date);
public:
	//其它方法
privateint _year;
	int _month;
	int _day;
};

istream& operator>>(istream& in, Date& date) {
	in >> date._year >> date._month >> date._day;
	return in;
}

int main() {
	Date d1;
	cin >> d1;
	cout << d1;
	return 0;
}

说明:

  • 友元函数可以直接访问类的私有和保护成员,但它不是类的成员函数,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加 friend 关键字。
  • 友元函数不能用 const 修饰。因为 const 只能修饰成员函数,更直接的说 const 是用来修饰 this指针 的,而友元函数没有 this指针。
  • 友元函数可以在类定义的任何地方声明,不受类访问限定符限制。
  • 一个函数可以是多个类的友元函数。
  • 友元函数的调用与普通函数的调用原理相同。

3.2 友元类

  • 友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
  • 友元关系是单向的,不具有交换性。
    如下,在 Time 类中声明 Date 类为其友元类,那么可以在 Date 类中直接访问 Time 类中的私有成员变量,但想在 Time 类中访问 Date 类中的私有成员变量则不行。
class Time{
	friend class Date; //声明日期类为时间类的友元类,则在日期类中就可直接访问Time类中的私有成员变量
public:
	Time(int hour = 0, int minute = 0, int second = 0)
		: _hour(hour)
		, _minute(minute)
		, _second(second)
	{}

private:
	int _hour;
	int _minute;
	int _second;
};

class Date{
public:
	Date(int year = 2023, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}

	void SetTimeOfDate(int hour, int minute, int second){
		//直接访问时间类私有的成员变量
		_t._hour = hour;
		_t._minute = minute;
		_t._second = second;
	}

private:
	int _year;
	int _month;
	int _day;
	Time _t;
};
  • 友元关系不能传递。 (如果 C 是 B 的友元,B 是 A 的友元,也不能说明 C 是 A 的友元。)
  • 友元关系不能继承。

4 内部类

概念:如果一个类定义在另一个类的内部,这个类就叫做内部类。 内部类是一个独立的类,它不属于外部类,更不能通过外部类的对象去访问内部类的成员。外部类对内部类没有任何优越访问权限。

注意:内部类就是外部类的友元类。 内部类可以通过外部类的对象参数来访问外部类中的所有成员。但外部类不是内部类的友元。

特性:

  • 内部类可以定义在外部类的public、protected、private任意位置。
  • 内部类可以直接访问外部类中的 static 成员,不需要外部类的对象/类名。
  • sizeof(外部类) = 外部类 ,和内部类没有任何关系。

示例:

class A {
private:
	static int k;
	int h;

public:
	class B { // B天生就是A的友元
	private:
		int _b = 2;

	public:
		void foo(const A& a) {
			cout << k << endl;//OK
			cout << a.h << endl;//OK
		}
	};
};

int A::k = 1;

int main() {
	A::B b;
	b.foo(A());
	cout << sizeof(A) << endl; //输出:4,其中静态成员变量存储在静态区
	return 0;
}

5 匿名对象

class A{
public:
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}

	~A(){
		cout << "~A()" << endl;
	}
private:
	int _a;
};

class Solution {
public:
	int Sum_Solution(int n) {
		//...
		return n;
	}
};

int main(){
	A aa1;

	// 不能这么定义对象,因为编译器无法识别下面是一个函数声明,还是对象定义
	//A aa1();
	// 
	// 但是我们可以这么定义匿名对象,匿名对象的特点不用取名字,
	// 但是他的生命周期只有这一行,我们可以看到下一行他就会自动调用析构函数
	A();

	A aa2(2);
	// 匿名对象在这样场景下就很好用
	Solution().Sum_Solution(10);
	return 0;
}

输出结果:


6 拷贝对象时的一些编译器优化

在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,在一些场景下还是非常有用的。

示例:

class A
{
public:
	//构造函数
	A(int a = 0)
		:_a(a)
	{
		cout << "A(int a)" << endl;
	}
	//拷贝构造函数
	A(const A& aa)
		:_a(aa._a)
	{
		cout << "A(const A& aa)" << endl;
	}
	//赋值运算符重载
	A& operator=(const A& aa){
		cout << "A& operator=(const A& aa)" << endl;
		if (this != &aa)
		{
			_a = aa._a;
		}
		return *this;
	}

	~A(){
		cout << "~A()" << endl;
	}

private:
	int _a;
};

void f1(A aa)
{}

A f2(){
	A aa;
	return aa;
}

int main(){
	// 传值传参
	A aa1;
	f1(aa1);
	cout << endl;

	// 传值返回
	f2();
	cout << endl;

	// 隐式类型,连续构造+拷贝构造->优化为直接构造
	f1(1);
	// 一个表达式中,连续构造+拷贝构造->优化为一个构造
	f1(A(2));
	cout << endl;

	// 一个表达式中,连续拷贝构造+拷贝构造->优化为一个拷贝构造
	A aa2 = f2();
	cout << endl;

	// 一个表达式中,连续拷贝构造+赋值重载->无法优化
	aa1 = f2();
	cout << endl;
	return 0;
}

结果输出:

说明: 不同的编译器的优化程度不同,从上述结果也可以看出,本文中所使用的VS2022的编译器的优化程度比较大。对于函数 f2 来说,不优化的情况下应该是调用一个构造函数和一个拷贝构造函数,而这里直接优化成了一次构造,显然优化方式稍有些激进,因为函数 f2 中的对象构造和返回是分开的,为了避免中间可能还有使用对象的地方,保守些的编译器在此是不做优化的。


总结:

  • 关于对象返回的总结:
    • 接收返回值对象时,尽量使用拷贝构造方式接收,不要赋值接收;
    • 函数中返回对象时,尽量返回匿名对象。
  • 关于函数传参的总结:
    • 尽量使用 const 类型& 的方式传参。

7 再次理解类和对象

现实生活中的实体,计算机并不认识,计算机只认识二进制格式的数据。如果想要让计算机认识现实生活中的实体,用户必须通过某种面向对象的语言,对实体进行描述,然后通过编写程序,创建对象后计算机才可以认识。比如想要让计算机认识洗衣机,就需要:

  • 用户先要对现实中洗衣机实体进行抽象 – 即在人为思想层面对洗衣机进行认识,洗衣机有什么属性,有哪些功能,即对洗衣机进行抽象认知的一个过程。
  • 经过上述步骤后,在人的头脑中已经对洗衣机有了一个清晰的认识,只不过此时计算机还不清楚,想要让计算机识别人想象中的洗衣机,就需要人通过某种面向对象的语言(比如:C++、Java、Python等)将洗衣机用类来进行描述,并输入到计算机中。
  • 经过上述步骤后,计算机中就有了一个洗衣机类,但洗衣机类只是站在计算机的角度对洗衣机对象进行描述的,通过洗衣机类,可以实例化出一个个具体的洗衣机对象,此时计算机才能清楚洗衣机是什么东西。
  • 接着用户就可以借助计算机中的洗衣机对象,来模拟现实中的洗衣机实体了。

注意:类是对某一实体(对象)来进行描述的,描述该对象具有哪些属性,哪些方法,描述完之后就形成了一种新的自定义类型,使用该自定义类型就可以实例化出具体的对象。


以上是我对C++中类和对象相关知识的一些学习记录总结,如有错误,希望大家帮忙指正,也欢迎大家给予建议和讨论,谢谢!

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

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

相关文章

使用脚本以可读的 JSON 格式显示 curl 命令输出

在我们经常调试微服务或者使用 Elasticsearch API 时&#xff0c;经常会使用curl 来进行调试。但是有时我们的输出不尽如意。显示的不是一 pretty 格式进行输出的。我们有时还必须借助于其他的一些网站工具&#xff0c;比如 Best JSON Formatter and JSON Validator: Online JS…

叮!一大波来自客户的感谢信

春风渐暖&#xff0c;美好如期&#xff0c;祝福的话语在日子的酝酿里更值得期待。神策数据走过 7 载春秋&#xff0c;描绘的大数据分析和营销科技图景在时间的打磨下清晰可见。时光沉淀经验&#xff0c;匠心兑换卓越&#xff0c;这条终点叫做「帮助中国三千万企业重构数据根基&…

类与对象(上)

类与对象(上) 1.面向过程和面向对象初步认识 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题。 C是基于面向对象的&#xff0c;关注的是对象&#xff0c;将一件事情拆分成不同的对象&#xff0c;靠对象之间…

vite从零创建react-ts项目

1.创建vite-react-ts文件夹&#xff0c;初始化项目 npm init 初始化后可以看到包管理文件package.json 2.项目结构 根目录下新建index.html模板文件。以往都是放在public文件夹的&#xff0c;但是官方推荐放在根目录。这是有意而为之的&#xff1a;在开发期间 Vite 是一个服…

qt之条形码与二维码的生成

一、简介 条形码&#xff1a; 条形码(barcode)是将宽度不等的多个黑条和空白&#xff0c;按照一定的编码规则排列&#xff0c;用以表达一组信息的图形标识符。常见的条形码是由反射率相差很大的黑条&#xff08;简称条&#xff09;和白条&#xff08;简称空&#xff09;排…

探讨接口测试以及其用例设计方法

接口测试的总结文档 第一部分&#xff1a;主要从问题出发&#xff0c;引入接口测试的相关内容并与前端测试进行简单对比&#xff0c;总结两者之前的区别与联系。但该部分只交代了怎么做和如何做&#xff1f;并没有解释为什么要做&#xff1f; 第二部分&#xff1a;主要介绍为什…

数据结构:带环单链表基础OJ练习笔记(leetcode142. 环形链表 II)(leetcode三题大串烧)

目录 一.前言 二.leetcode160. 相交链表 1.问题描述 2.问题分析与求解 三.leetcode141. 环形链表 1.问题描述 2.代码思路 3.证明分析 下一题会用到的重要小结论&#xff1a; 四.leetcode142. 环形链表 II 1.问题描述 2.问题分析与求解 Judgecycle接口&#xf…

婴儿监视器美国亚马逊CPC认证ASTM F2951标准要求?

婴儿监视器&#xff0c;又称婴儿监听器、婴儿监护器&#xff0c;英文名为( baby monitor其主要用于用于居家和婴儿的监听和护理。欧美市场上广泛使用&#xff0c;已经存在30年历史的 Baby Monitor是采用现代无线电技术应用于居家和婴儿的监听和护理的好帮手。婴儿监护器由看器(…

基于Jeecgboot前后端分离的ERP系统开发系列--出库单(1)

这次从出库单开始进行整个单据录入显示的模板&#xff0c;不再采用默认的online表单代码生成的方式&#xff0c;以满足实际的业务需要&#xff0c;当然刚开始做&#xff0c;以后还需要进行改进。 一、首先单号生成 采用系统开发里的代码编码规则&#xff0c;相应的修改增加代码…

7、MyBatis框架——MyBatis对一对一关系的处理、分步查询、MyBatis对一对多关系的处理

目录 一、项目框架搭建 二、在实体类中添加额外属性实现多表查询 1、mybatis两表关联查询 &#xff08;1&#xff09;实体类类型映射规则 &#xff08;2&#xff09;代码演示 2、分步查询 &#xff08;1&#xff09;autoMapping开启自动映射 &#xff08;2&#xff09;…

Python-正则表达式详解-快速掌握正则表达式核心函数

正则表达式为为高级的文本模式匹配、抽取或文本形式的搜索和替换功能提供了基础。本文主要介绍python正则表达式的一些基础功能&#xff0c;掌握它也可以使得在python编程中处理字符串游刃有余。1.简介正则表达式是一些由字符和特殊符号组成的字符串&#xff0c;匹配一系列有相…

Ansible的安装及部署

目录 一、Ansible对于企业运维的重大意义 二、Ansible的安装 三、构建Ansible清单 1.直接书写受管主机名或ip&#xff0c;每行一个 2.设定受管主机的组[组名称] 四、Ansible配置文件参数详解 1、配置文件的分类与优先级 2.配置新用户的Ansible配置 3.生成免密认证 本章…

算法 ——世界 二

个人简介&#xff1a;云计算网络运维专业人员&#xff0c;了解运维知识&#xff0c;掌握TCP/IP协议&#xff0c;每天分享网络运维知识与技能。个人爱好: 编程&#xff0c;打篮球&#xff0c;计算机知识个人名言&#xff1a;海不辞水&#xff0c;故能成其大&#xff1b;山不辞石…

Linux管道选取命令:cut、grep

选取命令就是将一段数据经过分析后&#xff0c;取出我们所想要的&#xff0c;或是经历分析关键词&#xff0c;取得我们所想要的那一行 一般来说&#xff0c;选取信息通常是针对一行一行来分析的&#xff0c;而不是整篇信息分析 下面介绍两个很常用的信息选取命令&#xff1a;…

Numpy基础与实例——人工智能基础——机器学习

文章目录一、Numpy概述1. 优势2. numpy历史3. Numpy的核心&#xff1a;多维数组4. 内存中的ndarray对象4.1 元数据&#xff08;metadata&#xff09;4.2 实际数据二、numpy基础1. ndarray数组2. arange、zeros、ones、zeros_like3. ndarray对象属性的基本操作3.1 修改数组维度3…

dubbo接口自动化用例性能优化

dubbo接口自动化用例性能优化 目录&#xff1a;导读 前言 优化本地调试时间 单用例执行时间的优化 提高并发 最后 前言 去年换了一个新部门&#xff0c;看了下当前的自动化用例的情况&#xff0c;发现存在三类性能问题&#xff1a; 本地调试运行时等待时间较长&#xf…

C++学习记录——팔 内存管理

文章目录1、动态内存管理2、内存管理方式operator new operator delete3、new和delete的实现原理1、动态内存管理 C兼容C语言关于内存分配的语法&#xff0c;而添加了C独有的东西。 //int* p1 (int*)malloc(sizeof(int));int* p1 new int;new是一个操作符&#xff0c;C不再需…

【工具】图片和PDF批量区域OCR识别图片文字并重命名,如何批量图片识别文字并将识别的文字改名该图片

前段时间接到一个棘手的难题&#xff08;识别图片文字&#xff0c;将图片文件名改成该文字&#xff09; 因为不解决就得手动挨个挨个输入然后把文件命名好 今天又一个文件需求是这样的 图上有姓名文字&#xff0c;要识别出来改成每一张图跟这个一样&#xff0c;有的人说了缩…

学习笔记——吴恩达《神经网络与深度学习》

神经网络与深度学习1. 基础知识神经网络用神经网络进行监督学习2. 神经网络基础知识二分分类logistic回归logistic回归损失函数梯度下降法导数计算图logistic回归中的梯度下降法m个样本的梯度下降向量化Python中的广播3. 浅层神经网络神经网络概述神经网络表示计算神经网络的输…

Android自动化测试——Monkey

本来是做Web后端的&#xff0c;来公司实习变成微信小程序前端了&#xff0c;到这周变成Android APP测试人员了&#xff0c;也是微醺啊。 由于对手工测试终究是有些抵触&#xff0c;所有昨天小试了一下不用写代码的自动化压力测试&#xff0c;在此记下我的心得。 一、Monkey与…