类和对象(下)
类和对象(下)
- 再谈构造函数
- 构造函数体赋值
- 初始化列表
- explicit关键字
- static成员
- 静态成员的特性
- 友元
- 友元函数
- 友元类
- 成员函数做友元
- 内部类
- 匿名对象
- 编译器的一些优化
再谈构造函数
构造函数体赋值
在创建对象的时候编译器会调用构造函数给对象中的成员变量一个合适的初始值:
class Date
{
public:
Date(int year, int month, int day)
{_year = year;_month = month;_day = day;
}
private:
int _year;
int _month;
int _day;
};
虽然在调用构造函数过后,对象中的每个成员都有了合适的值,但是不能将其称为对对象中的成员变量的初始化,构造函数中的语句最多叫赋初值(赋值),因为初始化只能进行一次,赋值可以进行很多次!
初始化列表
我现在创建了一个对象:Date d1(2022,2,3);
那么在我实例化d1对象的时候,_year、_month、_day的空间也随之开好了,现在我们要的对这块空间进行初始化啊!那么到底在哪里完成对这些成员变量的初始化呢?
祖师爷最后最后规定在执行构造函数之前先进行初始化,于是他将初始化这个动作放在了构造函数的后面,语言上不变描述那个位置,我们用图片表示:
这块进行初始化操作的区域,被称为初始化列表!
对于这块区域的写法,祖师爷也是有规定的:初始化列表以":
"开头,
分隔 , 每个成员变量后面跟一个放在()
里面的初始值或表达式;
具体写法如下:
当然对于自定义类型子类,我们也能让其调用指定的属于它的构造函数来对其进行初始化:
那么为啥会有初始化列表勒?
主要是因为我们可能会在类的成员变量中定义一些const修饰的成员变量或引用成员变量:
我们在实例化出对象过后,其成员变量的空间也随之开辟完毕,对于普通成员变量,我们可以不用初始化,但是对于const修饰的成员变量与引用成员变量必须初始化,因为这些变量只有一次初始化的机会;
未对const修饰的成员变量及引用成员变量进行初始化,编译器出现了报错:
因此为了解决某些成员变量必须初始化的问题,C++就提出了初始化列表的操作,在初始化列表区间完成成员变量的初始化;后来随着C++的不断发展,在C++11中允许在成员变量声明的时候给缺省值,这样的作法也是可以的,但是在以前的C++标准中只有初始化列表才能解决这个问题;
注意:
1、每个成员变量只能在初始化列表出现一次;
2、const修饰的成员便量、引用成员变量、没有默认构造函数的自定义类型,必须进行初始化,那么为什么没有默认构造函数的自定义类型必须初始化呢?因为如果我们没有显示初始化自定义类型的话,编译器就会调用默认构造函数来完成初始化,但是自定义类型又没得,编译器就会报错;
3、尽量使用初始化列表,因为不管你是否使用初始化列表,编译器都会先去初始化列表看一看,然后再去成员变量的声明处看看是否有缺省值,对于未在初始化列表初始化的成员变量就使用对应函数声明出给的缺省值进行初始化!最后才去执行构造函数体!
4、在初始化列表,编译器会按按照成员变量的声明顺序来初始化,与成员变量在初始化列表的位置顺序无关;
我们通过一个例子来观察这种现象:
允许结果截图:
那么为什么会出现这种情况?
根据上面的理论,aa对象调用了A(int a)构造函数,但是编译器不会马上去执行函数体,而是先去初始化列表看一看,有没有初始化的操作,编译器发现的确有,于是按照成员变量的声明顺序,先对_a2初始化,_a2是用_a1初始化的,但此时_a1还没完成初始化,里面是随机值,因此_a2也就被初始化成随机值,随后编译按照顺序再完成了_a1的初始化,_a1是用a初始化的,a是1;因此最后我们看到的结果是_a1输出1,_a2输出随机值;
explicit关键字
对于只有一个参数的或者第一个参数没有给缺省值其余参数都给缺省值的构造函数具有类型转换的作用;
我们来看看实例,来详细介绍构造函数的类型转换作用:
为什么10(int)可以用来给d1(Date)初始化?
主要是因为int类型向Date类型赋值进行了隐式类型转换10并不是直接赋值给d1的,具体作法就是:
那么编译器是如何通过10来构造Date类型的临时变量的呢?
我的理解是:编译器首先实例化好一个Date类型的临时变量,然后将10作为Date类型的构造函数的参数来构建临时变量,这样就完成了从int类型向Date类型的转换,是谁完成这次转换工作的?是构造函数,因此才说构造函数具有类型转换的作用!
但是,我们会发现这是不是上面的转换是不是很麻烦又要创建临时变量、又要完成拷贝构造;况且我们都能用10来构造临时变量,那么我能不能直接用10来构建d1?当然是可以的,在一些比较新的编译器基本上都采用了这种优化!减少了不必要的中间过程,提高了程序效率,何乐而不为?这是编译器最喜欢干的!当然比较老的编译器(比如:VC++6.0)就没有采用上面的优化,还是一步一步走的!(博主用的是VS2022)
为此对与Date d1=10;这条语句我们会看到d1会去调用Date(int day)这个构造函数,而不会去调用
拷贝构造;我们通过运行结果来验证:
我们会发现的确实这样;当然如果我们想要取消构造函数的的类型转换功能(也就是取消利用其他类型来构建Date对象)我们可以在构建值类型与构造函数类型相匹配的构造函数前面加上explicit关键字来修饰,以此来取消该构造函数的类型转换功能;比如:我们现在不想让利用int类型转换为Date类型,我们就可以在Date(int day);构造函数前面加上explicit来修饰,就相当于取消了int类型转换为Date类型的“桥梁”,如果我们再次使用int类型转换为Date类型编译器就会报错:
我们删掉explicit就又可以从int转换为Date类型了;
当然我们上面说的是 “对于只有一个参数的或者第一个参数没有给缺省值其余参数都给缺省值的构造函数具有类型转换的作用” ,这是C98的标准,在C11中具有多个参数的构造函数也具有类型转换的作用,也就是说我们可以用{10,20,34}这种方式来构建Date类,前提是存在这样的Date(int,int,int)这样的构造函数,为此我们可以来实验一把:
运行截图:
当我们对Date(int year, int month, int day=1)构造函数加上explicit关键字修饰时编译器无法完成从{12,13}类型向Date类型及{1,1,2}类型向Date类型的转换,编译器会报错;
你删掉explicit程序又可以跑起来了!
static成员
static成员:
用static修饰的成员变量被称为静态成员变量;
用static修饰的成员函数被称为静态成员函数,静态成员函数没有this指针,也就是说对于静态成员函数来说不需要知道是哪个具体的对象在调用它!,;
静态成员(成员变量和成员函数)由所有对象共享!不属于某个具体的对象,对于静态成员来说不需要知道是那个具体的对象在调用它们,因此对于静态成员来说,我们既可以通过对象调用,也可以通过类域调用!;
同时静态成员变量不能在初始化列表进行初始化,也不能在声明处给缺省值,这些地方都是针对非静态成员变量的!静态成员变量只能在类外初始化,初始化的时候需要指定是那个类域下的静态成员变量;
面试题: 实现一个类,并统计利用该类实例化了多少个对象!
分析: 我们知道每实例化一个对象必调用构造函数,那么我闷就可以从构造函数内部入手,我们可以定义一个全局变量,没实例化一个对象就在构造函数内部++这个全局变量;
代码如下:
#include<iostream>
using namespace std;
int g_count = 0;
class Date
{
public :Date(){g_count++;cout << "Date()" << endl;}Date(int year, int month, int day){g_count++;cout << "Date(int year, int month, int day)" << endl;}Date(const Date& d){g_count++;cout << "Date(const Date& d)" << endl;}
private:int _year;int _month;int _day;
};
void Func(Date d)
{Date d1;Date d2(2022, 2, 2);Date d3(d2);
}
int main()
{Date d0;Func(d0);cout << g_count << endl;return 0;
}
运行结果:
的确实创建了5个对象;
这样的代码确实能够得到我们想要的结果,但是如果有时候我们不小心,对g_count手动++了呢?
g_count作为全局变量,太自由了!任意位置都能访问!容易造成数据的滥用;
为此我们需要限定一下g_count的自由,但同时生命周期要是整个程序的!
为此我们可以利用static修饰的成员变量来完成,最后返回静态变量的值就好了;
运行结果:
的确也是符合预期的,但是现在有个问题,就是我们想调用GetCount()函数的话,还要专门创建一个对象,这是不是就在我们的预期上多了一个,就比如原先程序上只有5个对象,然后让你统计这5个对象的个数,为了统计出这5个对象,我们还得创建一个对象用来专门调用GetCount函数,因为GetCount函数是动态成员函数,需要传递this指针,必须通过对象调用;所以我们统计到的对象会比原来的多一个,既然都知道了多一个我们在输出的时候直接减去不就好了嘛!这是在我们知道的情况下,假设我们不知道真实情况呢?所以我们的代码还需要改进,有没有不需要通过对象调用的函数呢?
当然有,静态成员函数嘛!静态成员函数是不需要传递this指针的,也就是说静态成员函数不需要知道是谁调用了它,但是它还是一个成员函数,属于该类域,我们就可以通过类域去调用它!因此我们可以将GetCount函数变为静态函数:
运行结果:
静态成员的特性
1、静态成员不属于某个具体对象,为所有对象共享,存放在静态区;
2、静态成员既可以通过对象访问,也可以直接用类域访问;
3、静态成员变量必须在类外定义,在类内的只是声明,并没有在内存中开辟实际空间!只有在类外定义过后才会有实际空间,才能被使用;
4、静态成员函数没有隐藏的this指针,因此在静态成员函数内部不能访问非静态成员变量;
5、静态成员也是类的成员,也受访问限定符的限制!
提问:
1、静态成员函数可以调用非静态成员函数吗?
答:可以!只不过我们需要创建对象,然后通过对象来访问非静态成员,无法直接访问非静态成员函数,因为静态成员函数内部没有this指针,调用非静态成员函数是需要传递this指针,如果在静态成员函数内部直接访问非静态成员函数的话,无法完成this指针的调用,编译器会报错;
2、非静态成员函数可以调用静态成员函数吗?
答:可以!调用静态成员函数不需要this指针,可以完成传参要求,自然也就可以调用静态成员函数;同时非静态成员函数有this指针,也可以调用其他非静态成员函数!
友元
友元提供了一种突破封装的方式,有时提供了便利。但是友元会增加耦合度,破坏了封装,所以
友元不宜多用。
友元分为:友元函数和友元类
友元函数
问题:现在尝试去重载operator<<,然后发现没办法将operator<<重载成成员函数。因为cout的
输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作
数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以要将operator<<重载成
全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>>同理。
class Date
{
public:
Date(int year, int month, int day): _year(year), _month(month), _day(day)
{}
// d1 << cout; -> d1.operator<<(&d1, cout); 不符合常规调用
// 因为成员函数第一个参数一定是隐藏的this,所以d1必须放在<<的左侧
ostream& operator<<(ostream& _cout)
{_cout << _year << "-" << _month << "-" << _day << endl;return _cout;
}
private:
int _year;
int _month;
int _day;
};
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
class Date
{
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _cin, Date& d);
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout << d._year << "-" << d._month << "-" << d._day;
return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{
_cin >> d._year;
_cin >> d._month;
_cin >> d._day;
return _cin;
}
总结:
1、友元函数可以访问类的私有、保护、公有成员,但不是类的成员函数;
2、可以在类内任何地方声明,声明的时候需要加上friend关键字修饰;
3、友元函数不能加const,因为对于成员函数来说加上const并不是用来修饰函数本身的,而是修饰*this的,友元函数是类外的一个函数不属于成员函数,自然不可能拥有this指针!this指针都没有const自然无法修饰;
4、一个函数可以是多个类的友元函数;
5、友元函数与普通函数调用方式一样!
友元类
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 = 1900, 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;
};
总结:
1、友元关系是单向的,不具有交换性。
比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接
访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。
2、友元关系不能传递
如果C是B的友元, B是A的友元,则不能说明C时A的友元。
3、友元关系不能继承,在继承位置再给大家详细介绍。
成员函数做友元
欢迎跳转至我的另一篇博客:C++核心编程(三)
内部类
概念: 如果把一个类定义在另一个类的内部,这个类就叫做内部类;
内部类是一个独立的类,不属于外部类,不能通过外部类的对象访问内部类的成员,外部类对于内部类来说没有任何访问权限;但是反过来,在内部类中,可以无限制的访问外部类的所有成员,简而言之就是内部类是外部类天然的友元类!
特性:
1、内部类只是在外部类的类域中,在利用外部类实例化出来的对象不包含内部类实例化出来的空间;
这一点我们可以通过sizeof来验证:
利用A类实例化出来的对象大小是4字节,而非8字节!如果是8字节的话,那么就说明A实例化出来的对象的空间包括了B类的对象的空间!
2、我们无法直接使用内部类来实例化对象,因为内部类的定义是在外部类的作用域下面的,不在全局域;如果我们直接使用内部类实例化对象的话,编译器在全局域找不到内部类的定义,会直接报错!
因此我们需要显示的告诉编译器内部类的定义所在的作用域,让其去外部类的作用域下寻找内部类的作用域;
3、内部类的访问也是受外部类的访问限定符的限制,如果我们将内部类定义在外部类的protected、private区域,我们在外部类的外部是无法通过外部类::内部类 对象名
这样的方式实例化内部类,编译器会告诉我们不可访问!
4、在内部类内部可以直接访问外部类的静态成员,可以不需要通过类名::
或对象.
的方式调用;
匿名对象
顾名思义就是没有名字的对象;
class A
{
public:A():_a(0){cout << "A()" << endl;}A(int a) :_a(a){cout << "A(int a)" << endl;}A(const A& a):_a(a._a){cout << "A(const A& a)" << endl;}~A(){cout << "~A()" << endl;}void test(){cout << "void test()" << endl;}
private:int _a;
};
int main()
{
//匿名对象的生命周期只有所在行,离开所在行就会被析构掉;A();//这是一个匿名对象A(1);//这也是一个匿名对象A(A(1));//这也是一个匿名对象return 0;
}
编译器的一些优化
在传参和传返回值的过程中,一般编译器会做一些优化,减少对象的拷贝,这个在一些场景下还
是非常有用的。
演示代码:
class A
{
public:A():_a(0){cout << "A()" << endl;}A(int a) :_a(a){cout << "A(int a)" << endl;}A(const A& a):_a(a._a){cout << "A(const A& a)" << endl;}~A(){cout << "~A()" << endl;}void test(){cout << "void test()" << endl;}A& operator=(const A& a){cout << "A& operator=(const A& a)"<<endl;_a = a._a;return *this;}
private:int _a;
};
void fun1(A a)
{}
A fun2()
{A a;return a;
}
首先我们来看第一组:
正常情况下aa1调用A()构造函数,调用fun1然后aa1传参给形参,形参会调用拷贝构造函数;
最后程序结束;
我们来看看结果是不是这样:
这个没什么可说的,也没有可以优化的地方,是正常情况!
我们再来看看第二组:
首先程序先去调用fun2,来到fun2内部,调用A()构造函数初始化a对象,然后返回a对象;
一般情况下,对于这种值返回的函数并不是直接将值返回,而是按照下面这种方式:
那么综上所述:编译器先调A()构造函数初始化a对象,然后再调用的拷贝构造函数初始化的临时对象;
通过程序验证一遍:
我们会发现编译器并没有调用拷贝构造函数,这是为什么呢?
主要就是因为编译嫌麻烦!你看啊,编译器都可以用返回值来初始化临时变量的,那么为啥不直接用返回值来构造接受对象呢?
也就是说在编译器看来,一个表达式中,连续构造+拷贝构造->优化为一个构造,编译器先麻烦,对其进行了优化;省去了中间商赚差价(临时变量消耗时间),有利于提高程序运行效率;
为此我们没有看到拷贝构造函数;
第三组:
这是int类型传给A类型,会出现隐式类型转换,一般情况如下:
也就是说在一般情况下,我们会看到编译器先调用A(int)构造函数,再调用拷贝构造函数;
但是这么绕圈的方式,太麻烦了,编译想一步到位,根据 “一个表达式中,连续构造+拷贝构造->优化为一个构造”的规则,编译器就直接拿着这个1去构造形参,减少了中间商赚差价,提高了程序运行效率;
为此经过优化过后,编译器只会去调用A(int)构造函数来实现(int)到(A)的类型转换;
第四组:
一般情况:
根据“一个表达式中,连续构造+拷贝构造->优化为一个构造”的规则
编译器会直接拿着1去构建形参对象a,因此编译器只会调用A(int)构造函数来完成此次类型转换!不会生成匿名对象;
运行结果:
第五组:
我们来仔细推敲一下:
也就是说从return语句开始:调用临时对象的拷贝构造函数、调用aa1的拷贝构造函数,这样的连续拷贝让编译器受不了了,编译器对其进行了优化,直接直接绕过临时变量这个中间商,直接调用aa1的拷贝构造函数,将a对象的值拷贝给aa1;
为此我们最后看到的是编译器只会:调用A()构造函数(初始化a对象)、拷贝构造函数(初始化aa1);
对于一些激进的编译器,会将对象a的构造函数也优化了,直接拿初始化a的值来初始化aa1;
为此我们看到的就只有编译器调用A(),这种比较激进的结果在VS2022下可以看到:
一般来说编译器是不会将A a调用构造函数也加入优化,因为和reture语句都不是同一行,贸然的优化进来会出现风险和bug!
我们拿比较稳妥的结果,总结出一套优化结论:
一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造
第六组:
在比较稳定的编译器(不激进的编译器)下,这个过程是无法优化的,因为既满足:一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造的规则,也不满足“一个表达式中,连续构造+拷贝构造->优化为一个构造”的规则,编译器只能老老实实的按照原样走:我们就会看到编译器调用:A()、A()、A(const A&)、A& operator=(const A& a)这些函数;
但是对于一些比较激进的编译器,他们会优化掉拷贝给临时对象的过程,直接拿返回值赋值给接受对象,VS2022下就是这样做的:
总结:
1、为了提高程序运行效率,参数传递尽量用引用,尽管使用值传递的方式大多有优化,但是都需要去调用构造函数,会比较浪费时间,使用引用的话可以避免拷贝、调用构造函数;
2、尽量使用定义对象的方式接受值传递的返回值,避免使用赋值的方式接收值传递的返回值;通过对比第五组、第六组函数调用,明显使用第五组的方式接受返回值的效率更高!(我们讨论的是在不激进的编译器下的优化)
3、尽量使用匿名对象充当返回值;
相关文章:

类和对象(下)
类和对象(下)再谈构造函数构造函数体赋值初始化列表explicit关键字static成员静态成员的特性友元友元函数友元类成员函数做友元内部类匿名对象编译器的一些优化再谈构造函数 构造函数体赋值 在创建对象的时候编译器会调用构造函数给对象中的成员变量一…...
达梦数据库单机部署
一、安装前准备 1. 安装环境 操作系统:redhat7.9 达梦数据库版本:V8 内存:2G CPU:x86_64 2. 新建用户组和用户 groupadd dinstall useradd -g dinstall -m -d /home/dmdba -S /bin/bash dmdba passwd dmdba3. 配置参数 vi /etc/security/limits.conf #在末尾添加以下内…...
从零到一学习Flutter——(二)状态和路由
背景 前文提到了Widget的状态,在Flutter中一切都是Widget,那么由Widget组成的页面,会有很多复杂的父子关系,要想交互友好,则需要这些Widget进行通讯,也就是所谓的状态管理。 同时在了解了布局之后,我们会写出很多的页面,那么在这些页面切换,也是一个很重要的能力。 …...

TC358774XBG/TC358775XBG替代方案|CS5518替代TC358774XBG/TC358775XBG设计DSI转LVSD设计资料
TC358774XBG/TC358775XBG替代方案|CS5518替代TC358774XBG/TC358775XBG设计DSI转LVSD设计资料 TC358774XBG/TC358775XBG 芯片的主要功能是作为 DSI - LVDS 通信协议桥接,主芯片的视频数据可通过 DSI 链路流 出,以驱动兼容 LVDS 的显示板。换句话说&#x…...

