C++:单例模型、强制类型转换

目录

  • 特殊类的设计
    • 不能被拷贝的类
    • 实现一个类,只能在堆上实例化的对象
    • 实现一个类,只能在栈上实例化的对象
    • 不能被继承的类
  • 单例模式
    • 饿汉模式
    • 懒汉模式
    • 饿汉模式与懒汉模式的对比
      • 饿汉优缺点
      • 懒汉优缺点
      • 懒汉模式简化版本(C++11)
    • 单例释放问题
  • C++强制类型转换
    • static_cast 静态转换
    • reinterpret_cast 不同类型转换
    • const_cast 去除变量的const属性
    • dynamic_cast 动态转换

特殊类的设计

不能被拷贝的类

一个类能不能实现拷贝功能,要看两个默认成员函数:拷贝构造赋值重载

要实现一个不能被拷贝的类,就要防止拷贝构造赋值重载这两个函数的生成。

在类中,如果我们不去手动实现默认成员函数的话,编译器会自动生成默认的成员函数(像 构造、析构 拷贝构造 与 赋值重载等)

在C++11之前,为了避免编译器自动生成 拷贝构造赋值重载,可以将这两个函数直接进行声明,不去实现。但是直接声明的方式,防不住类外的人进行定义实现。

对此,可以直接将 拷贝构造赋值重载 声明设置为私有。

class CopyBan
{
public:CopyBan(){}private://将拷贝构造与赋值重载设置为私有CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);
};int main()
{CopyBan cb1;CopyBan cb2(cb1); //errorreturn 0;
}

在这里插入图片描述

C++11标准出来后,拓展了 delete 关键字 的使用。如果要让编译器不生成默认的成员函数,直接在这个成员函数后加上 delete 关键字即可。对此,上面的代码可以改写为:

class CopyBan
{
public:CopyBan(){}//让编译器删除掉默认成员函数CopyBan(const CopyBan&) = delete;CopyBan& operator=(const CopyBan&) = delete;
};int main()
{CopyBan cb1;CopyBan cb2(cb1); //errorreturn 0;
}

在这里插入图片描述
这样就实现了一个不能被拷贝的类了。

实现一个类,只能在堆上实例化的对象

实现这个类有两种方法,先来介绍第一种:将析构函数设置为私有

class HeapOnly
{
public:HeapOnly(int x = 0):_x(x){}private:~HeapOnly() //设置为私有{cout << "~HeapOnly()" << endl; }int _x;
};int main()
{HeapOnly hoy(1); //error 会调用析构return 0;
}

在这里插入图片描述
hoy 对象在程序运行结束后会调用析构函数,因为析构函数是私有的,调不动。对此,只能在堆上实例化对象:

int main()
{HeapOnly* hoy = new HeapOnly(1);return 0;
}

但是,如何去释放资源呢?

可以这样,在类中实现一个成员函数去调用析构函数:

