c++(继承、模板进阶)

news2024/11/19 3:15:30

一、模板进阶

1、非类型模板参数

模板参数分类类型形参与非类型形参。

类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。

非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常

量来使用。

namespace arr
{
	// 定义一个模板类型的静态数组
	template<class T, size_t N = 10>
	class array
	{
	public:
		T& operator[](size_t index) { return _array[index]; }
		const T& operator[](size_t index)const { return _array[index]; }
		size_t size()const { return _size; }
		bool empty()const { return 0 == _size; }
	private:
		T _array[N];
		size_t _size;
	};
}

2、模板的特化

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些

错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板

class Date
{
public:
	Date(int year, int month, int day)
		:_year(year)
	{}
	bool operator<(Date& d)
	{
		return _year < d._year;
	}
private:
	int _year = 0;
};
template<class T>
bool Less(T left, T right)
{
	return left < right;
}
int main()
{
	cout << Less(1, 2) << endl; // 可以比较,结果正确
	Date d1(2022, 7, 7);
	Date d2(2022, 7, 8);
	cout << Less(d1, d2) << endl; // 可以比较,结果正确
	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl; // 可以比较,结果错误
	return 0;
}

可以看到,Less绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果。上述示

例中,p1指向的d1显然小于p2指向的d2对象,但是Less内部并没有比较p1和p2指向的对象内

容,而比较的是p1和p2指针的地址,这就无法达到预期而错误。

此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方

。模板特化中分为函数模板特化类模板特化

函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板

  2. 关键字template后面接一对空的尖括号<>

  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型

  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇

怪的错误。

template<class T>
bool Less(T left, T right)
{
	return left < right;
}
// 对Less函数模板进行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
	return *left < *right;
}
int main()
{
	cout << Less(1, 2) << endl;
	Date d1(2022, 7, 7);
	Date d2(2022, 7, 8);
	cout << Less(d1, d2) << endl;
	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl; // 调用特化之后的版本,而不走模板生成了
	return 0;
}

注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该

函数直接给出

bool Less(Date* left, Date* right)
{
	return *left < *right;
}

该种实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化

时特别给出,因此函数模板不建议特化。

3、类模板特化

1、全特化

全特化即是将模板参数列表中所有的参数都确定化。

template<class T1, class T2>
class Data
{
public:
	Data() { cout << "Data<T1, T2>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};
template<>
class Data<int, char>
{
public:
	Data() { cout << "Data<int, char>" << endl; }
private:
	int _d1;
	char _d2;
};
void TestVector()
{
	Data<int, int> d1;
	Data<int, char> d2;
}

2、偏特化

偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。比如对于以下模板类:

template<class T1, class T2>
class Data
{
	public:
	Data() {cout<<"Data<T1, T2>" <<endl;}
	private:
	T1 _d1;
	T2 _d2;
};

偏特化有以下两种表现方式:

部分特化

将模板参数类表中的一部分参数特化。

// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
	public:
	Data() {cout<<"Data<T1, int>" <<endl;}
	private:
	T1 _d1;
	int _d2;
};

参数更进一步的限制

偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一

个特化版本。

//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
	Data() { cout << "Data<T1*, T2*>" << endl; }
private:
	T1 _d1;
	T2 _d2;
};
//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
	Data(const T1& d1, const T2& d2)
		: _d1(d1)
		, _d2(d2)
	{
		cout << "Data<T1&, T2&>" << endl;
	}
private:
	const T1& _d1;
	const T2& _d2;
};
void test2()
{
	Data<double, int> d1; // 调用特化的int版本
	Data<int, double> d2; // 调用基础的模板
	Data<int*, int*> d3; // 调用特化的指针版本
	Data<int&, int&> d4(1, 2); // 调用特化的指针版本
}

二、继承

1、概念

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许我们在保持原有

类特性的基础上进行扩展,增加方法(成员函数)和属性(成员变量),这样产生新的类,称子类。继承呈

现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的函数层次的复

用,继承是类设计层次的复用。

下面我们看到没有继承之前我们设计了两个类Student和Teacher,Student和Teacher都有姓名/地址/

电话/年龄等成员变量,都有identity身份认证的成员函数,设计到两个类里面就是冗余的。当然他们

