从C语言到C++_37(特殊类设计和C++类型转换)单例模式
目录
1. 特殊类设计
1.1 不能被拷贝的类
1.2 只能在堆上创建的类
1.3 只能在栈上创建的类
1.4 不能被继承的类
1.5 只能创建一个对象的类(单例模式)(重点)
1.5.1 饿汉模式
1.5.2 懒汉模式
2. 类型转换
2.1 static_cast
2.2 reinterpret_cast
2.3 const_cast
2.4 dynamic_cast
3. RTTI(了解)和类型转换常见面试题
本篇完。
1. 特殊类设计
普通类的设计基础上,提出一些限制条件设计的类就是特殊类。
1.1 不能被拷贝的类
拷贝只会发生在两个场景中:拷贝构造函数以及赋值运算符重载。
因此禁止拷贝的类只需要让该类禁止调用拷贝构造函数和赋值运算符重载函数即可。
C++98中的方式:将拷贝构造函数和赋值运算符重载函数只声明不定义,并设置成私有:
class CopyBan
{
public:CopyBan(){}private:CopyBan(const CopyBan& cb); // 拷贝构造函数声明CopyBan& operator=(const CopyBan& cb); // 赋值运算符重载声明
};
原因:
① 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
② 只声明不定义:在调用拷贝构造和赋值运算符重载函数的时候,由于没有定义就会产生链接错误,在编译阶段就报错。不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。

如上图代码,在对这个特殊类进行拷贝和赋值的时候,因为这两个成员函数私有而无法调用。
C++11的方式:C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上 = delete,表示让编译器删除掉该默认成员函数。
class CopyBan
{
public:CopyBan(){}private:CopyBan(const CopyBan&) = delete;CopyBan& operator=(const CopyBan&) = delete;
};

使用C++11中的给delete新赋予的意义来禁止生产拷贝构造和赋值运算符重载函数。
此时编译器也不会自动生成默认的拷贝构造函数和赋值运算符重载函数。
1.2 只能在堆上创建的类
正常创建类对象时,会在栈上创建,并且自动调用构造函数来初始化。
只能在创建在堆上时,就需要让该对象只能通过new来创建,并且调用构造函数来初始化。
class HeapOnly
{
public:static HeapOnly* CreateObject(){return new HeapOnly;}HeapOnly(const HeapOnly& hp) = delete;//禁止拷贝HeapOnly& operator = (const HeapOnly& hp) = delete; // 禁止拷贝private:HeapOnly() //构造函数{}
};
定义一个静态成员函数,在该函数内部new一个HeapOnly对象。将构造函数私有,并且禁止生成拷贝构造函数。
使用静态成员函数new一个HeapOnly对象的原因:
非静态成员函数在调用的时候,必须使用点(.)操作符来调用,这一步是为了传this指针。这样的前提是先有一个HeapOnly对象,但是构造函数设置成了私有,就无法创建这样一个对象。而静态成员函数的调用不用传this指针,也就不用必须有HeapOnly对象,只需要类域::静态成员函数即可。否则就面临了先有鸡还是先有蛋的问题:非静态成员函数调用需要传对象,此时对象又只能调用非静态成员函数创建。静态成员函数属于HeapOnly域内,所以在new一个对象的时候,可以调用私有的构造函数。
禁止调用拷贝构造函数,并且私有化的原因:
这样的目的是为了禁止拷贝,防止使用堆区上的HeapOnly对象在栈区上拷贝,如下面代码:
而禁止了拷贝构造就杜绝了这一行为,从而保证了HeapOnly对象只能在堆上创建。
1.3 只能在栈上创建的类
只能主要做到不能在堆上创建类对象。
new一个对象的时候,会调用该类的operator new(size_t size)函数,在释放资源的时候又会调用该类的operator delete(void* p)函数。
StackOnly1 st1;static StackOnly1 st2;StackOnly1* st3 = new StackOnly1;
此时就是想办法禁止下面两行代码的使用了
方法1:通过一个静态成员函数在栈区上创建一个类对象,并且将默认构造函数私有化。
class StackOnly1
{
public:static StackOnly1 CreateObject(){return StackOnly1();}private:StackOnly1(){}
};