class HeapOnly
{
public:HeapOnly(int x = 0):_x(x){}void DestroyHeap(){delete this; //调用析构}private:~HeapOnly(){cout << "~HeapOnly()" << endl;}int _x;
};
int main()
{HeapOnly* hoy = new HeapOnly(1);hoy->DestroyHeap(); //利用成员函数去调用析构函数return 0;
}

在这里插入图片描述

下面来介绍第二种方法:将构造函数设置为私有


class HeapOnly
{
public:~HeapOnly(){cout << "~HeapOnly()" << endl;}
private:HeapOnly(int x = 0)  //将构造函数设置为私有:_x(x){}int _x;
};

注意:这个方法会造成,在用 new 的时候也实例化不出对象

int main()
{HeapOnly hoy1(1); 				    //errorstatic HeapOnly hoy2(1);			//errorHeapOnly* hoy3 = new HeapOnly(1);   //errorreturn 0;
}

在这里插入图片描述
解决办法:通过成员函数来调用构造函数,只不过要将这个成员函数要设置为静态的

利用静态成员函数,去调用构造函数,返回这个对象的指针即可:

class HeapOnly
{
public:static HeapOnly* CreateObje(int x) //利用静态成员函数去调用构造{HeapOnly* hoy = new HeapOnly(1);return hoy;}~HeapOnly(){cout << "~HeapOnly()" << endl;}
private:HeapOnly(int x = 0):_x(x){}int _x;
};int main()
{HeapOnly* hoy = HeapOnly::CreateObje(1); //调用静态成员函数delete hoy; //释放资源return 0;
}

在这里插入图片描述

上面这个方法还需要考虑一个拷贝问题:

int main()
{HeapOnly* hoy = HeapOnly::CreateObje(1);HeapOnly hoy2(*hoy); //拷贝,在栈上开辟delete hoy;return 0;
}

构造被封死,但是拷贝构造没有。上面代码没有实现拷贝构造,但是编译器会自动生成一个。拷贝实例化出的对象是在栈上开辟,不符合案例的要求。对此,可以直接将拷贝与赋值直接禁用:

class HeapOnly
{
public:static HeapOnly* CreateObje(int x){HeapOnly* hoy = new HeapOnly(1);return hoy;}~HeapOnly(){cout << "~HeapOnly()" << endl;}
private:HeapOnly(int x = 0):_x(x){}//禁用拷贝与赋值HeapOnly(const HeapOnly& hop) = delete;HeapOnly& operator=(const HeapOnly& hop) = delete;int _x;
};

实现一个类,只能在栈上实例化的对象

使用 new 关键字会调用构造函数,为了防止在堆上开辟对象。可以直接将拷贝构造设置为私有,通过静态成员的方式去调用构造函数:

class StackOnly
{
public://利用静态成员函数进行实例化对象static StackOnly CreateObje(int x){return StackOnly(x); //返回对象}//拷贝StackOnly(const StackOnly& soy):_x(soy._x){}~StackOnly(){cout << "~StackOnly()" << endl;}private:StackOnly(int x)  //构造函数设置为私有:_x(x){}int _x;
};int main()
{//StackOnly* soy = new StackOnly(1);  //error new会调用构造StackOnly soy = StackOnly::CreateObje(1); //调用构造+拷贝构造return 0;
}

在这里插入图片描述
上面这样的方式防不住创建静态的对象:

int main()
{StackOnly soy = StackOnly::CreateObje(1); //调用构造+拷贝构造static StackOnly soy1 = soy; //会利用拷贝构造生成一个静态的对象return 0;
}

在这里插入图片描述
有小伙伴就会说,将拷贝构造禁用,就可以避免静态对象的生成了。

如果将拷贝构造禁用了,利用静态成员函数去实例化的方法会失效,因为用到的传值返回,需要用到拷贝构造。

传值返回是一个将亡值,那么可以生成一个移动构造不就解决了吗?

下面来实现一下:

class StackOnly
{
public://利用静态成员函数进行实例化对象static StackOnly CreateObje(int x){return StackOnly(x);}StackOnly(StackOnly&& soy) // 移动构造:_x(soy._x){}~StackOnly(){cout << "~StackOnly()" << endl;}private:StackOnly(const StackOnly& soy) = delete; //禁用拷贝StackOnly(int x):_x(x){}int _x;
};int main()
{StackOnly soy = StackOnly::CreateObje(1); //移动构造//static StackOnly soy1 = soy; //errorreturn 0;
}

在这里插入图片描述

但是,使用移动构造真的解决问题了吗?传值返回是一个将亡值,会去调用移动构造。但是,防不住有些人这样操作:

int main()
{StackOnly soy = StackOnly::CreateObje(1); //拷贝构造static StackOnly soy1 = move(soy); //将soy设置为将亡值,调用移动构造return 0;
}

在这里插入图片描述

使用静态成员函数去调用构造函数,通过传值返回对象的方式,很难去防止静态对象生成。很难禁掉,本质就是前者需要用到拷贝构造

不能被继承的类

在C++11之前,可以将类的构造函数设置为私有。子类对象如果继承了这个类的话,在实例化阶段就调不动父类的构造函数,从而达到无法继承的效果

C++11之后,推出了 final 关键字。final关键字修饰过类,不能被继承

class NonInherit final //final关键字修饰的类不能被继承
{//...
}

单例模式