也有⼀些不同的成员变量和函数,比如老师独有成员变量是职称,学生的独有成员变量是学号;学生

的独有成员函数是学习,老师的独有成员函数是授课。

#include<iostream>
#include<string>
using namespace std;
class Student
{
public:
	// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证
	void identity()
	{
		// ...
	}
	// 学习
	void study()
	{
		// ...
	}
protected:
	string _name = "peter"; // 姓名
	string _address; // 地址
	string _tel; // 电话
	int _age = 18; // 年龄
	int _stuid; // 学号
};
class Teacher
{
public:
	
	// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证
	void identity()
	{
		// ...
	}
	// 授课
	void teaching()
	{
		//...
	}
protected:
	string _name = "张三"; // 姓名
	int _age = 18; // 年龄
	string _address; // 地址
	string _tel; // 电话
	string _title; // 职称
};
int main()
{
	return 0;
}

下面我们公共的成员都放到Person类中,Student和teacher都继承Person,就可以复用这些成员,就

不需要重复定义了,省去了很多麻烦。

class Person
{
public:
	// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证
	void identity()
	{
		cout << "void identity()" << _name << endl;
	}
protected:
	string _name = "张三"; // 姓名
	string _address; // 地址
	string _tel; // 电话
	int _age = 18; // 年龄
};
class Student : public Person
{
public:
	// 学习
	void study()
	{
		// ...
		}
		protected:
		int _stuid; // 学号
};
class Teacher : public Person
{
public:
	// 授课
	void teaching()
	{
		//...
	}
protected:
	string title; // 职称
};
int main()
{
	Student s;
	Teacher t;
	s.identity();
	t.identity();
	return 0;
}

2、继承定义

a、定义格式:

下面我们看到Person是父类,也称作基类。Student是子类,也称作派生类。(因为翻译的原因,所以

既叫父类/子类,也叫父类/子类)

在这里插入图片描述

在这里插入图片描述

3、继承父类成员访问方式的变化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 父类private成员在子类中无论以什么方式继承都是不可见的。这里的不可见是指父类的私有成员还

是被继承到了子类对象中,但是语法上限制子类对象不管在里面还是类外面都不能去访问它。

  1. 父类private成员在子类中是不能被访问,如果父类成员不想在类外直接被访问,但需要在子类中能

访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。

  1. 实际上面的表格我们进行⼀下总结会发现,父类的私有成员在子类都是不可见。父类的其他成员在

子类的访问方式 == Min(成员在父类的访问限定符,继承方式),public > protected > private。

  1. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显

示的写出继承方式。

  1. 在实际运用中⼀般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用

protetced/private继承,因为protetced/private继承下来的成员都只能在子类的类里面使用,实际

中扩展维护性不强。

// 实例演⽰三种继承关系下父类成员的各类型成员访问关系的变化
class Person
{
public:
	void Print()
	{
		cout << _name << endl;
	}
protected:
	string _name; // 姓名
private:
	int _age; // 年龄
}; 
//class Student : protected Person
//class Student : private Person
class Student : public Person
{
protected:
	int _stunum; // 学号
};

4、继承类模板

#include<iostream>
#include<vector>
using namespace std;
template<class T>
class stack :public vector<T>
{
public:
	void push(const T t)
	{
		vector<T>::push_back(t);
	}
	void pop()
	{
		vector<T>::pop_back();
	}
	const T& top()
	{
		return vector<T>::back();
	}
};
int main()
{
	stack<int> st;
	st.push(1);
	st.push(1);
	st.push(1);
	st.push(1);
	for (auto& it : st)
	{
		cout << it;
	}
	return 0;
}

5、父类和子类对象赋值兼容转换

public继承的子类对象 可以赋值给 父类的对象 / 父类的指针 / 父类的引用。这里有个形象的说法叫

切片或者切割。寓意把子类中父类那部分切来赋值过去。

父类对象不能赋值给子类对象。

父类的指针或者引用可以通过强制类型转换赋值给子类的指针或者引用。但是必须是父类的指针是

指向子类对象时才是安全的。这里父类如果是多态类型,可以使用RTTI(Run-Time Type

Information)的dynamic_cast 来进行识别后进行安全转换。

在这里插入图片描述