此时new一个对象的时候,由于默认构造函数私有无法调用,所以报错。
但是此时我可以调用拷贝构造来在栈上创建:

如果加上防拷贝,第一种创建都创建不了了:

所以并没有很好的解决方式。
方法2:防止在堆上创建类对象就是要禁止调用这两个函数。
class StackOnly2
{
public:StackOnly2() //构造函数{}void* operator new(size_t size) = delete; // 禁止调用newvoid operator delete(void* p) = delete; // 禁止调用delete
};
使用delete来禁止这两个函数的调用,那么在new一个对象的时候,就会产生编译错误,从而无法在堆区上创建类对象。此时在堆上创建对象时就会报错,尝试引用已经删除的函数。
这俩种设计方法共同的一个漏洞,类对象可以在静态区(数据段)上创建:

所以设计只能在栈上创建的类并没有什么很好的方案。
设计特殊类的核心点:只能通过静态成员函数来创建类,封掉其他所有创建方式。
1.4 不能被继承的类
C++98的方式:基类的构造函数私有,派生类在创建对象的时候,无法调用基类的构造函数。
class NonInherit // 基类
{
private:NonInherit() //基类构造函数私有{}
};class B : public NonInherit // 派生类
{};

C++11的方式:使用C++11中的 final 关键字修饰基类,这个基类就无法继承。

