C++的第一道门坎:类与对象(二)

一.类中生成的默认成员函数详解

0.类的6个默认成员函数

编译器会给类生成六个默认成员函数,在类中即使我们什么都不做,也会自动生成。

默认成员函数:用户没有显式实现,编译器会自动生成的成员函数称为默认成员函数。

下面我们逐个来讲解这些函数。 

1.构造函数

1.1概念

对于以下Date类: 

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;
};

对于我们写的这个类来说,我们使用时需要用Init的公有方法给对象设置日期,但是如果我们每次都要使用Init方法给对象设置日期信息,未免有些过于繁琐,那么有没有办法能够在对象创建时就将信息设置进去呢?

构造函数即可完成这件事情。

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

1.2特性

构造函数虽然叫构造函数,但其作用并不是开一个空间来创建对象而是初始化对象

其特征如下:

  • 函数名与类名相同
  • 无返回值
  • 对象实例化时编译器会自动调用相应的构造函数
  • 构造函数可以重载

下面我们来验证一下以上四个特性:

class Date
{
public:
	Date()//无返回值而且函数名与类名相同
	{
		_year = 2024;
		_month = 5;
		_day = 28;
	}
	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 A;//调用无参构造
	A.Print();
	Date B(3, 4, 5);//调用三个参数的构造
	B.Print();
	return 0;
}

在上面这段代码中,通过打印,我们可以观察到一个调用了无参构造,一个调用了有参构造。

  • 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义,编译器将不再生成默认构造函数。

可以看到,我们在将构造函数屏蔽了之后,依旧是可以通过编译的,因此当我们不显式定义构造函数时,编译器会自动生成默认构造函数。 

现在我们执行下这段代码

我们发现了一个奇怪的现象,这里生成了随机值,那么默认构造函数做了什么事呢?

答案:C++将类型分为了内置类型和自定义类型,内置类型就是语言提供的类型,而自定义类型则是我们使用class/union/struct等关键字自己定义的类型。

而默认构造函数会对自定义类型成员调用它的默认构造函数。

我们可以用以下代码验证一下:

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	int _year;
	int _month;
	int _day;
	Time _a;
};

我们在main函数中初始化了一个Date类型的对象,而Date类型的对象中声明了一个Time类型的对象,由于我们没有写Date类的默认构造函数,因此它会调用编译器自动生成的默认构造函数,这时默认的构造函数所作的事情就是去调用Date类中的自定义类型的构造函数。而Time类的构函数中打印了Time(),因此这段程序的最终输出结果为上图所示。

注意点:在C++11版本中,为我们无法对内置类型生成默认构造的问题打了一个补丁,我们可以在声明时给一个缺省值。

如下图所示

无参的构造函数和全缺省的构造函数都被称为默认构造函数,并且默认构造函数只能有一个。

主要注意的是:无参的构造函数、全缺省的构造函数、编译器生成的构造函数只能有一个,否则会有调用歧义。

譬如下图代码,编译器则会不知道调用的是哪个函数。

总结:不用传递参数就可以调用的构造函数就是默认构造函数。 

2.析构函数

2.1概念

刚刚我们学习了构造函数,现在我们已经知道一个对象是怎么来的了,那么它是怎么没的呢?这就需要析构函数来大显神威了!

析构函数:与构造函数功能相反,析构函数不是完成对对象的销毁,局部对象的销毁工作(生命周期)是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

2.2特性

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

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

下面我们还是来使用一下它