Linux---Kernal与Shell讲解
目录 Shell简介 什么是Shell Shell分类 内核Kernal Shell简介 什么是Shell 我们首先需要知道一台完整的计算机是由硬件组成的,而人不可以直接与硬件交互,为了完成交互,进行了以下的操作 将硬件设备交由内核管理,给硬件套个内…...

Thiol-PEG-Acid,HS-PEG-COOH,巯基-聚乙二醇-羧基试剂供应
一:产品描述 1、名称 英文:HS-PEG-COOH,Thiol-PEG-Acid 中文:巯基-聚乙二醇-羧基 2、CAS编号:N/A 3、所属分类:Carboxylic acid PEG Thiol PEG 4、分子量:可定制,Thiol-聚乙二…...

数据结构与算法基础-学习-09-线性表之栈的理解、初始化顺序栈、判断顺序栈空、获取顺序栈长度的实现
一、个人理解栈是线性表的一种衍生,和之前的顺序表和链表在插入和删除元素上有较大差异,其他基本相同,栈是数据只能插入表的尾部(栈顶),删除数据时只能删除表的尾部(栈顶)数据&#…...

深入Kafka核心设计与实践原理读书笔记第二章
1 生产者 生产逻辑 配置生产者客户端参数及创建相应的生产者实例。构建待发送的消息。发送消息关闭实列 参数说明 bootstrap.servers :用来指定生产者客户端链接Kafka集群搜需要的broker地址清单,具体格式 host1:port1,host2:port2,可以设置一个或多…...