class Person
{
protected:
	string _name; // 姓名
	string _sex; // 性别
	int _age; // 年龄
};
class Student : public Person
{
public:
	int _No; // 学号
};
int main()
{
	Student sobj;
	// 1.子类对象可以赋值给父类对象/指针/引用
	Person pobj = sobj;
	Person * pp = &sobj;
	Person & rp = sobj;
	//2.父类对象不能赋值给子类对象,这⾥会编译报错
	sobj = pobj;
	return 0;
}

6、继承中的作用域

a、隐藏规则

  1. 在继承体系中父类和子类都有独⽴的作用域。

  2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏。(在子类成员函数中,可以使用父类::父类成员 显示访问)

  3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。

  4. 注意在实际中在继承体系里面最好不要定义同名的成员。

// Student的_num和Person的_num构成隐藏关系,可以看出这样代码虽然能跑,但是⾮常容易混淆
class Person
{
protected:
	string _name = "⼩李子"; // 姓名
	int _num = 111; // ⾝份证号
};
class Student : public Person
{
public:
	void Print()
	{
		cout << " 姓名:" << _name << endl;
		cout << " ⾝份证号:" << Person::_num << endl;
		cout << " 学号:" << _num << endl;
	}
protected:
	int _num = 999; // 学号
};
int main()
{
	Student s1;
	s1.Print();
	return 0;
}

7、子类的默认成员函数

a、四个常见的默认构造函数

  1. 子类的构造函数必须调用父类的构造函数初始化父类的那⼀部分成员。如果父类没有默认的构造函

数,则必须在子类构造函数的初始化列表阶段显示调用。

  1. 子类的拷贝构造函数必须调用父类的拷贝构造完成父类的拷贝初始化。

  2. 子类的operator=必须要调用父类的operator=完成父类的复制。需要注意的是子类的operator=隐

藏了父类的operator=,所以显示调用父类的operator=,需要指定父类作用域

  1. 子类的析构函数会在被调用完成后⾃动调用父类的析构函数清理父类成员。因为这样才能保证子类

对象先清理子类成员再清理父类成员的顺序。

  1. 子类对象初始化先调用父类构造再调子类构造。

  2. 子类对象析构清理先调用子类析构再调父类的析构。

在这里插入图片描述

在这里插入图片描述

class Person
{
public:
	Person(const char* name = "peter")
		: _name(name)
	{
		cout << "Person()" << endl;
	}
	Person(const Person & p)
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}
	Person & operator=(const Person & p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;
		return *this;
	}
	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // 姓名
};
class Student : public Person
{
public:
	Student(const char* name, int num)
		 : Person(name)
		, _num(num)
	{
		cout << "Student()" << endl;
	}
	Student(const Student & s)
		: Person(s)
		, _num(s._num)
	{
		cout << "Student(const Student& s)" << endl;
	}
	Student & operator = (const Student & s)
	{
		cout << "Student& operator= (const Student& s)" << endl;
		if (this != &s)
		{
			// 构成隐藏,所以需要显⽰调用
			Person::operator =(s);
			_num = s._num;
		}
		return *this;
	}
	~Student()
	{
		cout << "~Student()" << endl;
	}
protected:
	int _num; //学号
};
int main()
{
	Student s1("jack", 18);
	Student s2(s1);
	Student s3("rose", 17);
	s1 = s3;
	return 0;
}

8、继承与友元

友元关系不能继承,也就是说父类友元不能访问子类私有和保护成员 。

class Person
{
public:
	friend void Display(const Person & p, const Student & s);
protected:
	string _name; // 姓名
};
class Student : public Person
{
protected:
	int _stuNum; // 学号
};
void Display(const Person & p, const Student & s)
{
	cout << p._name << endl;
	cout << s._stuNum << endl;
}
int main()
{
	Person p;
	Student s;
	// 编译报错:error C2248: “Student::_stuNum”: ⽆法访问 protected 成员
	// 解决⽅案:Display也变成Student 的友元即可
	Display(p, s);
	return 0;
}

9、继承与静态成员

父类定义了static静态成员,则整个继承体系里面只有⼀个这样的成员。无论派生出多少个子类,都只

有⼀个static成员实例。