此时不实例化也报错了。
1.5 只能创建一个对象的类(单例模式)(重点)
单例模式是一种设计模式。
设计模式: 设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打 仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后 来孙子就总结出了《孙子兵法》。孙子兵法也是类似。
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模 式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
单例模式:一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
单例模式有两种实现模式:饿汉模式和懒汉模式
1.5.1 饿汉模式
① 饿汉模式:就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。
class Singleton
{
public:static Singleton* GetInstance() // 获取单例对象接口{return &m_instance;}private:Singleton() // 构造函数私有化{}Singleton(const Singleton& s) = delete; // 禁止使用拷贝构造Singleton& operator=(const Singleton& s) = delete; // 禁止使用赋值运算符重载//保证单例对象在静态区且只有一个static Singleton m_instance;//单例对象
};Singleton Singleton::m_instance;//在程序入口之前就完成单例对象初始化int main()
{Singleton* s = Singleton::GetInstance();//获取单例对象return 0;
}
静态成员变量只能在类域外进行定义初始化。所以在main函数之前就将单例对象定义初始化,此时该单例对象创建在静态区上,而且仅有一个,后面就无法再创建。
想要获取该单例对象只能通过静态成员函数GetInstance()来获取。
静态成员函数可以直接访问静态成员变量m_instance。
为什么称之为饿汉模式呢?不管将来会不会使用到这个单例对象,但是在程序一启动还没有进入main函数之前就创建一个唯一的实例对象。就像一个饿汉一样,一上来就先吃(创建单例对象)。
缺点:
- 可能会导致进程启动较慢,如过实例对象很复杂,在创建实例对象时就会花费很多时间。
- 实例顺序不确定,如果有多个单例对象,并且对象之间存在互相依赖关系,由于对象的实例对象不确定(和代码顺序无关,由编译器决定),此时就会发生错误。
优点:简单、没有线程安全问题。
如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避 免资源竞争,提高响应速度更好。
1.5.2 懒汉模式
懒汉模式:
class Singleton
{
public:static Singleton* GetInstance(){// 如果单例对象没有创建则在堆区创建if (m_pInstance == nullptr){m_pInstance = new Singleton;}return m_pInstance;}private:Singleton() // 构造函数{}Singleton(const Singleton& s) = delete; // 禁止拷贝Singleton& operator=(const Singleton& s) = delete; // 禁止赋值// 静态单例对象指针static Singleton* m_pInstance; // 单例对象指针
};Singleton* Singleton::m_pInstance = nullptr; // 初始化为空int main()
{Singleton* ps = Singleton::GetInstance();//获取单例对象return 0;
}
同样将构造函数私有,拷贝构造和赋值运算符重载函数禁止调用,用来保证单例模式的唯一性。
增加静态单例对象指针成员变量。
在类外实例化静态指针变量的时候,并没有创建单例对象,而是将其初始化为空。
在获取单例对象的时候,如果是第一次使用,那么就会在堆区上new一个单例对象,并且将所在地址赋值给静态的成员指针变量。
为什么称之为懒汉模式呢?
懒汉模式又叫做延时加载模式,如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。
优点:
① 第一次使用单例对象时才创建对象,进程启动过程中无负载。
② 多个互相依赖的单例可以控制启动顺序(通过代码顺序)。
缺点:
① 相对复杂。(线程安全问题没讲,后面学了一半Linux再讲)
② 线程安全问题要处理好
单例对象释放问题:
① 一般情况下,单例对象不需要释放的。因为一般整个程序运行期间都可能会用它。
单例对象在进程正常结束后,也会资源释放。
② 有些特殊场景需要释放,比如单例对象析构时,要进行一些持久化(往文件、数据库写)操作。
2. 类型转换
在C语言中,如果赋值运算符(=)两边的类型不同,或者形参和实参类型不匹配,或者返回值类型和接收值类型不一致,就需要发生类型转换。
C语言中有两种类型转换:
- 隐式类型转换:编译器在编译阶段自动进行,能转就转,不能转就编译失败。
- 显式类型转换:需要用户自己处理。

double d = i是发生了隐式类型转换,将整形转换成了double类型。
隐式类型转换只发生在相近类型,比如整形家族直接,或者这些int,double等表示数值直接的变量类型。
int address = (int)p是发生了显式类型转换,将int * 类型的变量转换为int类型。
显式类型需要用户自己维护,在两种类型没有任何关系的时候需要进行显式类型转换,比如将指针类型转换成普通类型等等。
C语言的类型转换存在缺陷:
隐式类型转换有些情况下会出现问题,比如数据精度发生丢失(整形提升等)。
显式类型转换将所有情况混合在一起,代码不够清晰。
所以C++提出了自己的类型转换风格,但是仍然可以使用C语言的转换风格,因为要兼容C语言。
标准C++为了加强类型转换的可视性,引入了四种命名的强制类型转换操作符:
static_cast、reinterpret_cast、const_cast、dynamic_cast。下面我们一一了解。
2.1 static_cast
C语言的隐式类型转换(两个相近的类型转换)在C++中就可以使用static_cast来转换,但是不能用于两个不相关的类型进行转换。
int main()
{double a = 7.14;int b = static_cast<int>(a);cout << a << " " << b << endl;return 0;
}

double类型转int类型,在C语言中是隐式类型转换,在C++中为了更加明确使用了static_cast。
static_cast后的<>里放要转换的类型,()里放被转换的类型。
如果将static_cast看成是类模板,<int>就是在实例化,(d)就是在拷贝构造,siatic_cast<int>(d)就是在用d构建一个匿名对象。
2.2 reinterpret_cast
reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型。C语言的显式类型转换在C++中就可以reinterpret_cast。
int main()
{int a = 7;int* pa = &a;int address = reinterpret_cast<int>(pa);cout << a << " " << pa << " " << address << endl;return 0;
}

int*类型转换为int类型,在C语言中是显式类型转换,在C++中为了不混乱使用了reinterpret_cast。
这里如果使用static_cast进行类型转换的话会报错,必须使用reinterpret_cast:
2.1反过来用也会报错:

2.3 const_cast
const_cast最常用的用途就是删除变量的const属性,方便赋值:
int main()
{const int a = 2;int* p = const_cast<int*>(&a);*p = 3;cout << a << " " << *p << endl;return 0;
}

变量a原本是const属性的,不能被修改,使用了const_cast以后去除了常量属性,可以修改了,如*p = 3。

F11:

在调试窗口中可以看到,成功修改了原本是const属性的变量a。
为什么a在调试窗口看到的是3,打印出来的是2?这就涉及到了以前提到的汇编里的寄存器的内容

此时代码已经被优化了,不同编译器的优化不一样,有些编译器(VS)会把a = 2放到寄存器(认为const类型的变量不会被修改,每次去内存取的话很慢),那么读的时候去寄存器取就好了,打印的时候不会去内存取,所以打印出来的是3,监视窗口去内存取的,是2。
如果加上一个volatile关键字就告诉编译器不要优化:
int main()
{//const int a = 2;//int* p = const_cast<int*>(&a);//*p = 3;//cout << a << " " << *p << endl; // 2 3volatile const int a = 2;int* p = const_cast<int*>(&a);*p = 3;cout << a << " " << *p << endl; // 3 3return 0;
}

可以用C语言的强制类型转化:


这里用reinterpret_cast就不行:

const_cast更多的是一种警示,表示去除了const属性,要谨慎操作。
已经讲了三种C++的类型转化,应该也知道了是不能混着用的,
static_cast对标的是C语言的隐式类型转化,reinterpret_cast和const_cast对标的都是C语言的强制类型转化,增加就是为了建议你规范的用。
下面的dynamic_cast是C++特有的。
2.4 dynamic_cast
dynamic_cast是C++特有的,因为dynamic_cast设计到继承和多态的内容。
dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
向上转换:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
向下转换:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
首先要知道:父类对象无论如何都是不允许转换成子类对象的
class A // 父类
{
public:virtual void f() // 父类必须含有虚函数{}int _a = 1;
};class B : public A // 子类
{
public:int _b = 2;
};int main()
{A aa;// 父类对象无论如何都是不允许转换成子类对象的B bb = dynamic_cast<B>(aa);B bb = (B)aa;B bb;return 0;
}

但是父类的指针/引用是允许转换成子类指针/引用的,但C语言这样的场景就会报错:
void Func(A* pa)
{B* bptr = (B*)pa;cout << bptr << endl;bptr->_a = 6;bptr->_b = 7;
}int main()
{A aa;B bb;Func(&aa);return 0;
}

class A是父类,class B是子类,父类中有成员变量int _a,子类中有成员变量_b。
在main函数中,传父类指针&aa给函数,在函数中将A* pa父类指针接收该值,然后将其强转为子类指针B*,使用子类指针访问子类成员,bptr->_b = 7发生运行时错误。
形参A* pa是父类指针,接收的也是父类指针,所以强转成子类指针后访问子类成员_b会发生越界。
如果传的是子类指针就不会报错,因为即使形参是父类指针,强转成子类以后并不会越界。
C++使用dynamic_cast将父类指针强转为子类指针:
void Func(A* pa)
{B* bptr = dynamic_cast<B*>(pa);cout << bptr << endl;bptr->_a = 6;bptr->_b = 7;
}int main()
{A aa;B bb;Func(&aa);return 0;
}

传父类指针,然后强转为子类指针后,打印出来的结构是nullptr,表示该次转换不能进行。
传子类指针:
void Func(A* pa)
{B* bptr = dynamic_cast<B*>(pa);cout << bptr << endl;bptr->_a = 6;bptr->_b = 7;
}int main()
{A aa;B bb;// Func(&aa);Func(&bb);return 0;
}

传子类指针,形参的父类指针接收后再强转为子类,打印出来的结构是强转后的地址,表示该次强转可以成功。
注意:
- dynamic_cast只能用于父类含有虚函数的类。
- dynamic_cast会先检查是否能转换成功,能成功则转换并返回正确的地址,不能则返回nullptr。
- dynamic_cast是安全的,直接使用C语言的转换方式是不安全的(因为有越界风险)。
再看一段代码:
class A1
{
public:virtual void f(){}
public:int _a1 = 0;
};class A2
{
public:virtual void f(){}
public:int _a2 = 0;
};class B : public A1, public A2
{
public:int _b = 1;
};int main()
{B bb;A1* ptr1 = &bb;A2* ptr2 = &bb;cout << ptr1 << endl;cout << ptr2 << endl << endl;B* pb1 = (B*)ptr1;B* pb2 = (B*)ptr2;cout << pb1 << endl;cout << pb2 << endl << endl;B* pb3 = dynamic_cast<B*>(ptr1);B* pb4 = dynamic_cast<B*>(ptr2);cout << pb3 << endl;cout << pb4 << endl << endl;return 0;
}

结果可能和想的不一样,此时C++中dynamic_cast和C语言的强转就差了父类必须有虚函数:
class A1
{
public://virtual void f(){}
public:int _a1 = 0;
};class A2
{
public://virtual void f(){}
public:int _a2 = 0;
};class B : public A1, public A2
{
public:int _b = 1;
};int main()
{B bb;A1* ptr1 = &bb;A2* ptr2 = &bb;cout << ptr1 << endl;cout << ptr2 << endl << endl;B* pb1 = (B*)ptr1;B* pb2 = (B*)ptr2;cout << pb1 << endl;cout << pb2 << endl << endl;B* pb3 = dynamic_cast<B*>(ptr1);B* pb4 = dynamic_cast<B*>(ptr2);cout << pb3 << endl;cout << pb4 << endl << endl;return 0;
}

屏蔽掉下面代码:

C++中的类型转换,尤其是前两种static_cast和reinterpret_cast是建议用法,可以采用也可以不采用。const_cast是一种新用法,但是存在风险,dynamic_cast是一种安全的类型转换。
3. RTTI(了解)和类型转换常见面试题
RTTI:Run - time Type identification的简称,即:运行时类型识别。
C++通过以下方式来支持RTTI:
1. typeid运算符
2. dynamic_cast运算符
3. decltype
常见面试题1. C++中的4中类型转化分别是:_________、_________、_________、_________。2. 说说4中类型转化的应用场景。
① static_cast,命名上理解是静态类型转换
使用场景:
用于类层次结构中基类和派生类之间指针或引用的转换注意: 上行转换(派生类 -> 基类)是安全的;
下行转换(基类 -> 派生类)由于没有动态类型检查,所以是不安全的。
用于基本数据类型之间的转换,如把int转换为char,这种带来安全性问题由程序员来保证
使用特点:
主要执行非多态的转换操作,用于代替C中通常的转换操作隐式转换都建议使用static_cast进行标明和替换
② const_cast,字面上理解就是去const属性
使用场景:
常量指针转换为非常量指针,并且仍然指向原来的对象常量引用被转换为非常量引用,并且仍然指向原来的对象
使用特点:
cosnt_cast是四种类型转换符中唯一可以对常量进行操作的转换符去除常量性是一个危险的动作,尽量避免使用
③ reinterpreter_cast,仅仅重新解释类型,但没有进行二进制的转换
使用场景:
不到万不得已,不用使用这个转换符,高危操作使用特点:
reinterpret_cast可以将整型转换为指针,也可以把指针转换为数组reinterpret_cast可以在指针和引用里进行肆无忌惮的转换
④ dynamic_cast,命名上理解是动态类型转换
使用场景:
只有在派生类之间转换时才使用dynamic_cast,type-id必须是类指针,类引用或者void使用特点:
基类必须要有虚函数对于下行转换,dynamic_cast是安全的(当类型不一致时,转换过来的是空指针),而static_cast是不安全的(当类型不一致时,转换过来的是错误意义的指针,可能造成踩内存,非法访问等各种问题)
总结:
去const属性用const_cast基本类型转换用static_cast
不同类型的指针类型转换用reinterpreter_cast
多态类之间的类型转换用daynamic_cast
本篇完。
特殊类的设计中,要掌握好一点,就是只能通过一个接口来获取类,其他的方式不允许,让成员函数或私有或禁掉就可以。特别是单例模式,经常要用到。
对于类型转换,除了dynamic_cast是在多态转换中必须使用外,其他三种方式建议使用,可以增加代码的规范性。
下一篇:从C语言到C++_38(C++的IO流+空间适配器)STL六大组件联系。
相关文章:
从C语言到C++_37(特殊类设计和C++类型转换)单例模式
目录 1. 特殊类设计 1.1 不能被拷贝的类 1.2 只能在堆上创建的类 1.3 只能在栈上创建的类 1.4 不能被继承的类 1.5 只能创建一个对象的类(单例模式)(重点) 1.5.1 饿汉模式 1.5.2 懒汉模式 2. 类型转换 2.1 static_cast 2.2 reinterpret_cast 2.3 const_cast 2.4 d…...
go 使用systray 实现托盘和程序退出
1.先 go get 安装 包 go get github.com/getlantern/systray2.使用的代码 func main() {fmt.Println("开始")systray.Run(onReady, onExit) }func onReady() {systray.SetIcon(icon.Data)systray.SetTitle("Awesome App")systray.SetTooltip("Prett…...
Electron之单例+多窗口
Electron之单例多窗口 Electron 24 React 18 单例可以通过app.requestSingleInstanceLock实现,多窗口可以简单通过路由来实现 单例 const gotTheLock app.requestSingleInstanceLock(); if (!gotTheLock) {app.quit(); } else {app.on(second-instance, (event, …...
A Survey of Knowledge-Enhanced Pre-trained Language Models
本文是LLM系列的文章,针对《A Survey of Knowledge-Enhanced Pre-trained Language Models》的翻译。 知识增强的预训练语言模型综述 摘要1 引言2 背景3 KE-PLMs用于NLU4 KE-PLMs用于NLG5 未来的方向5.1 整合来自同质和异质来源的知识5.2 探索多模态知识5.3 提供可…...
动态规划(选择)
链接:登录—专业IT笔试面试备考平台_牛客网 来源:牛客网 作为队伍的核心,forever97很受另外两个队友的尊敬。 Trote_w每天都要请forever97吃外卖,但很不幸的是宇宙中心forever97所在的学校周围只有3家forever97爱吃的外卖。 如果T…...
IIS WebDAV配置,https绑定及asp设置
IIS支持标准CGI,因此可以用程序语言针对STDIN和STDOUT开发。 IIS CGI配置和CGI程序FreeBasic, VB6, VC 简单样例_Mongnewer的博客-CSDN博客 IIS支持脚本解释CGI,因此可以用脚本语言针对STDIN和STDOUT开发。 IIS perl python cbrother php脚本语言配置…...
【计算机视觉项目实战】中文场景识别
✨专栏介绍: 经过几个月的精心筹备,本作者推出全新系列《深入浅出OCR》专栏,对标最全OCR教程,具体章节如导图所示,将分别从OCR技术发展、方向、概念、算法、论文、数据集等各种角度展开详细介绍。 👨&…...
Java 中 Map 初始化的几种方法
# 传统方式 Map<String, String> map new HashMap<>(); map.put("k1", "v1"); map.put("k2", "v2");# java8新特性-双括号初始化 Map<String, String> map1 new HashMap<>() {{put("k1", "v…...
【学习方法论】学习的三种境界、三种习惯、三个要点,三个心态
学习的三种境界、三种习惯、三个要点,三个心态 三种学习境界 苦学 古人云:“头悬梁、锥刺股”,勤学苦练是第一境界。处于这种层次的同学,觉得学习枯燥无味,对他们来说学习是一种被迫行为,体会不到学习中的…...
[管理与领导-67]:IT基层管理者 - 辅助技能 - 4- 职业发展规划 - 评估你与公司的八字是否相合
目录 前言: 一、概述 二、八字相合的步骤 2.1 企业文化是否相合 2.2.1 企业文化对职业选择的意义 2.2.2 个人与企业三观不合的结果 2.2.3 什么样的企业文化的公司不能加入 2.2 公司的发展前景 2.3 公司所处行业发展 2.4 创始人的三观 2.5 创始人与上司的…...
【PMO项目管理】深入了解项目管理 | Stakeholder 利益相关者 | 利益相关者之间的立场差异
💭 写在前面:本文将带您深入了解项目管理的核心概念和关键要素。我们将从项目管理的基本理解开始,逐步探讨其领域、复杂性和变化的重点,以及项目管理的具体过程。我们还将研究项目的性质以及成功项目所必备的条件。在此过程中&…...
设计模式-原则篇-01.开闭原则
简介 可以把设计模式理解为一套比较成熟并且成体系的建筑图纸,经过多次编码检验目前看来使用效果还不错的软件设计方案。适用的场景也比较广泛,在使用具体的设计模式之前先要学习软件设计的基础 “软件设计原则”,后面的23个设计模式都是…...
JAVA毕业设计096—基于Java+Springboot+Vue的在线教育系统(源码+数据库+18000字论文)
基于JavaSpringbootVue的在线教育系统(源码数据库18000字论文)096 一、系统介绍 本系统前后端分离 本系统分为管理员、用户两种角色(管理员角色权限可自行分配) 用户功能: 注册、登录、课程预告、在线课程观看、学习资料下载、学习文章预览、个人信息管理、消息…...
windows环境搭建ELK
目录 资源下载(8.9.1) ES安装、注册、使用 Kibana安装、注册、使用 Logstash安装、注册、使用 Filebeat安装、使用(如果只有一个数据流,则不需要使用filebeat,直接上logstash即可) 资源下载࿰…...
langchain介绍之-Prompt
LangChain 是一个基于语言模型开发应用程序的框架。它使得应用程序具备以下特点:1.数据感知:将语言模型与其他数据源连接起来。2.代理性:允许语言模型与其环境进行交互 LangChain 的主要价值在于:组件:用于处理语言模型…...
汇编语言Nasmide编辑软件
用来编写汇编语言源程序,Windows 记事本并不是一个好工具。同时,在命令行编译源程序也令很多人迷糊。毕竟,很多年轻的朋友都是用着 Windows 成长起来的,他们缺少在 DOS和 UNIX 下工作的经历。 我一直想找一个自己中意的汇编语言编…...
用python开发一个炸金花小游戏
众所周知扑克牌可谓是居家旅行、桌面交友的必备道具, 今天我们用 Python 来实现一个类似炸金花的扑克牌小游戏,先来看一下基本的游戏规则。 炸(诈)金花又叫三张牌,是在全国广泛流传的一种民间多人纸牌游戏…...
Uniapp中使用uQRCode二维码跳转小程序页面
下载插件 uQRCode官网地址 引入插件 文件如下 //--------------------------------------------------------------------- // github https://github.com/Sansnn/uQRCode //---------------------------------------------------------------------let uQRCode = {};(functio…...
解决husky在mac下不生效的问题
目录 一、问题 1.1 问题描述 二、解决 2.1 解决 一、问题 1.1 问题描述 本文主要解决的问题是,husky在windows上正常生肖,但放到mac下后不生效的问题! 为了确保团队中提交代码的一致性,因此使用了 husky 作为提交的检测工具…...
如何在自动化测试中使用MitmProxy获取数据返回?
背景介绍 当我们在接口或UI自动化项目中,常常会出现这种现象——明明是正常请求,却无法获取到想要的数据返回。 比如: 场景A:页面是动态数据,第一次进入页面获取到的数据,和下次进入页面获取到的数据完全…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/
使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题:docker pull 失败 网络不同,需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