知乎kol投放怎么做?知乎kol资源从哪里找?
每个领域都有一些比较专业且具有话语权的大V博主,他们推荐某个产品或是品牌就能对粉丝产生很深的影响力,影响用户消费决策。 互联网时代,每个热门的内容平台上都活跃着一大批kol博主,做kol投放具有很高的商业价值。 知乎内容社区…...

python设计模式-享元设计模式,抽象工厂设计模式,面向对象设计模式
享元设计模式 享元(flyweight)设计模式属于结构设计模式类别。 它提供了一种减少对象数的方法。 它包含各种有助于改进应用程序结构的功能。享元对象最重要的特性是不可变的。 这意味着一旦构建就不能修改它们。 该模式使用HashMap来存储引用对象 如何实现享元(flyweight)设计…...

10条终身受益的Salesforce职业发展建议!
Salesforce这个千亿美金巨兽,在全球范围内有42,000多名员工。作为一家发展迅速的科技公司,一直在招聘各种角色,包括销售、营销、工程师和管理人员等。 据IDC估计,从2016年到2020年,该生态系统创造了190万个工作岗位。…...

电子科技大学人工智能期末复习笔记(四):概率与贝叶斯网络
目录 前言 概率 概率公式 贝叶斯公式 链式条件概率 例题 1. 求联合概率分布/边缘概率分布/条件概率分布 2. 灵活运用贝叶斯公式 概率总结 贝叶斯网络 判断独立性 两个事件独立的判断 条件独立性的判断 假设条件独立的链式法则 ⚠Active / Inactive Paths 判断独…...