typedef int DataType;
class STack
{
public:
	STack(size_t capacity = 3)
	{
		_a = (DataType*)malloc(sizeof(DataType) * capacity);
		if (!_a)
		{
			perror("malloc fail!");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	void Push(DataType data)
	{
		_a[_size] = data;
		_size++;
	}
	~STack()
	{
		if (_a)
		{
			cout << "我析构啦!" << endl;
			free(_a);
			_a = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _a;
	int _capacity;
	int _size;
};
int main()
{
	STack s;
	s.Push(1);
}

这里我们可以发现,当s的生命周期要结束时,编译器自动调用了析构函数。

  • 与构造函数类似,编译器生成的析构函数也只会调用自定义类型的析构函数
class Time
{
public:
~Time()
{
cout << "我析构啦!" << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本类型(内置类型)
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定义类型
Time _t;
};
int main()
{
Date d;
return 0;
}

 这里不再画序号帮助大家理解过程,而是通过文字帮助大家深入理解原理
 在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?
原因:main方法中创建了Date对象d,而d中包含4个成员变量,其中_year, _month,
_day三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t是Time类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。但是,main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁。main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数。
注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数
 

  • 如果类中没有在堆上申请资源,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。。

3.拷贝构造函数

3.1概念

在现实生活中,我们在超市买小面包,可以认为同一种类型的两袋小面包是一样的。

那么,有没有一样的对象呢?我们能否创建一个跟已有对象一样的对象呢?

这就引入了我们的拷贝构造函数。

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

3.2特性

  • 拷贝构造函数是构造函数的一个重载形式。
  • 拷贝构造函数的参数只有一个而且必须是类类型对象的引用,使用传值方式编译器会报错,因为会引发无穷递归调用。
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
// Date(const Date d) // 错误写法:编译报错,会引发无穷递归
Date(const Date& d) // 正确写法。
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2(d1);
return 0;
}

  • 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造对象按照内存存储字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。(和memcpy的拷贝模式一样)。

对于我们的Date类来说,值拷贝就已经够用了,但是别的类呢?就譬如说我们的栈

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 10)
	{
		_array = (DataType*)malloc(capacity * sizeof(DataType));
		if (nullptr == _array)
		{
			perror("malloc申请空间失败");
			return;
		}
		_size = 0;
		_capacity = capacity;
	}
	void Push(const DataType& data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	size_t _size;
	size_t _capacity;
};
int main()
{
	Stack s1;
	s1.Push(1);
	s1.Push(2);
	s1.Push(3);
	s1.Push(4);
	Stack s2(s1);
	return 0;
}

我们在运行这段代码时会发现它崩溃了,这是为什么呢?我们的s1对象中使用malloc动态开辟了一块内存,而我们是字节序拷贝的,因此我们的s2的array数组也指向了这块内存,而在程序退出时,s2和s1都要销毁,此时s2先销毁,s1再销毁(原因是栈区先进后出),s2已经将malloc的那块内存释放掉了,s1再去释放就会崩溃了。

 因此,我们可以得到如此一个结论:类中如果没有涉及到资源的申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数必须要写。

3.3拷贝构造的使用方法

我们的拷贝构造有两种写法,分别如下:

Stack s2(s1);//新创一个对象s2,并将s1拷贝给s2。
Stack s2 = s1;//这样也是可以完成拷贝的。

4.运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其
返回值类型函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

注意点:

  • 不能通过连接其他符号来创建新的操作符:比如operator@
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐
  • 藏的this
  • .*   ::   sizeof   ?:     .    注意以上5个运算符不能重载。

现在我们来重载一个运算符:

//类内-->成员函数
bool operator==(const Date& d)
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}
//类外-->全局函数
bool operator==(const Date& d1, const Date& d2)
{
return d1._year == d2._year
&& d1._month == d2._month
&& d1._day == d2._day;
}

在这里提一句题外话,我们在写代码的途中,能用引用返回或者引用传值时就尽量用引用,因为它它的效率会比较高。至于能否用引用返回,这需要我们关注他们的生命周期。 

5.赋值运算符重载

  • 参数类型:const Date&, 传递引用可以提高传参效率
  • 返回值类型 Date&,返回引用可以提高返回的效率,有返回值的目的是为了方便连续赋值
  • 检测是否自己给自己赋值
  • 返回*this,以满足连续赋值。
class Date
{
public:
	Date(int year = 1900, 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;
	}
    //成员函数-->第一个参数是隐藏的this指针
	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;
};
//全局函数-->没有隐式的this,需要传递两个参数。
Date& operator=(const Date& Mythis,const Date& d)
{
	if (Mythis != &d)
	{
		Mythis._year = d._year;
		Mythis._month = d._month;
		Mythis._day = d._day;
	}
	return *Mythis;
}

按理来说,上述的代码是可以跑的过去的,但是实际上会产生编译错误。

为什么赋值运算符必须是成员函数呢?

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

如果我们不显式实现赋值运算符的话,编译器生成的赋值运算符是以值的方式逐字节拷贝

注意:

  • 1.内置类型成员变量是直接赋值的,而自定义类型的成员变量需要调用对应的类的赋值运算符重载完成赋值。
  • 需要深拷贝的类需要我们手写赋值运算符,浅拷贝即可解决需求的类用默认生成的即可。

关于运算符重载的知识这里不再阐述,博主会在后续写一篇日期类的实现讲运算符重载更加深入的讲解。 

6.const修饰函数

如果我们想要用const修饰类内某个成员函数的this指针,应该如何实现呢?

CPP将const修饰的成员函数称为const成员函数,而const修饰类成员函数,实际上修饰的是该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

譬如这段代码

class Date
{
public:
	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << "Print()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};

int main()
{
	const Date d2(2022, 1, 13);
	d2.Print();//error
	return 0;
}

 由于我们传递的d2const的,但是在调用Print函数时,*this并没有加const修饰,这就造成了权限的放大,是不被允许的。因此我们应当将Print函数加上const修饰。

	void Print() const
	{
		cout << "Print()" << endl;
		cout << "year:" << _year << endl;
		cout << "month:" << _month << endl;
		cout << "day:" << _day << endl << endl;
	}

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

取地址(&)也是可以重载的,不过一般情况下不需要我们写重载函数。

class Date
{
public:
	Date* operator&()
	{
		return this;
	}
	const Date* operator&()const
	{
		return this;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};

二.struct和class的对比

1struct

1.1struct的成员

在C++中,struct中不仅可以放数据类型,而且可以放函数。这个函数可以在类内定义,也可以在类外定义。

  • 类内定义
#include <iostream>
using namespace std;
struct kuzi
{
	int Add(int a, int b)
	{
		return a + b;
	}
};
int main()
{
	struct kuzi trousers;
	printf("%d", trousers.Add(3, 2));
	return 0;
}

  • 类外定义 

 1.1.2结构体的默认访问权限

在结构体中,为了兼容C语言,数据成员的默认访问权限为public,这也就代表着它们可以在结构体的外部直接进行访问。

如以下代码,我们可以直接给trousers这个对象的a和b进行赋值。

struct kuzi
{
	int a;
	int b;
};
int main()
{
	struct kuzi trousers;
	trousers.a = 3;
	trousers.b = 5;
	return 0;
}

 1.1.3访问权限控制

在C++11的标准中,引入了结构体成员的引用控制修饰符(public,privata,protect),我们可以显式的控制它的成员的访问权限。  

struct kuzi
{
	//默认是public
	int a;
	int b;
private:
	int c;
	int d;
};

1.2.class

在C++中,类(class)是一种重要的概念,可以创建用户定义的数据类型,其中可以包括数据成员成员函数。而且,C++中的类支持继承和多态等面向对象的概念,允许你通过基类创建派生类,实现代码的扩展和重用。剩下的知识点已经讲解过了,这里不再赘述。

1.3结构体Struct和类Class之间的区别以及各自使用场景

C++中的结构体(struct)和类(class)有一些相似之处,但也存在一些关键的区别。以下是结构体和类之间的主要区别:

1、默认访问权限:

结构体的成员默认访问权限是公共(public),因此结构体的成员在外部可以直接访问。
类的成员默认访问权限是私有(private),因此类的成员在外部不能直接访问,需要通过公共的成员函数来访问。
2、成员函数:

类可以包含成员函数,这些函数可以操作类的私有成员,并且可以实现类的行为和功能。
结构体也可以有成员函数,但是它们的主要目的是为了实现一些操作,而不是类似于类的行为。
3、继承:

类可以通过继承实现子类与父类之间的关系,可以使用公共、保护或私有继承来控制成员的访问权限。
结构体也可以继承,但由于其成员默认是公共的,继承可能导致访问权限问题,当然我们也可以控制其访问权限,让其可以继承。
4、构造函数和析构函数:

拥有构造函数和析构函数,用于对象的初始化和清理。
结构体也可以有构造函数和析构函数(但不会默认生成),但是它们的使用场景通常是比较简单的数据封装。
5、默认成员访问标签(Access Labels):

在类和结构体中,可以使用访问标签(public、private、protected)来指定成员的访问权限。

6、 使用场景:

结构体的使用场景:

用于存储一组相关的数据,但没有复杂的操作和逻辑。
类的使用场景:

当你需要封装数据并附加操作和行为时,类更适合,因为它可以将数据和操作封装在一起。
在实现更复杂的数据结构,如树、图等,使用类也更加合适。
总结:
  虽然结构体和类在某些方面很相似,但它们的默认行为、访问权限、使用场景以及是否支持面向对象编程的特性(如继承、多态等)都有一些差异。在选择使用结构体还是类时,需要考虑你的代码的需求和设计目标。但是,结构体可以做的事类都可以干,结构体不可以干的事类也都可以干!

码字不易,如果你觉得博主写的不错,给个三联关注评论把!!!

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

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

相关文章

ubuntu 配置用户登录失败尝试次数限制

前言&#xff1a; 通过修改pam配置来达到限制密码尝试次数&#xff01; 1&#xff1a;修改 /etc/pam.d/login 配置&#xff08;这里只是终端登录配置&#xff0c;如果还需要配置SSH远程登录限制&#xff0c;只配置下面的 /etc/pam.d/pam.d/common-auth 即可&#xff09; vim…

如何克隆非默认分支

直接git clone下来的我们知道是默认分支&#xff0c;那如何克隆其他分支呢&#xff1a; 比如这个&#xff0c;我们想克隆AdvNet。 我们可以在本地文件夹打开Git Bash 依次输入&#xff1a; git clone --branch AdvNet https://github.com/wgcban/SemiCD.git cd SemiCD git b…

【全开源】旅游系统源码(Uniapp+FastAdmin+ThinkPHP)

一款基于UniappFastAdminThinkPHP开发的旅游系统&#xff0c;包含消费者端&#xff08;手机端&#xff09;、机构工作人员&#xff08;手机端&#xff09;、机构端&#xff08;PC&#xff09;、平台管理端&#xff08;PC&#xff09;。机构可以发布旅游线路、景点项目&#xff…

计算机网络学习笔记——网络层(b站)

目录 网络层概述 网络层提供的两种服务 ①面向连接的虚电路服务 ②无连接的数据报服务 IPv4 路由选择 路由器转发IP数据报 静态路由选择 动态路由选择 路由信息协议RIP 开放最短路径优先OSPF&#xff08;Open Shortest Path First&#xff09; 内部网关协议IGP&…

【MATLAB】数字滤波器的设计

一、引言 在信号处理过程中&#xff0c;所处理的信号往往混有噪声&#xff0c;从接收到的信号中消除或减弱噪声是信号传输和处理中十分重要的问题。根据有用信号和噪声的不同特性&#xff0c;提取有用信号的过程称为滤波,实现滤波功能的系统称为滤波器。在以往的模拟电路中用的…

10.RedHat认证-Linux文件系统(上)

10.RedHat认证-Linux文件系统(上) ⽂件系统&#xff0c;顾名思义&#xff0c;是⼀个组织⽂件的“系统(system)”。file system ⽂件系统是⽤来组织⽂件的&#xff0c;通俗⼀点理解的话&#xff0c;⽂件系统是⽤来存储⽂件的。 硬盘是不能直接存放⽂件或数据。 我们通过将硬…

美光EMMC芯片丝印型号查询 8LK17/D9PSK, OXA17/JY997

问题说明 最近在使用美光EMMC的时候&#xff0c;发现通过芯片丝印查询不到 芯片的规格说明书&#xff1b; 经过查阅资料&#xff0c;发现美光的EMMC芯片 “由于空间限制&#xff0c;FBGA 封装组件具有与部件号不同的缩写部件标记”&#xff0c;需要通过官网查询丝印的FBGA cod…

百度智能云千帆AppBuilder升级!开放多源模型接入,思考模型再次加速!

>>【v0.5.4版本】 上线时间&#xff1a;2024/5/24 关键发版信息&#xff1a; 大模型优化&#xff1a;开放多源模型接入&#xff0c;思考模型再次加速&#xff01; Agent思考模型&#xff1a;新增AppBuilder专用版模型ERNIE Speed-AppBuilder&#xff0c;自主任务规划…

Euler 欧拉系统介绍

Euler 欧拉系统介绍 1 简介重要节点与版本EulerOS 特色EulerOS 与 openEuler 区别联系Euler 与 HarmonyOS 区别联系 2 openEuler特色支持 ARM&#xff0c;x86&#xff0c;RISC-V 等全部主流通用计算架构融入 AI 生态嵌入式实时能力提升引入 OpenHarmony 一些突出功能 参考 1 简…

BIO/NIO学习

在传送文件的时候常常出现这么一个问题&#xff0c;就是当客户端的文件全部传送完了之后&#xff0c;服务器没有接收到客户端那边传过的停止信号&#xff0c;所以服务器也就跟着客户端停止运行了&#xff0c;我们可以使用 try {socket.shutdownOutput();} catch (IOException e…

VSCode安装platformio插件

文章目录 一、安装VSCode二、安装platformio&#xff08;一&#xff09;整理文件夹&#xff08;二&#xff09;整理Python环境&#xff08;三&#xff09;安装platformio 三、创建ESP8266项目四、使用命令行创建项目五、创建项目太久怎么办六、参考链接 一、安装VSCode VSCode…

这款网站测试工具,炫酷且强大!【送源码】

随着互联网的普及和发展&#xff0c;Web 应用程序的数量也越来越多&#xff0c;各种网络问题也是层出不穷&#xff0c;因而监测这些 Web 应用程序的性能和可用性变得非常重要。 今天的文章&#xff0c;了不起和大家分享一款十分好用的的网站分析项目 - Web-Check。 项目简介 …

xss-labs之level9、level10

一、level9 1、测试分析 尝试了之前的payload&#xff0c;发现都不行&#xff0c;看源码发现多了个strpos函数&#xff0c; strpos() 是一个在 PHP 中用于查找子串首次出现位置的函数。它接受两个参数&#xff1a;要搜索的字符串&#xff08;主字符串&#xff09;和要查找的子…

【PHP【实战训练】系统性学习】——最经典的web端头像上传,数据库内容安全精简

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

DSP6657 GPIO中断学习

1 简介 使用创龙板卡的KEY2按键通过中断的方式控制LED3的亮灭 2 中断学习 在C665x设备上&#xff0c;CPU中断是通过C66x CorePac中断控制器进行配置的。该中断控制器允许最多128个系统事件被编程到任意12个CPU可屏蔽中断输入&#xff08;CPUINT4至CPUINT15&#xff09;、CPU…

Spring:事务

1. 简介 spring对jdbc进行封装&#xff0c;简化对数据库的操作 2. HelloWorld 1. 搭建模块 2.加入依赖 <dependencies><!--spring jdbc Spring 持久化层支持jar包--><dependency><groupId>org.springframework</groupId><artifactId>s…

5.28OpenMV入门

10分钟快速上手 OpenMV中文入门教程 使用的元件 先安装好&#xff0c;上述链接上手 IDE显示颜色阈值&#xff0c;同时也配有示例文件&#xff0c;如下图打开&#xff0c;helloworld 你好&#xff0c;世界&#xff01; OpenMV中文入门教程&#xff0c;在官方也有每一个的详细…

那智不二越机器人维修案例分享

那智不二越工业机器人在工业范围内广泛应用于各种生产领域。其示教器作为人机交互的重要设备&#xff0c;常常需要定期维护和Nachi不二越机械手示教盒修理。 【Nachi不二越机器人示教器维修步骤】 1. 关闭电源 在进行任何那智不二越机器人维修操作之前&#xff0c;务必确保机器…

黑马es0-1实现自动补全功能

1、安装分词器 上github上找人做好的分词器&#xff0c;放到es-plugin数据卷里&#xff0c;然后重启es即可 2、自定义分词器 elasticsearch中分词器(analyzer)的组成包含三部分: character filters:在tokenizer之前对文本进行处理。例如删除字符、替换字符 …

如何开启tomcat管理功能

1 、修改conf/tomcat-users.xml <role rolename"manager-gui"/> <user username"tomcat" password"tomcat" roles"manager-gui"/>2、修改webapps/manager/META-INF/context.xml <Valve className"org.apache.cata…