  • 单例模式:一个类只能创建一个唯一的对象

该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享

实现单例模式的方式有两种:饿汉模式、懒汉模式

饿汉模式

在程序运行起来就实例化出这个对象,且为唯一的一份

记住单例模式要的是创建一个唯一的对象

举个示例,实现一个简单的饿汉模式:

要限制这个类实例化对象,将构造函数设置为私有、拷贝和赋值禁用

#include <iostream>
#include <string>
#include <vector>class Singleton
{
public:~Singleton() //析构{std::cout << "~Singleton()" << std::endl;}
private:Singleton(){} //设置为私有,防止类外实例化对象Singleton(const Singleton& stn) = delete;Singleton& operator=(const Singleton& stn) = delete;
private:vector<string> _v;int _n = 0;
};

下面来考虑一下,如何实例化出唯一的一份对象。

可以定义一个Singleton* _ins静态成员指针,当然也可以是Singleton _ins对象(静态成员在整个类都是唯一的)。

在这里定义一个 Singleton* _ins的静态成员指针,在类中进行声明,在类外进行实例化。当然还要实现一个静态成员函数,用于获取这个静态成员指针。

为了方便演示,下面还增添一些功能代码:

#include <iostream>
#include <string>
#include <vector>class Singleton
{
public:~Singleton() //析构{std::cout << "~Singleton()" << std::endl;}static Singleton* GetInstance() //用于获取对象指针{return _ins; //返回静态成员的指针,方便在类外部使用}void AddString(const string& str){ _v.push_back(str);}void Print(){for (auto& e : _v)cout << e << endl;}
private:Singleton(){} //设置为私有,防止类外实例化对象Singleton(const Singleton& stn) = delete;Singleton& operator=(const Singleton& stn) = delete;
private:vector<string> _v;int _n = 0;static Singleton* _ins; //声明静态成员指针
};Singleton* Singleton::_ins = new Singleton; //初始化int main()
{Singleton* sln = Singleton::GetInstance(); //获取对象的指针,唯一的sln->AddString("苹果");sln->AddString("香蕉");sln->AddString("梨");sln->AddString("西瓜");sln->Print();return 0;
}

在这里插入图片描述

一开始就通过静态成员指针或者是对象直接在类外初始化的方式被饿汉模式,也就是在 main 函数之前进行初始化。

饿汉模式 相对应的就是 懒汉模式

懒汉模式

懒汉模式与饿汉模式不同,并不是一上来就进行初始化。而是在第一次访问实例的时候进行初始化

对上述代码进行修改,变成懒汉模式。下面只展示修改的代码,方便对比:

class Singleton
{
public:~Singleton(){cout << "~Singleton()" << endl;}//懒汉处理static Singleton* GetInstance(){if (_ins == nullptr){_ins = new Singleton; //懒汉模式的初始化}return _ins;}//... 其他成员函数
private:Singleton() {} //设置为私有,防止类外实例化对象Singleton(const Singleton& stn) = delete;Singleton& operator=(const Singleton& stn) = delete;vector<string> _v;static Singleton* _ins; //定义静态Singleton指针
};//懒汉模式
Singleton* Singleton::_ins = nullptr; 

懒汉模式并不是一上来就进行初始化,而是将静态成员对象的指针的初始化放到静态成员函数内部进行

下面来测试一下懒汉模式:

int main()
{Singleton* sln = Singleton::GetInstance(); sln->AddString("苹果");sln->AddString("香蕉");sln->AddString("梨");sln->AddString("西瓜");sln->Print();return 0;
}

在这里插入图片描述
有老铁就会说,这个 懒汉模式 和 饿汉模式 也没有多少变化啊。只是初始化方式不同而已。

但真的只是初始化有所不同吗?

饿汉模式与懒汉模式的对比

饿汉优缺点

先来分析饿汉模式缺点:

  1. 当一个单例对象很大
  • 当这个单例对象不需要使用时,会占用资源(main函数之前就要进行资源的申请)
  • 会影响程序的启动(程序在还没有启动前就要初始化一段时间)
  1. 当一个程序中有两个单例对象都是使用的饿汉模式,并且相互之间存在依赖关系。要求单例1对象先创建,单例2对象后创建。饿汉模式无法控制创建的顺序

饿汉模式的优点:创建单例对象很简单(相较于懒汉模式)

懒汉优缺点

优点:懒汉模式完美解决了饿汉模式的缺点

缺点:懒汉模式会造成线程安全问题

回过头来看看懒汉模式下的初始化那段代码,同样的为了方便查阅,只展示局部代码:

//懒汉处理
static Singleton* GetInstance()
{if (_ins == nullptr){_ins = new Singleton; //懒汉模式的初始化}return _ins;
}

有没有这样的一种情况:在多线程下,有两个甚至多个线程同时进行 new 对这个单例进行初始化,造成 _ins 指针被多次赋值

线程执行总得有个先后顺序,假设线程一先执行了初始化的工作,正在调用其他函数执行其他的功能。此时,线程二来了,直接进行 new 操作申请了一块新的资源空间,然后赋值给 _ins。线程一前面执行的函数(像是读写操作),就会被覆盖了做无用功。

相对比懒汉模式,饿汉模式就没有线程安全问题,因为饿汉模式在 main 程序执行前直接就进行了初始化。

对此,实现懒汉模式的单例需要引入互斥锁来解决线程安全问题。

通过双检查加锁来处理线程安全问题:只需要保证第一次加锁的情况

class Singleton
{
public:~Singleton(){cout << "~Singleton()" << endl;}static Singleton* GetInstance(){//懒汉线程安全问题的处理//双检查加锁:解决线程安全问题if (_ins == nullptr){//上锁_mtx.lock();if (_ins == nullptr){_ins = new Singleton; //保证一次创建}//解锁_mtx.unlock();}return _ins;}
private:Singleton() {} //设置为私有,防止类外实例化对象Singleton(const Singleton& stn) = delete;Singleton& operator=(const Singleton& stn) = delete;vector<string> _v;static mutex _mtx;static Singleton* _ins; //定义静态Singleton指针
};Singleton* Singleton::_ins = nullptr;
mutex Singleton::_mtx; //初始化互斥锁

双检查加锁:

  • 第一次 if 判断是为了提高效率,避免多个线程在访问这块代码的时候,多次进行上锁解锁操作
  • 第二个 if 判断是为了保证线程安全,只进行一次资源的申请

正是因为线程安全问题,懒汉模式对单例的初始化就变得复杂起来(对比饿汉模式)

懒汉模式简化版本(C++11)

前提声明:简化版本的懒汉模式只适合C++11,C++11之前使用这个版本的懒汉模式不能保证线程安全问题

C++11之后,局部的静态成员变量在进行初始化,当其他线程来获取时会进入阻塞状态,直到初始化成功,从而保证了线程安全问题。

下面来实现一个简化版本的懒汉模式:

实现不需要用到互斥锁,也不需要定义静态的单例对象的指针。在静态成员函数中定义一个静态的单例对象,使用的时候,返回这个静态的单例对象地址即可(当然也可以返回这个对象的引用,看个人的喜欢)

class Singleton
{
public:~Singleton(){cout << "~Singleton()" << endl;}static Singleton* GetInstance(){//懒汉模式的简化实现static Singleton inst;return &inst; //返回这个对象的地址}//...其他成员函数
private:Singleton() {} Singleton(const Singleton& stn) = delete;Singleton& operator=(const Singleton& stn) = delete;vector<string> _v;
};
int main()
{Singleton* sln = Singleton::GetInstance();return 0;
}

在这里插入图片描述
局部的静态变量有一个特点:只会初始化一次

局部的静态变量只有当程序运行到才会初始化;全局的静态变量在程序运行起来之前就进行初始化

单例释放问题

一般情况下,全局都要使用单例对象,所以单例对象不需要显示的释放。程序结束,资源也就被回收了。

但是有些特殊情况还是会考虑单例对象资源释放的。可以提供一个静态成员函数去调用析构函数;

当然,如果怕内存泄漏的话可以提供一个内部类。在单例类中定义内部类的对象,当内部类对象生命周期结束后,析构函数去调用单例的提供的释放资源的函数。从而达到单例对象指针的资源释放的功能。

具体可以参考以下代码:

class Singleton
{
public:static Singleton* GetInstance(){// 双检查加锁if (_ins == nullptr) {_imtx.lock();if (_ins == nullptr)  {_ins = new Singleton;}_imtx.unlock();}return _ins;}static void DelInstance() //释放资源的函数{_imtx.lock();if (_ins){delete _ins;_ins = nullptr;}_imtx.unlock();}// 内部类:单例对象回收class GC{public:~GC(){DelInstance();}};~Singleton(){// 持久化// 比如要求程序结束时,将数据写到文件,单例对象析构时持久化就比较好}private:// 限制类外面随意创建对象Singleton(){}// 防拷贝Singleton(const Singleton& s) = delete;Singleton& operator=(const Singleton& s) = delete;private:vector<string> _v;static GC _gc;static Singleton* _ins;static mutex _imtx;
};

单例模式就介绍到这里,下面来介绍一下C++的类型转换:

C++强制类型转换

在 C语言 中如果遇到 赋值运算符左右两侧类型不同,或者形参与实参类型不匹配,或者返回值类型与接收返回值类型不一致时,就需要发生类型转化

C语言的类型类型转换可以分成两种:隐式类型转换强制类型转换

  • 隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
  • 显式类型转化:需要用户自己处理

C语言的隐式类型转换有时候会造成一些莫名的 BUG,来看这样的一段代码:

假设这段代码是完成数组的插入功能

void Insert(size_t pos, char ch)
{int end = _size; // _size是数组个数while(end >= pos){//...--end;}
}

上面这段代码中,乍一看好像没有什么问题,指定 pos 位置进行字符的插入。

但是,仔细一点可以看到 end 变量是 int 类型,pos 是 size_t 类型。在进行大小比较的时候就会发生类型转化,end 变量会转换为 size_t 类型

一般的,pos是大于0的数还好,一旦 pos为 0 时,end 是无符号整型,当end减到0时,循环体再进行end-- 操作,end 会变成负1吗?不会,end 会变成一个很大的无符号整数。程序就进入了死循环

像上面隐式类型转换很难去察觉。

C语言的显式类型转换将所有情况混合在一起,也会造成代码不够清晰。

面对C语言类型转换出现的缺点,C++要说将C语言这套类型转换全部丢掉是不可能的,因为C++要兼容C语言。

对此,为了规范类型转换,C++搞出了属于自己的一套类型转换方式。

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

static_castreinterpret_castconst_castdynamic_cast

static_cast 静态转换

  • static_cast 用于非多态类型的转换(静态转换),static_cast 它不能用于两个不相关的类型进行转换

示例:

int main()
{//静态转换double b = 11.22;int a = static_cast<int>(b); //a、b变量为相似类型可以进行转换cout << a << endl;return 0;
}

在这里插入图片描述

如果两个不相近的类型使用 static_cast会怎么样?

int main()
{//静态转换double b = 11.22;int a = static_cast<int>(b); //a、b变量为相似类型可以进行转换int *p = static_cast<int*>(a);//p是指针、a是整形类型不相近return 0;
}

编译器会直接报错:
在这里插入图片描述
但是使用C语言的强制类型转换就可以:

int main()
{//静态转换double b = 11.22;int a = static_cast<int>(b); //a、b变量为相似类型可以进行转换cout << a << endl;int* p = (int*)a; //强制类型转换cout << p << endl;return 0;
}

在这里插入图片描述

使用C++的类型转换更能保证数据的安全性。C++的类型转换只是一个规范,并不是强制性的。不是强制性的就会造成有人会遵守,有人不会遵守。

reinterpret_cast 不同类型转换

  • reinterpret_cast 用于将一种类型转换为另一种不同的类型(不同类型之间的强制转换)

示例:

int main()
{//不同类型的转换int c = 20;int* p1 = reinterpret_cast<int*>(c);cout << p1 << endl;return 0;
}

reinterpret_cast后面跟的尖括号表示:要将变量转化为怎么样的类型

const_cast 去除变量的const属性

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

示例:

int main()
{//去除常量的属性const int x = 10;int* p = const_cast<int*>(&x);*p = 20;cout << x << " " << *p << endl;return 0;
}

运行前来猜一下,x 和 *p 的值是什么?

在这里插入图片描述
这是编译器的优化问题,x的值是直接在寄存器中获取的,*p 是在内存中获取的。寄存器的运行速度是远高于内存的,并不能说 x的值没有修改,而是修改了还没有来得及更新。可以使用 volatile 关键字,让 x 不要去寄存器中获取值。

int main()
{//去除常量的属性volatile const int x = 10;//volatile不让编译器去寄存器拿数据int* p = const_cast<int*>(&x);*p = 20;cout << x << " " << *p << endl;return 0;
}

此时再来看看打印的结果:
在这里插入图片描述
在使用 const_cast 的时候要注意,后跟尖括号要转换为对应的指针类型,不然编译器会报错

int main()
{//去除常量的属性volatile const int x = 10;int p = const_cast<int>(x); //errorp = 20;cout << x << " " << p << endl;return 0;
}

在这里插入图片描述

dynamic_cast 动态转换

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

前提声明:父类表示上,子类表示下

在这里引入两个词:向上转型向下转型

  1. 向上转型子类对象指针/引用 转换为 父类指针/引用(不需要转换,赋值兼容规则)

对向上转型不太理解,或者说对继承还不熟悉的小伙伴可以转站看看小编写的这篇文章:C++继承介绍,直接跳转到文章的 基类和派生类对象之间的赋值 进行查阅。

  1. 向下转型父类对象指针/引用 转换为 子类指针/引用(用dynamic_cast转型)

注意:dynamic_cast 只能用于父类含有虚函数的类dynamic_cast 转换成功会返回这个子类的指针/引用转换失败会返回0

示例:

class A
{
public:virtual void f(){}
};class B : public A
{};void fun(A* pa, const string& s)
{cout << "pa指向" << s << endl;B* pb = dynamic_cast<B*>(pa);
}int main()
{A a;B b;fun(&a, "指向父类的指针");fun(&b, "指向子类的指针");return 0;
}

实现一个简单的继承体系(B 继承 A,A实现了虚函数),通过 fun 函数的参数 pa 父类的指针,对被传入的参数进行 向上转型操作。然后在 fun 函数内部实现 向下转型(利用 dynamic_castpa 父类的指针转换为 pb 子类的指针

在这里插入图片描述

如果 父类对象的 指针(或者是引用)原先就是指向派生类的,在使用 dynamic_cast进行动态转换可以转换成功

如果 父类对象的 指针(或者是引用)原先指向的是基类的,在使用 dynamic_cast进行动态转换会转换失败

没错,使用 dynamic_cast 动态转换是有条件的。同样的,使用C语言的强制类型转化也可以:

class A
{
public:virtual void f(){}
};class B : public A
{};void fun(A* pa, const string& s)
{cout << "pa指向" << s << endl;//强制转换B* pb1 = (B*)pa;cout << "[强制转换]:pb1:" << pb1 << endl;//动态转换B* pb2 = dynamic_cast<B*>(pa);cout << "[dynamic_cast转换]:pb2:" << pb2 << endl;
}int main()
{A a;B b;fun(&a, "指向父类的指针");fun(&b, "指向子类的指针");return 0;
}

在这里插入图片描述
强制转换不会管 pa 原先指向的是父类还是子类,我都给你转换,然后返回一个地址。这个地址可以使用吗?答案是不可以的,会造成越界访问。不相信的老铁可以试试,在这里就不演示了。

使用C语言的强制类型转换是不安全的,使用C++的 dynamic_cast 动态转换是安全的

为了代码的可视性,建议大家还是使用C++的强制类型转换的方式。虽然不是强制性的,但是可以避免很多的潜在 bug 发生。

这篇文章就介绍到这里,感谢大家的观看!

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

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

相关文章

索引失效的场景有哪些?

一.概念 索引失效是指在查询时&#xff0c;数据库引擎无法使用索引来加速查询&#xff0c;从而导致查询性能下降。常见的索引失效原因有以下几种&#xff1a; 索引列没有被包含在查询条件中。如果查询条件中没有包含索引列&#xff0c;那么数据库引擎无法使用索引来加速查询。…

域提权漏洞系列分析-Zerologon漏洞分析

2020年08⽉11⽇&#xff0c;Windows官⽅发布了 NetLogon 特权提升漏洞的⻛险通告&#xff0c;该漏洞编号为CVE-2020-1472&#xff0c;漏 洞等级&#xff1a;严重&#xff0c;漏洞评分&#xff1a;10分&#xff0c;该漏洞也称为“Zerologon”&#xff0c;2020年9⽉11⽇&#xff…

WinRAR技巧:如何让多个文件压缩到更小!?

但我们要压缩多个文件的时候&#xff0c;可能会出现压缩后的体积仍然过大&#xff0c;或者需要将文件再压缩到更小&#xff0c;这种情况下&#xff0c;小编之前建议过大家将文件压缩成7z格式就会更加压缩体积。今天分享另一个技巧&#xff0c;帮助我们将多个文件压缩到更小。 …

【网络安全】勒索软件ShrinkLocker使用 windows系统安全工具BitLocker实施攻击

文章目录 威胁无不不在BitLocker 概述如何利用BitLocker进行攻击如何降低影响Win11 24H2 装机默认开启 BitLocker推荐阅读 威胁无不不在 网络攻击的形式不断发展&#xff0c;即便是合法的 Windows 安全功能也会成为黑客的攻击工具。 卡巴斯基实验室专家 发现 使用BitLocker的…

以不变应万变:在复杂世界中保持初心,坚持原则

在这个日新月异、瞬息万变的世界里&#xff0c;人情世故也显得尤为复杂。我们常常会因为忙碌的生活、工作压力以及人际关系的纠葛而感到迷茫和疲惫。在面对这些复杂局面的同时&#xff0c;如何保持内心的平静&#xff0c;坚持自己的原则&#xff0c;并在变幻莫测的环境中持续成…

ClickHouse架构概览 —— Clickhouse 架构篇(一)

文章目录 前言Clickhouse 架构简介Clickhouse 的核心抽象列和字段数据类型块表 Clickhouse 的运作过程数据插入过程数据查询过程数据更新和删除过程 前言 本文介绍了ClickHouse的整体架构&#xff0c;并对ClickHouse中的一些重要的抽象对象进行了分析。然后此基础上&#xff0…

乘风破浪,创维汽车旗舰店落户安徽

2024年5月19日&#xff0c;创维汽车宣城家奇体验中心盛大开业。宣城市委办公室副主任师典雅、市投资促进局副局长金崇学、经开区管委会副主任汤晓峰、宣城市通信局局长梁登峰、创维汽车战区总经理刘俊、创维汽车大区总监王大明等人出席此次开业盛典&#xff0c;共同见证了创维汽…

内网穿透实现公网访问自己搭建的Ollma架构的AI服务器

内网穿透实现公网访问自己搭建的Ollma架构的AI服务器 [2024年5月9号我发布一个博文关于搭建本地AI服务器的博文][https://blog.csdn.net/weixin_41905135/article/details/138588043?spm1001.2014.3001.5501]&#xff0c;今天我们内网穿透实现从公网访问我的本地AI服务器&…

全国多地入夏!对抗“高温高湿”约克VRF中央空调有妙招

随着气温飙升,北京、上海、广州、南京、天津、江苏、新疆、内蒙古部分地区等多地进入夏季状态,华北、黄淮等不少地方最高气温都超过了30℃,大街上人们短袖、短裤纷纷上阵,一派夏日炎炎的景象。 炎热夏季不仅高温频频来袭,往往还伴随着降雨带来的潮湿,天气湿热交织容易让人们身…

C++第二十弹---深入理解STL中vector的使用

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、vector的介绍 2、vector的使用 2.1、构造函数和赋值重载 2.1.1、构造函数的介绍 2.1.2、代码演示 2.2、容量操作 2.3、遍历 2.4、增删…

【STM32】 独立看门狗配置方法

什么是看门狗 看门狗&#xff08;watchdog&#xff09;指的是一种监控系统或程序&#xff0c;用于定期检测和监控其他系统或程序的运行状态&#xff0c;并在出现问题或故障时采取相应的措施。它可以是硬件设备&#xff0c;也可以是软件程序。 在计算机领域中&#xff0c;看门狗…

【OceanBase诊断调优】—— 排查 IO 问题的方法

本文主要介绍 OceanBase 数据库 V4.x 版本中排查 IO 问题的方法以及 IO 相关的日志和视图。 IO 相关问题 -4013 内存爆、IoControl 模块内存泄漏 目前 IO 内存爆可能的原因如下&#xff0c;及相应的排查方法。 其他模块使用 IO 内存后未释放导致泄漏。 日志分析。 通过关键词…

移动云:连接未来的智慧之旅

随着数字化转型的加速&#xff0c;云服务在各行各业中的应用越来越广泛。移动云不仅提供了灵活的计算和存储资源&#xff0c;还通过创新的技术手段&#xff0c;为企业和开发者解决了许多实际问题。在这个变革的大背景下&#xff0c;移动云服务作为中国移动倾力打造的云业务品牌…

​​​【收录 Hello 算法】10.4 哈希优化策略

目录 10.4 哈希优化策略 10.4.1 线性查找&#xff1a;以时间换空间 10.4.2 哈希查找&#xff1a;以空间换时间 10.4 哈希优化策略 在算法题中&#xff0c;我们常通过将线性查找替换为哈希查找来降低算法的时间复杂度。我们借助一个算法题来加深理解。 Question 给…

vscode 插件-01基础

翻译 Chinese (Simplified) (简体中文) Language Pack for Visual Studio Code 适用于 VS Code 的中文&#xff08;简体&#xff09;语言包 远程连接 Remote Development Remote Development是vscode的远程编程与调试的插件&#xff0c;使用这个插件可以在很多情况下代替vim…

Linux VIM指令

三种模式 命令模式&#xff1a;控制屏幕光标的移动&#xff0c;字符、字或行的删除等输入对文件的一些指令 插入模式&#xff1a;对文件内容进行文字输入 底行摸索&#xff1a;文件保存或退出&#xff0c;也可以进行文件替换&#xff0c;找字符串&#xff0c;列出行号等操作…

error LNK2038: 检测到“boost_log_abi”的不匹配项

最近项目上遇到了这个问题&#xff0c;看提示和查阅资料知道是boost版本不匹配的问题&#xff0c;但是项目原因换boost版本不太现实&#xff0c;一时竟然不知道怎么解决。 后来在同事的提示下&#xff0c;对于boost_log相关的代码&#xff0c;引用其头文件时&#xff0c;放到最…

云计算-关系型数据库(Relational Database)

关系数据库服务&#xff08;RDS&#xff09;&#xff08;Relational Database Service (RDS)&#xff09; Amazon RDS 可用于在云中设置和运行关系数据库。它支持多种数据库实例类型以及多个数据库引擎&#xff0c;如 Amazon Aurora、PostgreSQL、MySQL、MariaDB、Oracle 数据库…

Unity环绕物体的摄像机,添加了遮挡的适应

第三人人称摄像机 支持的功能 设定目标后使用鼠标可以环绕目标点旋转&#xff0c;且会进行遮挡的适配&#xff0c;当有遮挡的时候会移动差值移动到没有遮挡的位置。 使用方式 将vThirdPersonCamera 挂在与摄像机上然后为target赋值。 如果有需要检测遮挡的层级可以修改&…

早餐店燃气安全岂能马虎?探头选择与年检必须到位

在现代都市生活中&#xff0c;早餐店作为人们日常生活中的重要一环&#xff0c;其安全性问题日益受到人们的关注。其中&#xff0c;燃气泄漏引发的火灾和爆炸事故尤为令人担忧。 因此&#xff0c;点式可燃气体报警器在早餐店中的应用显得尤为重要。 在这篇文章中&#xff0c;…