码上掘金实现电子木鱼
前言 前几天在朋友圈看到“敲电子木鱼”的视频,敲一下木鱼就提示“功德 1”,还带有敲击声和念经的声音,感觉挺有意思的。 心血来潮,捣鼓了一晚上,借助码上掘金实现了这个功能。 展示效果 素材 准备素材如下&#…...
深度学习_L2正则化
文章目录参考博客正则化介绍正则化的实现参考博客 深入理解L1、L2正则化 PyTorch 实现L2正则化以及Dropout的操作 正则化介绍 正则化(Regularization)是机器学习中一种常用的技术,其主要目的是控制模型复杂度,减小过拟合。最基…...

第一章 认识Python
本章目录 一、初识Python 二、Python环境安装 三、Python代码的执行 四、Python集成开发环境 五、Python2.x与Python3.x的区别 六、本章小结 Python代码的编辑和运行方式主要分为两种:交互模式和脚本模式。 在交互模式下, 用户输入Python代码并按…...

复习0206
目录 一、访问修饰符 一、权限范围 二、注意事项 二、封装(面向对象的三大特征之一) 一、封装的好处 二、封装的实现步骤 三、和构造器结合 四、练习题中的细节 一、访问修饰符 一、权限范围 访问修饰符用于控制方法和属性(成员变量…...

小红书如何查看笔记
小红书如何查看笔记 在小红书上找关键词的 6 大方法进阶版想要查找品类词、行业词、产品词、长尾词的小伙伴看过来,这一次我们就来给大家升级了 6 种找关键词的方法,也是我们的进阶版。 第一种,下拉框查找。我们只需要在小红书 AP 输入主要的…...

linux001之linux系统部署安装
注意:本次安装讲解以乌班图(Ubuntu) 虚拟机来说明讲解,既然学习linux,就无需用图形界面了,直接用服务器版本 1. 下载乌班图 网址:https://www.ubuntu.org.cn/download/server 然后就可以看到右下角有下载提示ÿ…...

服务异步通信 RabbitMQ-高级篇
服务异步通信RabbitMQ-高级篇服务异步通信RabbitMQ-高级篇1.消息可靠性1.1.生产者消息确认1.1.1.修改配置1.1.2.定义Return回调1.1.3.定义ConfirmCallback1.2.消息持久化1.2.1.交换机持久化1.2.2.队列持久化1.2.3.消息持久化1.3.消费者消息确认1.3.1.演示none模式1.3.2.演示aut…...

【PR】零基础快速入门教程
【PR】零基础快速入门教程PR(Premiere)能做什么?PR欢迎界面及新建项目工作区及窗口说明导入文件建立序列视频剪辑添加字幕导出视频使用软件:Premiere2020新年卷起来,写文章已近不能满足与我了,我要向着更前…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...