class Person
{
public:
	string _name;
	static int _count;
};
int Person::_count = 0;
class Student : public Person
{
protected:
	int _stuNum;
};
int main()
{
	Person p;
	Student s;
	// 这⾥的运⾏结果可以看到⾮静态成员_name的地址是不⼀样的
	// 说明子类继承下来了,父子类对象各有⼀份
	cout << &p._name << endl;
	cout << &s._name << endl;
	// 这⾥的运⾏结果可以看到静态成员_count的地址是⼀样的
	// 说明子类和父类共用同⼀份静态成员
	cout << &p._count << endl;
	cout << &s._count << endl;
	// 公有的情况下,父子类指定类域都可以访问静态成员
	cout << Person::_count << endl;
	cout << Student::_count << endl;
	return 0;
}

10、继承模型

单继承:⼀个子类只有⼀个直接父类时称这个继承关系为单继承

多继承:⼀个子类有两个或以上直接父类时称这个继承关系为多继承,多继承对象在内存中的模型

是,先继承的父类在前面,后面继承的父类在后面,子类成员在放到最后面。

菱形继承:菱形继承是多继承的⼀种特殊情况。菱形继承的问题,从下面的对象成员模型构造,可以

看出菱形继承有数据冗余和⼆义性的问题,在Assistant的对象中Person成员会有两份。⽀持多继承就

⼀定会有菱形继承,像Java就直接不⽀持多继承,规避掉了这⾥的问题,所以实践中我们也是不建议

设计出菱形继承这样的模型的。

在这里插入图片描述

class Person
{
public:
	string _name; // 姓名
};
class Student : public Person
{
protected:
	int _num; //学号
};
class Teacher : public Person
{
protected:
	int _id; // 职⼯编号
};
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};
int main()
{
	// 编译报错:error C2385: 对“_name”的访问不明确
	Assistant a;
	a._name = "peter";
	// 需要显⽰指定访问哪个父类的成员可以解决⼆义性问题,但是数据冗余问题⽆法解决
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
	return 0;
}

11、虚继承

class Person
{
public:
	string _name; // 姓名
};
class Student : virtual public Person
{
protected:
	int _num; //学号
};
class Teacher : virtual public Person
{
protected:
	int _id; // 职⼯编号
};
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};
int main()
{
	// 使用虚继承,可以解决数据冗余和⼆义性
	Assistant a;
	a._name = "peter";
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
	return 0;
}

12、继承与组合

• public继承是⼀种is-a的关系。也就是说每个子类对象都是⼀个父类对象。

• 组合是⼀种has-a的关系。假设B组合了A,每个B对象中都有⼀个A对象。

• 继承允许你根据父类的实现来定义子类的实现。这种通过⽣成子类的复用通常被称为白箱复用

