【C++】特殊类的设计、单例模式以及Cpp类型转换

📚 博主的专栏

🐧 Linux   |   🖥️ C++   |   📊 数据结构  | 💡C++ 算法 | 🌐 C 语言

上篇文章: C++ 智能指针使用,以及shared_ptr编写

下篇文章: C++ IO流

目录

特殊类的设计

请设计一个类,不能被拷贝

请设计一个类,只能在堆上创建对象

请设计一个类,只能在栈上创建对象

请设计一个类,不能被继承

请设计一个类,只能创建一个对象(单例模式)

设计模式:

单例模式:

饿汉模式

懒汉模式

C++的类型转换

C语言中的类型转换

CPP中类型转换

为什么C++需要四种类型转换

隐式类型转换

示例:内置类型 -> 自定义类型

示例:自定义类型 -> 内置类型

自定义类型和自定义类型之间的转换

隐式类型转换的坑:

C++强制类型转换

static_cast(对应之前的隐式类型转换)

reinterpret_cast(对应的之前的显式类型转换)

const_cast

dynamic_cast(专门争对C++设计的,向下转换)

RTTI(了解)运行时类型识别


特殊类的设计

请设计一个类,不能被拷贝

拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。

线程不能被拷贝,IO流(IO流中的缓冲区不拷贝)不能被拷贝

C++98:

将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。

C++11:

请设计一个类,只能在堆上创建对象

实现方式:

1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。

2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建

class HeapOnly
{
public:static HeapOnly* CreateObject(){return new HeapOnly;//智能在堆上new}
private:HeapOnly() {}// C++98// 1.只声明,不实现。因为实现可能会很麻烦,而你本身不需要// 2.声明成私有HeapOnly(const HeapOnly&);// or
// C++11HeapOnly(const HeapOnly&) = delete;HeapOnly& operator=(const HeapOnly&) = delete;};

调用成员函数需要对象,因此将函数指定为static让其只属于类,不属于对象 

	//调用成员函数需要对象,因此将函数指定为static让其只属于类,不属于对象HeapOnly* obj4 = HeapOnly::CreateObject();

第一种办法:

为了防止调用拷贝构造,因此将拷贝构造也设置为私有,或者使用C++11的方法,直接封掉拷贝构造

	HeapOnly obj5(*obj4);

一般封了拷贝构造,将赋值也封上,避免发生浅拷贝。

第二种方法:

将析构私有化(让析构无法自动调用):再自己写函数显示调用自己写的释放指针函数:

class HeapOnly
{
public:void Release(){delete this;}
private:~HeapOnly(){ }
};

请设计一个类,只能在栈上创建对象

同上将构造函数私有化,然后设计静态方法创建对象返回即可

class StackOnly
{
public:static StackOnly CreateObj(){return StackOnly();}// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉// StackOnly obj = StackOnly::CreateObj();// StackOnly* ptr3 = new StackOnly(obj);void* operator new(size_t size) = delete;void operator delete(void* p) = delete;
private:StackOnly():_a(0){}
private:int _a;
};

请设计一个类,不能被继承

C++98方式

// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:static NonInherit GetInstance(){return NonInherit();}
private:NonInherit(){}
};

C++11方法

final关键字,final修饰类,表示该类不能被继承。

class A final
{// ....
};

请设计一个类,只能创建一个对象(单例模式)

设计模式:

设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。

使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

设计模式举例:

单例模式

迭代器模式

适配器模式

观察者

工厂 

单例模式:

一个类只能创建一个对象,即单例(单实例)模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。

单例模式有两种实现模式:

饿汉模式

就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。

优点:简单

缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。

饿汉(很饥饿,极早就创建对象)模式 : 一开始就(main函数之前)就创建对象
问题1:很多单例类,都是饿汉模式,有些单例对象初始化资源很多,导致程序启动慢,迟迟进不了main函数
问题2:如果两个单例类有初始化依赖关系,饿汉也无法解决。比如A类和B类是单例,A单例要连接数据库,B单例要用A单例访问数据库。

示例代码:

 //饿汉(很饥饿,极早就创建对象)模式 : 一开始就(main函数之前)就创建对象//问题1:很多单例类,都是饿汉模式,有些单例对象初始化资源很多,导致程序启动慢,迟迟进不了main函数//如果两个单例类有初始化依赖关系,饿汉也无法解决。比如A类和B类是单例,A单例要连接数据库,B单例要用A单例访问数据库
class ConfigInfo
{
public:static ConfigInfo* GetInstance(){return &_sInfo;//可以用指针也能用引用(看需求)}string GetIp(){return _ip;}void SetIp(const string& ip){_ip = ip;}private://1.私有化构造函数ConfigInfo(){cout << "ConfigInfo()" << endl;}//2.把拷贝构造和赋值封掉ConfigInfo(const ConfigInfo&) = delete;ConfigInfo& operator=(const ConfigInfo&) = delete;
private:string _ip = "127.0.0.1";//存网络ipint _port = 80;//端口//...// 声明 static ConfigInfo _sInfo;//受类域的限制,但实际上在静态区,这里只是一个声明,需要在类外面定义
};// 定义
ConfigInfo ConfigInfo::_sInfo;
int main()
{//ConfigInfo info;cout << ConfigInfo::GetInstance() << endl;ConfigInfo::GetInstance()->SetIp("192.22.1.13");cout << ConfigInfo::GetInstance()->GetIp() << endl;return 0;
}

如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好
 

懒汉模式

优点:第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控制。

如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。

懒汉就是第一次调用时,创建单例对象

//懒汉完美解决上面饿汉存在的问题
class ConfigInfo
{
public:static ConfigInfo* GetInstance(){static ConfigInfo info;//定义一个局部的静态对象return &info;}string GetIp(){return _ip;}void SetIp(const string& ip){_ip = ip;}private://1.私有化构造函数ConfigInfo(){cout << "ConfigInfo()" << endl;}//2.把拷贝构造和赋值封掉ConfigInfo(const ConfigInfo&) = delete;ConfigInfo& operator=(const ConfigInfo&) = delete;
private:string _ip = "127.0.0.1";//存网络ipint _port = 80;//端口//...
};int main()
{//ConfigInfo info;cout << ConfigInfo::GetInstance() << endl;cout << ConfigInfo::GetInstance() << endl;cout << ConfigInfo::GetInstance() << endl;ConfigInfo::GetInstance()->SetIp("192.22.1.13");cout << ConfigInfo::GetInstance()->GetIp() << endl;return 0;
}

缺点是:在C++11前,局部static对象构造,有线程安全的风险

C++11之前的解决办法:仍然在第一次调用的时候再创建

class ConfigInfo
{
public:static ConfigInfo* GetInstance(){if (_spInfo == nullptr){_spInfo = new ConfigInfo;}return _spInfo;}string GetIp(){return _ip;}void SetIp(const string& ip){_ip = ip;}private://1.私有化构造函数ConfigInfo(){cout << "ConfigInfo()" << endl;}//2.把拷贝构造和赋值封掉ConfigInfo(const ConfigInfo&) = delete;ConfigInfo& operator=(const ConfigInfo&) = delete;
private:string _ip = "127.0.0.1";//存网络ipint _port = 80;//端口//...// 定义一个静态指针static ConfigInfo* _spInfo;//受类域的限制,但实际上在静态区,这里只是一个声明,需要在类外面定义
};ConfigInfo* ConfigInfo::_spInfo = nullptr;
int main()
{//ConfigInfo info;cout << ConfigInfo::GetInstance() << endl;cout << ConfigInfo::GetInstance() << endl;cout << ConfigInfo::GetInstance() << endl;ConfigInfo::GetInstance()->SetIp("192.22.1.13");cout << ConfigInfo::GetInstance()->GetIp() << endl;return 0;
}

现在还不能避免多线程调用的线程安全的问题:

如果两个线程都来,t1先来正在new的时候,t2判断了_spInfo为空,也要开始new,t1已经new完了,赋值给了_spInfo,最怕的是,t2时间片到了,开始在判断后的地方休眠,t1继续执行,并且已经SetIp(),t2醒了,又开始new,将t1申请的全给覆盖,导致内存泄漏。因此需要加锁 。

注意静态成员函数,只能访问静态的成员变量,因此mutex的对象也要设置成静态的:

注意看注释

class ConfigInfo
{
public:static ConfigInfo* GetInstance(){//加锁t1、t2只能一个进判断,保证只会new一个对象//单单只这样写,会导致,已经创建好对象了,但是每次调用GetInstance的时候都需要再被加锁/*	unique_lock<mutex> lock(_mtx);if (_spInfo == nullptr){_spInfo = new ConfigInfo;}*///因为不能直接将锁加载if判断内,这仍然会导致线程安全,要的就是,每个线程都得判断对象是否创建//解决办法:多写一层if (_spInfo == nullptr){unique_lock<mutex> lock(_mtx);if (_spInfo == nullptr){_spInfo = new ConfigInfo;}}return _spInfo;}string GetIp(){return _ip;}void SetIp(const string& ip){_ip = ip;}private://1.私有化构造函数ConfigInfo(){cout << "ConfigInfo()" << endl;}//2.把拷贝构造和赋值封掉ConfigInfo(const ConfigInfo&) = delete;ConfigInfo& operator=(const ConfigInfo&) = delete;
private:string _ip = "127.0.0.1";//存网络ipint _port = 80;//端口//...// 定义一个静态指针static ConfigInfo* _spInfo;//受类域的限制,但实际上在静态区,这里只是一个声明,需要在类外面定义static mutex _mtx;
};ConfigInfo* ConfigInfo::_spInfo = nullptr;
mutex ConfigInfo::_mtx;
int main()
{//ConfigInfo info;cout << ConfigInfo::GetInstance() << endl;cout << ConfigInfo::GetInstance() << endl;cout << ConfigInfo::GetInstance() << endl;ConfigInfo::GetInstance()->SetIp("192.22.1.13");cout << ConfigInfo::GetInstance()->GetIp() << endl;return 0;
}

C++的类型转换

C语言中的类型转换

在C语言中,如果赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化,C语言中总共有两种形式的类型转换:隐式类型转换和显式类型转换。

1. 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败

2. 显式类型转化:需要用户自己处理

隐式类型转换一般是类型关联度紧密(像是指针和double不能转换的就能转,整形、浮点型、char(ASCII码)之间就能互相隐式类型转。

显式(强制)类型转换一般就是任意指针之间,整形(数据大小)和指针(字节编号)之间

示例:

	// 隐式类型转换double d = i;printf("%d, %.2f\n", i, d);int* p = &i;// 显示的强制类型转换int address = (int)p;printf("%p, %d\n", p, address);

缺陷:

转换的可视性比较差,所有的转换形式都是以一种相同形式书写,难以跟踪错误的转换

CPP中类型转换

为什么C++需要四种类型转换

C风格的转换格式很简单,但是有不少缺点的:

1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失

2. 显式类型转换将所有情况混合在一起,代码不够清晰

因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。


隐式类型转换

示例:

仍然支持有一定关联的类型的类型转换

1.C++内置类型支持转换为自定义类型

2.C++自定义类型支持转换成内置类型

单参数的构造函数支持隐式类型的转换

示例:内置类型 -> 自定义类型

class A
{
public:A(int a1):_a1(a1){}A(int a1, int a2):_a1(a1), _a2(a2){}
private:int _a1 = 1;int _a2 = 1;
};
// 内置类型 -> 自定义类型
A aa1 = 1;
// C++11
A aa2 = {2,2};

示例:自定义类型 -> 内置类型

默认是不支持的,我们需要重载operator int

int x = aa1;
cout << x << endl;

想转什么类型就写什么类型: 这里想转成int,因此就写int

    operator int(){return _a1 + _a2;}

在OJ当中会遇到这样的写法:

while (cin>>x){cout << x << endl;
}

原理是:

	while (cin>>x)while (cin.operator>>(x))while (cin.operator>>(x).operator bool()){cout << x << endl;}

如果想要输入string呢?

	string str;while (cin>>str){cout << str << endl;}

自定义类型和自定义类型之间的转换

比如我现在有一个B类型,正常情况下:能否将B类型转为A类型,不可以,强制类型转换也不可以

class B
{
public:private:int _b1;
};

解决办法:用构造函数来支持相关的转换

class B
{
public:B(const A& aa):_b1(aa.get()){}private:int _b1;
};
	B bb = aa1;

就能支持了(实际上,我们模拟写const iterator和普通iterator时,就用的这个方法)

隐式类型转换的坑:

// 隐式类型转换的坑
void Insert(size_t pos)
{int end = 10;while (end >= pos){cout << end << endl;--end;}
}
//
int main()
{Insert(5);Insert(0);return 0;
}

运行结果:由于隐式类型转换导致的死循环,原因是因为,这里的int被隐式类型转换为unsize_int


C++强制类型转换

标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符: static_cast、reinterpret_cast、const_cast、dynamic_cast

static_cast(对应之前的隐式类型转换)

static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用static_cast,但它不能用于两个不相关的类型进行转换。

	int i = 1;// 隐式类型转换 : static_castdouble d = static_cast<double>(i);printf("%d, %.2f\n", i, d);int* p = &i;// error C2440: “static_cast”: 无法从“int *”转换为“int”// int address1 = static_cast<int>(p);

reinterpret_cast(对应的之前的显式类型转换)

reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型

示例:

	// 显示的强制类型转换 : reinterpret_castint address = reinterpret_cast<int>(p);printf("%p, %d\n", p, address);

之前不支持的还是不会支持 

	// 类型强制转换”: 无法从“int *”转换为“double”// double x = (double)p;// 类型强制转换”: 无法从“int *”转换为“double”//double x = reinterpret_cast<double> p;

const_cast

const_cast最常用的用途就是删除变量的const属性,方便赋值

示例:

	const int a = 1;//a不是存在常量区的,还是在栈上的(常变量// a++;int x = 0;cout << &a << endl;cout << &x << endl;

是可以修改的,间接修改:

	int* ptr = (int*)&a;(*ptr)++;cout << *ptr << endl;cout << a << endl;

为啥用*ptr间接去访问拿到的是2,直接访问a又拿到的是1

但是又通过监视窗口,又会发现:a已经被修改了,为什么却打印1。

这是因为编译器的优化,第一种是因为:a是const修饰了的,不会被改变,把a存在寄存器中,寄存器中的a值没有被改变,内存变了,但是取值是直接从寄存器中拿。

第二种是:a直接被替换成 1,类似于宏,在编译的时候被替换

这个时候就可以在定义a的时候加一个关键字:volatile,稳定的。告诉编译器别优化,每次都到内存当中取

	volatile const int a = 1;

运行结果:

因此增加一个const_cast,来显式告诉自己,a的const属性已经被去掉了,他已经可以被改变了,提醒要加上volatile

	int* ptr = const_cast<int*>(&a);

示例:这种情况用哪一种隐式类型转换方式

	A aa1 = static_cast<A>(1);

这是单参数的构造函数,还是应该走隐式类型的转换因此使用 static_cast ,reinterpret_cast是显式类型转换。


dynamic_cast(专门争对C++设计的,向下转换)

dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)

向上转型:子类对象指针/引用->父类指针/引用(是天然的,不会产生临时对象,切片父类,不需要转换,赋值兼容规则)

向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)

注意:

1. dynamic_cast只能用于父类含有虚函数的类

2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0

示例:

class A
{
public:virtual void f() {}int _a1 = 1;
};class B : public A
{
public:int _b1 = 1;
};void fun(A* pa)
{// 无差别转换,存在一定风险B* pb = (B*)pa;cout << pb << endl;//pb->_b1++;
}int main()
{A a;B b;fun(&a);fun(&b);return 0;
}

这样转换是有风险,我们分别给A、B成员_a1,_b1,pa指针有可能只想A对象也有可能指向B对象,如果原本就指向B对象,将你强转成B是没有问题的。转换的时候,现在就指向整个B。如果原来指向A,转成B之后,会多看一部分,但是那一部分并不属于pb,越界。

因此C++设计了dynamic_cast来动态转换:

不是无差别转换,他会检查pa原本指向什么对象,如果指向B对象,转换成功,如果指向A对象,转换失败,返回空。能识别是指向父类还是子类。

void fun(A* pa)
{// pa指向B对象,转换成功// pa指向A对象,转换失败,返回空B* pb = dynamic_cast<B*>(pa);if (pb){cout << pb << endl;pb->_b1++;}else{cout << "转换失败" << endl;}
}

RTTI(了解)运行时类型识别

RTTI:Run-time Type identification的简称,即:运行时类型识别。C++通过以下方式来支持RTTI:

1. typeid运算符

2. dynamic_cast运算符

3. decltype

复习RAII是什么:把一个资源交给一个对象管理,可能是动态开辟的资源,或者锁,当生命周期到了自动调用析构函数,解锁、释放资源

结语:

       随着这篇博客接近尾声,我衷心希望我所分享的内容能为你带来一些启发和帮助。学习和理解的过程往往充满挑战,但正是这些挑战让我们不断成长和进步。我在准备这篇文章时,也深刻体会到了学习与分享的乐趣。    

         在此,我要特别感谢每一位阅读到这里的你。是你的关注和支持,给予了我持续写作和分享的动力。我深知,无论我在某个领域有多少见解,都离不开大家的鼓励与指正。因此,如果你在阅读过程中有任何疑问、建议或是发现了文章中的不足之处,都欢迎你慷慨赐教     。

        你的每一条反馈都是我前进路上的宝贵财富。同时,我也非常期待能够得到你的点赞、收藏,关注这将是对我莫大的支持和鼓励。当然,我更期待的是能够持续为你带来有价值的内容。

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

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

相关文章

探索 Flowable 后端表达式:简化流程自动化

什么是后端表达式&#xff1f; 在 Flowable 中&#xff0c;后端表达式是一种强大的工具&#xff0c;用于在流程、案例或决策表执行期间动态获取或设置变量。它还能实现自定义逻辑&#xff0c;或将复杂逻辑委托…… 后端表达式在 Flowable 的后端运行&#xff0c;无法访问前端…

【Lua】Lua 入门知识点总结

Lua 入门学习笔记 本教程旨在帮助有编程基础的学习者快速入门Lua编程语言。包括Lua中变量的声明与使用&#xff0c;包括全局变量和局部变量的区别&#xff0c;以及nil类型的概念、数值型、字符串和函数的基本操作&#xff0c;包括16进制表示、科学计数法、字符串连接、函数声明…

符号速率估计——小波变换法

[TOC]符号速率估计——小波变换法 一、原理 1.Haar小波变换 小波变换在信号处理领域被成为数学显微镜&#xff0c;不同于傅里叶变换&#xff0c;小波变换可以观测信号随时间变换的频谱特征&#xff0c;因此&#xff0c;常用于时频分析。   当小波变换前后位置处于同一个码元…

android contentProvider 踩坑日记

写此笔记原因 学习《第一行代码》到第8章节实现provider时踩了一些坑&#xff0c;因此记录下来给后来人和自己一个提示&#xff0c;仅此而已。 包含内容 Sqlite数据库CURD内容provider界面provider项目中书籍管理provider实现逻辑用adb shell确认providercontentResolver接收…

Eureka、LoadBalance和Nacos

Eureka、LoadBalance和Nacos 一.Eureka引入1.注册中心2.CAP理论3.常见的注册中心 二.Eureka介绍1.搭建Eureka Server 注册中心2.搭建服务注册3.服务发现 三.负载均衡LoadBalance1.问题引入2.服务端负载均衡3.客户端负载均衡4.Spring Cloud LoadBalancer1).快速上手2)负载均衡策…

【开关电源】关于GaN反激电源开关噪声

文章目录 0 前言1 设计信息1.1 设计需求1.2 原理图1.3 电源表现 2 原因分析3 横向对比TI UCG28826 &#xff08;GaN&#xff09;采购的普通QR反激变换器 4 总结 0 前言 笔者原计划设计一款省电的&#xff0c;效率尚可的&#xff0c;稳定的2路输出反激电源&#xff0c;用于系统…

DOCA介绍

本文分为两个部分&#xff1a; DOCA及BlueField介绍如何运行DOCA应用&#xff0c;这里以DNS_Filter为例子做大致介绍。 DOCA及BlueField介绍&#xff1a; 现代企业数据中心是软件定义的、完全可编程的基础设施&#xff0c;旨在服务于跨云、核心和边缘环境的高度分布式应用工作…

mybatis mapper.xml中使用枚举

重点&#xff1a;application.propertis配置类 #TypeEnumHandler 这个类的包名&#xff0c;不是全路径 mybatis.type-handlers-packagecom.fan.test.handler两个枚举类&#xff1a; public enum StatusEnum {DELETED(0),ACTIVE(1);private final int code;StatusEnum(int cod…

鸿蒙生态:鸿蒙生态校园行心得

&#xff08;个人观点&#xff0c;仅供参考&#xff09; 兄弟们&#xff0c;今天来浅浅聊一聊这次的设立在长沙的鸿蒙生态行活动。 老样子&#xff0c;我们先来了解一下这个活动&#xff1a; &#xff28;&#xff41;&#xff52;&#xff4d;&#xff4f;&#xff4e;&#x…

【速写】多LoRA并行衍生的一些思考

迁移学习上的一个老问题&#xff0c;怎么做多领域的迁移&#xff1f;以前的逻辑认为领域迁移属于是对参数做方向性的调整&#xff0c;如果两个领域方向相左&#xff0c;实际上不管怎么加权相加都是不合理的。 目前一些做法想着去观察LoRA权重矩阵中的稠密块与稀疏块&#xff0…

【Delphi 基础知识 44】接口interface的应用

目录 1. 前言2. 接口有哪些优势2.1. 实现多态性2.2 实现多重(解决单继承限制)2.3 解耦代码(依赖注入)2.4 便于测试(模拟接口)2.5 跨语言互操作性(COM支持)1. 前言 总结为一句话就是:接口只告诉你要做什么,而类会告诉你应该怎么做 下面是最简单的接口实现 typeIMyIn…

09.传输层协议 ——— TCP协议

文章目录 TCP协议 谈谈可靠性TCP协议格式 序号与确认序号窗口大小六个标志位 确认应答机制&#xff08;ACK&#xff09;超时重传机制连接管理机制 三次握手四次挥手 流量控制滑动窗口拥塞控制延迟应答捎带应答面向字节流粘包问题TCP异常情况TCP小结基于TCP的应用层协议 TCP协…

NLP高频面试题(五十一)——LSTM详解

长短期记忆网络(LSTM)相较于传统循环神经网络(RNN)的核心改进在于通过引入记忆单元(cell state)和门机制(gating mechanism)来有效缓解梯度消失与梯度爆炸问题,从而更好地捕捉长距离依赖关系 。在其网络结构中,信息通过输入门(input gate)、遗忘门(forget gate)和…

SpringCloud组件—Eureka

一.背景 1.问题提出 我们在一个父项目下写了两个子项目&#xff0c;需要两个子项目之间相互调用。我们可以发送HTTP请求来获取我们想要的资源&#xff0c;具体实现的方法有很多&#xff0c;可以用HttpURLConnection、HttpClient、Okhttp、 RestTemplate等。 举个例子&#x…

无需花钱购买域名服务器!使用 VuePress + Github 30分钟搭建属于自己的博客网站(保姆级教程)

前言 GitHub Pages 提供免费全球加速的服务器资源&#xff0c;VuePress 将 Markdown 变成艺术品级的网页&#xff0c;仅需 30 分钟&#xff0c;你便可以像提交代码一样发布文章&#xff0c;过程完全免费。 博客搭建好的效果如下&#xff1a;https://honorsong.github.io/exam…

提交到Gitee仓库

文章目录 注册配置公钥创建空白的码云仓库把本地项目上传到码云对应的空白仓库中 注册 注册并激活码云账号&#xff08; 注册页面地址&#xff1a;https://gitee.com/signup &#xff09; 可以在自己C盘/用户/用户名/.ssh 可以看到 有id_rsa.pub 以前在GitHub注册时搞过&…

如何在 Java 中从 PDF 文件中删除页面(教程)

由于 PDF 文件格式不是 Java 原生支持的&#xff0c;因此要从 PDF 中删除页面&#xff0c;你需要使用外部库。 本教程介绍如何使用 JPedal 来实现这一功能。 开始使用 • 将 JPedal 添加到你的类路径或模块路径中&#xff08;可从官网下载安装试用版 JAR 文件&#xff09; •…

机器学习第二篇 多变量线性回归

数据集&#xff1a;世界幸福指数数据集中的变量有幸福指数排名、国家/地区、幸福指数得分、人均国内生产总值、健康预期寿命、自由权、社会支持、慷慨程度、清廉指数。我们选择GDP per Capita和Freedom&#xff0c;来预测幸福指数得分。 文件一&#xff1a;linear&#xff0c;…

位运算,状态压缩dp(算法竞赛进阶指南学习笔记)

目录 移位运算一些位运算的操作最短 Hamilton 路径&#xff08;状态压缩dp模板&#xff0c;位运算&#xff09; 0x是十六进制常数的开头&#xff1b;本身是声明进制&#xff0c;后面是对应具体的数&#xff1b; 数组初始化最大值时用0x3f赋值&#xff1b; 移位运算 左移 把二…

Java高频面试之并发编程-05

hello啊&#xff0c;各位观众姥爷们&#xff01;&#xff01;&#xff01;本baby今天来报道了&#xff01;哈哈哈哈哈嗝&#x1f436; 面试官&#xff1a;线程有哪些调度方法&#xff1f; 在Java中&#xff0c;线程的调用方法主要包括以下几种方式&#xff0c;每种方式适用于…