(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,父类的内部细节对子类可见

。继承⼀定程度破坏了父类的封装,父类的改变,对子类有很大的影响。子类和父类间的依赖关系

很强,耦合度高。

• 对象组合是类继承之外的另⼀种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对

象组合要求被组合的对象具有良好定义的接⼝。这种复用风格被称为黑箱复用(black-box reuse),

因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。 组合类之间没有很强的依赖关

系,耦合度低。优先使用对象组合有助于你保持每个类被封装。

• 优先使用组合,而不是继承。实际尽量多去用组合,组合的耦合度低,代码维护性好。不过也不太

那么绝对,类之间的关系就适合继承(is-a)那就用继承,另外要实现多态,也必须要继承。类之间的

关系既适合用继承(is-a)也适合组合(has-a),就用组合。

• 很多⼈说C++语法复杂,其实多继承就是⼀个体现。有了多继承,就存在菱形继承,有了菱形继承

就有菱形虚拟继承,底层实现就很复杂,性能也会有⼀些损失,所以最好不要设计出菱形继承。

12、继承与组合

• public继承是⼀种is-a的关系。也就是说每个子类对象都是⼀个父类对象。

• 组合是⼀种has-a的关系。假设B组合了A,每个B对象中都有⼀个A对象。

• 继承允许你根据父类的实现来定义子类的实现。这种通过⽣成子类的复用通常被称为白箱复用

(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,父类的内部细节对子类可见

。继承⼀定程度破坏了父类的封装,父类的改变,对子类有很大的影响。子类和父类间的依赖关系

很强,耦合度高。

• 对象组合是类继承之外的另⼀种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对

象组合要求被组合的对象具有良好定义的接⼝。这种复用风格被称为黑箱复用(black-box reuse),

因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。 组合类之间没有很强的依赖关

系,耦合度低。优先使用对象组合有助于你保持每个类被封装。

• 优先使用组合,而不是继承。实际尽量多去用组合,组合的耦合度低,代码维护性好。不过也不太

那么绝对,类之间的关系就适合继承(is-a)那就用继承,另外要实现多态,也必须要继承。类之间的

关系既适合用继承(is-a)也适合组合(has-a),就用组合。

• 很多⼈说C++语法复杂,其实多继承就是⼀个体现。有了多继承,就存在菱形继承,有了菱形继承

就有菱形虚拟继承,底层实现就很复杂,性能也会有⼀些损失,所以最好不要设计出菱形继承。多

继承可以认为是C++的缺陷之⼀,后来的⼀些编程语⾔都没有多继承,如Java。

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

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

相关文章

装饰器模式decorator

学习笔记&#xff0c;原文链接 https://refactoringguru.cn/design-patterns/decorator 将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为 调用过程 当你调用 encoded.writeData(salaryRecords); 时&#xff0c;控制流首先进入 CompressionDecorator 的 writeData …

微信小程序原生支持TS、LESS、SASS能力探究

文章目录 原生支持开始使用旧项目新建项目TS声明文件更新 功能说明less 使用全局变量sass 使用全局变量 可以参考原文 在之前开发小程序中&#xff0c;无法使用 less/sass 等 css 预编译语言&#xff0c;也无法使用 TS 进行开发&#xff0c;但在最新的编辑器版本中&#xff0c…

面向对象程序设计之模板进阶(C++)

在之前我出过一篇博客介绍了模版的初阶:面向对象程序设计(C)模版初阶&#xff0c;接下来我们将进行模版的进阶学习&#xff0c;介绍关于更多模版的知识 1.非类型模版参数 模板参数分类类型形参与非类型形参 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或…

如何处理DDOS攻击问题

随着信息技术的飞速发展&#xff0c;网络已成为现代社会不可或缺的一部分&#xff0c;极大地便利了个人社交和商业活动。然而&#xff0c;网络空间在创造无限机遇的同时&#xff0c;也潜藏着诸多威胁&#xff0c;其中分布式拒绝服务攻击&#xff08;DDoS&#xff0c;Distribute…

利用Idea远程调试

注意&#xff1a;远程调试不要应用在正式环境中&#xff0c;即便是测试环境也只建议在开发网段使用 在实际的开发过程中&#xff0c;为了验证测试环境的一些问题&#xff0c;且问题在本地不可复现&#xff0c;可以使用远程debug的形式来进行问题定位&#xff0c;而不用循环进行…

如何使用智能合约铸造 NFT —— 以 NftMarket 合约为例

系列文章目录 使用Pinata在IPFS上存储NFT图片的实践&#x1f6aa; scaffold-eth-2使用详细教程&#x1f6aa; 文章目录 系列文章目录前言一、使用到的 OpenZeppelin 库1.1. ERC721 合约1.2. ERC721URIStorage 合约1.3. Counters 合约 二、编写合约代码2.1. 准备NFT元数据2.2. …

【重学 MySQL】二十七、七种 join 连接

【重学 MySQL】二十七、七种 join 连接 union 的使用UNION 的基本用法示例UNION ALL 的用法 七种 join 连接代码实现语法格式小结 union 的使用 UNION 在 SQL 中用于合并两个或多个 SELECT 语句的结果集&#xff0c;并默认去除重复的行。如果希望包含重复行&#xff0c;可以使…

设计模式-行为型模式-访问者模式

访问者模式难以实现&#xff0c;且应用该模式可能会导致代码可读性变差&#xff0c;可维护性变差&#xff0c;除非必要&#xff0c;不建议使用&#xff1b; 1.访问者模式定义 允许在运行时将一个或多个操作应用于一组对象&#xff0c;将操作与对象结构分离&#xff1b; 访问者…

K8s1.28 部署Dashboard获取登录信息

Kubernetes Dashboard 是一个基于 Web 的用户界面&#xff0c;用户可以通过它管理和监控 Kubernetes 集群。它提供了对容器化应用程序的概览、集群资源的状态查看、以及对服务和容器的简单操作管理。 配置 Dashboard 访问的方式&#xff1a; Kubernetes 中的服务类型默认是 C…

语音识别相关概念

声音如何保存成数字信号&#xff1f; 声音是听觉对声波产生的感知&#xff0c;而声波是一种在时间和振幅上连续的模拟量&#xff0c;本质是介质的振动&#xff0c;&#xff0c;比如空气的振动。那么只需要把这个振动信号记录下来&#xff0c;并用一串数字来表达振动信号振动的…

中学生考试成绩在线查询系统

时代在发展&#xff0c;社会在进步&#xff0c;传统的成绩发布方式已经显得力不从心了。老师们&#xff0c;是时候尝试一种更高效、更安全的成绩查询方式了。 还在为如何保护学生隐私而头疼&#xff1f;还在担心成绩的公平性和准确性&#xff1f;易查分小程序将这些这些问题都将…

vue+IntersectionObserver + scrollIntoView 实现电梯导航

一、电梯导航 电梯导航也被称为锚点导航&#xff0c;当点击锚点元素时&#xff0c;页面内相应标记的元素滚动到视口。而且页面内元素滚动时相应锚点也会高亮。电梯导航一般把锚点放在左右两侧&#xff0c;类似电梯一样。 二、scrollIntoView() 介绍 scrollIntoView() 方法会…

加密软件有哪些数据防护功能?

1.文件透明加密&#xff1a;采用透明加密技术&#xff0c;自动对指定类型的敏感文件进行实时加密&#xff0c;确保数据在存储和传输过程中的安全性。 2.权限管理与访问控制&#xff1a;通过细粒度的权限管理&#xff0c;控制员工对敏感数据的访问权限&#xff0c;包括读取、修…

基于SpringBoot+Vue的预制菜平台系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…

蓝桥杯真题——数星星

输入样例&#xff1a; 5 1 1 5 1 7 1 3 3 5 5 输出样例&#xff1a; 1 2 1 1 0 分析&#xff1a; 根据题目&#xff0c;是逐行读入数据&#xff0c;我们要求每颗星星左下方的星星数量&#xff0c;就是要迅速求一个区间内的值 于是我们联想到树状数组来解决问题 代码演示…

商业银行零售业务数智运营探索与应用

一、商业银行零售业务面临新形势 根据国家金融监督管理总局近期发布的数据,2024年一季度商业银行净息差降至1.54%,较2023年四季度的1.69%下降15个基点。在当前经营环境复杂、客户投资预期降低等多重因素的叠加作用下,商业银行经营压力日益加大。与此同时,随着数字化转型的不…

【技术调研】三维(3)-ThreeJs-几何体、材质、贴图、灯光及案例

几何体 ​ 几何体是构建模型的基础,模型=几何体+材质。threejs中已内置了很多几何体。这里不一一介绍。 BufferGeometry 是面片、线或点几何体的有效表述。包括顶点位置,面片索引、法相量、颜色值、UV 坐标和自定义缓存属性值。使用 BufferGeometry 可以有效减少向 GPU 传输…

代码随想录训练营Day3 | 链表理论基础 | 203.移除链表元素 | 707.设计链表 | 206.反转链表

今天任务&#xff1a;学习链表理论基础 链表的类型 链表的存储方式 链表的定义…

基于SpringBoot+Vue+MySQL的招聘管理系统

系统展示 用户前台界面 管理员后台界面 企业后台界面 系统背景 在当今数字化转型的大潮中&#xff0c;企业对于高效、智能化的人力资源管理系统的需求日益增长。招聘作为人力资源管理的首要环节&#xff0c;其效率与效果直接影响到企业的人才储备与竞争力。因此&#xff0c;构建…

linux 操作系统下crontab命令及使用案例介绍

linux 操作系统下crontab命令及使用案例介绍 Linux 操作系统下的 crontab 命令用于设置周期性执行的任务 crontab 命令概述 基本语法 bash crontab [-u user] file crontab [-u user] [-l | -r | -e] [-i] [-s] 主要功能 创建、编辑和管理用户的计划任务&#xff08;cron…