【C++高阶】:特殊类设计和四种类型转换

✨                                          人生如梦,朝露夕花,宛若泡影     🌏

📃个人主页:island1314

🔥个人专栏:C++学习

⛺️ 欢迎关注:👍点赞 👂🏽留言 😍收藏  💞 💞 💞

                


特殊类设计和四种类型转换:

1. 特殊类设计

1.1 无法被拷贝的类

1.1.1 C++98的做法:

1.1.2 C++11的做法:

1.2 只能在堆上创建对象的类

1.3 只能在栈上创建对象的类

🍉1.3.1 实现一:

🥝1.3.2 实现二:

1.4 无法被继承的类

1.4.1 C++98的做法:

1.4.2 C++11的做法:

1.5 只能创建一个对象(单例模式)

🍍1.5.1 设计模式:

🍎1.5.2 单例模式:

🌈饿汉模式

🌈懒汉模式

2. 类型转换

2.1 C语言中的类型转换

2.2 C++中的类型转换

🌱2.2.1 static_cast

☘️ 2.2.2 reinterpret_cast

🍀2.2.3 const_cast

🌿2.2.4  dynamic_cast

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

2.4 RTTI

2.5 扩展

 2.5.1 内置类型和自定义类型之间

2.5.2 自定义类型与自定义类型之间

2.6 建议

📖后言


1. 特殊类设计

1.1 无法被拷贝的类

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

1.1.1 C++98的做法:
  • 将拷贝构造函数与赋值运算符重载只声明不定义。(防自己)
  • 并且将其访问权限设置为私有即可。(防外面)
class CopyBan
{// ...CopyBan(const CopyBan&)=delete;CopyBan& operator=(const CopyBan&)=delete;//...
};

原因:

  1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
  2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。

1.1.2 C++11的做法:

   💢C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟=delete,表示让编译器删除掉该默认成员函数。

class CopyBan
{// ...CopyBan(const CopyBan&)=delete;CopyBan& operator=(const CopyBan&)=delete;//...
};

1.2 只能在上创建对象的类

实现方式如下:

思路1:

1. 将类的构造函数私有,拷贝构造声明成私有(可以直接delete掉)。防止别人调用拷贝在栈上生成对象。

      注意:拷贝构造可以直接delete掉,但是构造函数不行!!因为我们还需要利用构造函数在堆上创建对象。

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

     注意:这里涉及到的是先有鸡还是先有蛋的问题,因为如果不去创建这个对象就没有办法去调用他的构造函数,但是没有调用构造函数就没有办法创建对象。所以这里必须通过静态成员函数的返回值去构造堆对象。

class HeapOnly
{
public://HeapOnly* CreatrObj()static HeapOnly* CreateObject()//需要用静态的{return new HeapOnly;}
private:HeapOnly() {};HeapOnly(const HeapOnly&) = delete;
};int main()
{//HeapOnly H; //这个不行HeapOnly* p = HeapOnly::CreateObject();return 0;
}

思路2:

1、相比较于上一种思路把构造函数私有、拷贝构造delete,也可以选择将析构函数给设成私有。

      析构函数设置成私有同样会导致对象无法在栈上进行创建。因为自定义类型在栈帧中销毁的时候会去自动调用他的析构函数,但是因为调不到所以会报错。

2、封装一个destory的成员函数,这样我们可以手动释放堆空间的资源。

      在堆上创建对象是用指针去接受,所以并不影响,但是内存需要我们去手动释放,因此我们需要封装destory的成员函数去调用delete,这样delete可以对应调用到析构函数。 这里有两个方案:一种是设置能静态成员函数(类域调用),一种是设置成普通成员函数(对象自己调用)。

class HeapOnly
{
public://方案1 static void Destroy(HeapOnly* hp){delete hp;}//方案2void Destroy(){delete this;}
private:~HeapOnly() {};
};int main()
{//HeapOnly H; 调不到析构函数,无法创建//静态成员函数释放HeapOnly* hp1 = new HeapOnly;HeapOnly::Destroy(hp1);//HeapOnly hp(*hp1);//普通成员函数释放HeapOnly* hp2 = new HeapOnly;hp2->Destroy(); //手动释放return 0;
}

1.3 只能在上创建对象的类

💢new和delete是全局的运算符重载函数,因此我们只要将这两个给禁用掉,就不会在堆上创建对象。

🍉1.3.1 实现一
class StackOnly
{
public:static StackOnly CreateObj(){return StackOnly();}// 封死拷贝构造//StackOnly(const StackOnly& s) = delete; // 禁掉operator newvoid* operator new(size_t size) = delete;void operator delete(void* p) = delete;private:StackOnly():_a(0){}
private:int _a;
};int main()
{// 1.由于构造私有化,无法直接构造//static StackOnly s1;//StackOnly s2;//StackOnly* s3 = new StackOnly;// 2.在栈上创建对象,如果封死拷贝构造,//由于封死了拷贝构造,Creatrobj也无法调用,因此下面我们都不封拷贝构造StackOnly s4 = StackOnly::CreateObj();// 3. 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉StackOnly* s5 = new StackOnly(s4); //拷贝构造// 4.由于拷贝构造没封,即使封死了operator new,也可以调用static StackOnly s6(s4); // 静态区return 0;
}
🥝1.3.2 实现二:

 🚀由于仍然是没有严格的封死,因此我们还是得封拷贝构造, 但是导致上面之前得都走不通
 但是因为我们这里实现了拷贝构造,那么这里就不会默认生成移动构造了,因此我们得写出移动构造

class StackOnly
{
public:static StackOnly CreateObj(){return StackOnly();}// 封死拷贝构造StackOnly(const StackOnly& s) = delete; // 提供移动构造StackOnly(StackOnly&& s) {}private:StackOnly():_a(0){}
private:int _a;
};int main()
{// 1.由于构造私有化,无法直接构造//static StackOnly s1;//StackOnly s2;//StackOnly* s3 = new StackOnly;// 2.拷贝构造封死,然后再调用移动构造,就可以彻底封死了StackOnly s4 = StackOnly::CreateObj();//StackOnly* s5 = new StackOnly(s4); //static StackOnly s6(s4); // 3.漏洞static StackOnly s7(move(s4));  //移动构造StackOnly* s8 = new StackOnly(move(s4));return 0;
}

1.4 无法被继承的类

1.4.1 C++98的做法:

💢构造函数私有化,这样子类调用不到父类的构造函数,无法实现继承

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

💢直接用final关键字,可以使得该类无法被继承

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

1.5 只能创建一个对象(单例模式)

🍍1.5.1 设计模式:

 🎈设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也  是有套路的,后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。
 🎈使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。

🍎1.5.2 单例模式:

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

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

🌈饿汉模式

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

//main函数之前创建出来的就是饿汉
// 单例:全局只有唯一实例对象
///*——-------—————— 饿汉模式 ————————————*/ 
// 优点:简单
// 缺点:
//	1、多个饿汉模式的单例,某个对象初始化内容较多(读文件),会导致程序启动慢
//	2、A和B两个饿汉,对象初始化存在依赖关系,要求A先初始化,B再初始化,饿汉无法保证class InfoMgr
{
public:static InfoMgr& GetInstance(){return _ins;}void Print(){cout << _ip << endl;cout << _port << endl;cout << _buffSize << endl;}
private:InfoMgr(const InfoMgr&) = delete;InfoMgr& operator=(const InfoMgr&) = delete;InfoMgr(){cout << "InfoMgr()" << endl;}
private:string _ip = "127.0.0.1";int _port = 80;size_t _buffSize = 1024 * 1024;//...static InfoMgr _ins;
};
InfoMgr InfoMgr::_ins; // 在程序入口之前就完成单例对象的初始化int main()
{cout << endl;InfoMgr::GetInstance().Print();//InfoMgr copy(InfoMgr::GetInstance());cout << endl;InfoMgr::GetInstance().Print();cout << endl;cout << &InfoMgr::GetInstance() << endl;cout << &InfoMgr::GetInstance() << endl;return 0;
}

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

🌈懒汉模式

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

写法一:

class InfoMgr
{
public:// 第一次调用时再去创建单例对象// 线程安全的风险(暂时没学)static InfoMgr& GetInstance(){if (_pins == nullptr) //第一次调用刚好是空{_pins = new InfoMgr;}return *_pins;}void Print(){cout << _ip << endl;cout << _port << endl;cout << _buffSize << endl;}static void DelInstance() //主动释放单例{delete _pins;_pins = nullptr;}private:InfoMgr(const InfoMgr&) = delete;InfoMgr& operator=(const InfoMgr&) = delete;InfoMgr(){cout << "InfoMgr()" << endl;}
private:string _ip = "127.0.0.1";int _port = 80;size_t _buffSize = 1024 * 1024;//...static InfoMgr* _pins;
};InfoMgr* InfoMgr::_pins = nullptr; //设计为指针

写法二:

懒汉模式更简单的写法,C++11之后更加推荐

//懒汉模式更简单的写法,C++11之后更加推荐
class InfoMgr
{
public:// 第一次调用时再去创建单例对象// 线程安全的风险(暂时没学)static InfoMgr& GetInstance(){//C++11之后才是这样static InfoMgr ins; //创建一个局部的静态,因为局部的静态是在创建的时候初始化return ins;}void Print(){cout << _ip << endl;cout << _port << endl;cout << _buffSize << endl;}private:InfoMgr(const InfoMgr&) = delete;InfoMgr& operator=(const InfoMgr&) = delete;InfoMgr(){cout << "InfoMgr()" << endl;}
private:string _ip = "127.0.0.1";int _port = 80;size_t _buffSize = 1024 * 1024;//...static InfoMgr* _pins;
};InfoMgr* InfoMgr::_pins = nullptr; //设计为指针

调用及结果示意:

int main()
{cout << endl;InfoMgr::GetInstance().Print();cout << endl;InfoMgr::GetInstance().Print();cout << endl;//查看多次调用时对象地址cout << &InfoMgr::GetInstance() << endl;cout << &InfoMgr::GetInstance() << endl;return 0;
}

从饿汉与懒汉模式得打印结果我们可以发现,饿汉模式是先进行的全局然后再进行main函数,而懒汉模式是先进入main函数中才开始。

2. 类型转换

2.1 C语言中的类型转换

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

  1.  隐式类型转化:编译器在编译阶段自动进行,能转就转,不能转就编译失败
  2.  显式类型转化:需要用户自己处理
/* ———————————— a. 内置类型——————————————————*///1、隐式类型转换    整形之间/整形和浮点数之间//2、显示类型的转换  指针和整形、指针之间int main()
{int i = 1;// 隐式类型转换double d = i;printf("%d, %.2f\n", i, d);int* p = &i;// 显示的强制类型转换int address = (int)p;printf("%p, %d\n", p, address);return 0;
}

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

2.2 C++中的类型转换

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

🌱2.2.1 static_cast

       static_cast用于非多态类型的转换(静态转换),编译器隐式执行的任何类型转换都可用(相近类型用static_cast->意义相似的类型)

int main()
{double d = 12.34;int a = static_cast<int>(d);cout << a << endl;return 0;
}
☘️ 2.2.2 reinterpret_cast

        reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释,用于将一种类型转换为另一种不同的类型(一定的关联,但是意义不相似的的类型用reinterpret_cast 

int main()
{double d = 12.34;int a = static_cast<int>(d);cout << a << endl;// 这里使用static_cast会报错,应该使用reinterpret_cast//int *p = static_cast<int*>(a);int* p = reinterpret_cast<int*>(a);return 0;
}
🍀2.2.3 const_cast

       const_cast最常用的用途就是删除变量的const属性,方便做赋值操作(const_cast的类型必须是对象的指针或者引用)

int main()
{// 对应强制类型转换中有风险的去掉const属性//常变量const int b = 2;//int* p2 = (int*)&b; //上下两种方式效果等同int* p2 = const_cast<int*>(&b);*p2 = 3;const int c = b; //在这里很明显可以看到 b 的具体值// b 其实在内存中已经修改为3,比如在监视窗口下就是3,但是去读的时候已经变成常量2了cout << b << " " <<  * p2 << " " << c << endl;return 0;
}

思考:为什么*p被修改了,a却没有被修改??

       因为常量被存到寄存器中了,所以其实改变的是内存中的a,但是不是寄存器中的a。这其实是一种优化,如果我们想要去掉这种优化,用volatile关键字(告诉编译器不要优化,直接从内存中读取)

volatile关键字

     这里我们可以使用volatile 来打印我们希望的结果 3 

当要求使用 volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据, 即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存

int main()
{// 我们可以使用volatile 来打印我们希望的结果 3 volatile const int b = 2; //int* p2 = (int*)&b; //上下两种方式效果等同int* p2 = const_cast<int*>(&b);*p2 = 3;const int c = b; //在这里很明显可以看到 b 的具体值cout << b << " " <<  * p2 << " " << c << endl;return 0;
}

🌿2.2.4  dynamic_cast

dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换)
向上转型:子类对象指针/引用->父类指针/引用(不需要转换,赋值兼容规则)
向下转型:父类对象指针/引用->子类指针/引用(用dynamic_cast转型是安全的)
注意:

  1.  dynamic_cast只能用于父类含有虚函数的类
  2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回nullptr。

举个例子🌰:

假如存在下面这样一个类和main函数:

class A
{
public:virtual void f() {}int _a = 1;
};
class B : public A
{
public:int _b = 2;
};void fun(* pa){}int main()
{A a;B b;fun(&a);fun(&b);return 0;
}

假如fun函数实现如下:

void fun(A* pa)
{// 指向父类转换时有风险的,后续访问存在越界访问的风险// 指向子类转换时安全B* pb1 = (B*)pa;cout << "pb1: " << pb1 << endl;cout << "pb1->_a: " << pb1->_a << endl;cout << "pb1->_b: " << pb1->_b << endl;pb1->_a++;pb1->_b++;cout << "pb1->_a: " << pb1->_a << endl;cout << "pb1->_b: " << pb1->_b << endl
}

因此我们就需要用到 dynamic_cast 来解决子类转换为 父类的问题

void fun(A* pa)
{// dynamic_cast会先检查是否能转换成功(指向子类对象),能成功则转换,// (指向父类对象)不能则返回NULLB* pb1 = dynamic_cast<B*>(pa);if (pb1) //先判断是否为空{cout << "pb1:" << pb1 << endl;cout << "pb1->_a: " << pb1->_a << endl;cout << "pb1->_b: " << pb1->_b << endl;pb1->_a++;pb1->_b++;cout << "pb1->_a: " << pb1->_a << endl;cout << "pb1->_b: " << pb1->_b << endl;}else{cout << "转换失败" << endl;}B* pb2 = static_cast<B*>(pa);B* pb3 = dynamic_cast<B*>(pa);cout << "pb2: " << pb2 << endl;cout << "pb3: " << pb3 << endl;}

课外知识点:

1、为什么父类和子类明明是两种类型,但是向上转型不需要转换呢??

    因为子类会直接充当子类中父类那一部分的别名,不存在类型转换

2、为什么向下转型是不安全的??

      因为子类的指针或引用如果是父类对象,那么会存在一部分的越界!!!

3、为什么 dynamic_cast只能用于父类含有虚函数的类

        dynamic_cast转换是在运行时进行转换,因为只有对于这种类层次结构,才应该将派生类的地址赋给基类指针。运行时转换就需要知道类对象的信息(继承关系等)。C++对象模型中,对象实例最前面的就是虚函数表指针,通过这个指针可以获取到该类对象的所有虚函数,包括父类的。因为派生类会继承基类的虚函数表,所以通过这个虚函数表,我们就可以知道该类对象的父类,在转换的时候就可以用来判断对象有无继承关系。

  所以虚函数对于正确的基类指针转换为子类指针是非常重要的。

4、dynamic_cast的使用原理

       父类指针或引用如果本来指向的是子类的对象,那么类型转化是安全的,如果原本指向的是父类的对象,那么转化是不安全的(有越界,但是编译器检查不出来),所以dynamic_cast可以帮助我们去判断这种情况,如果不符合安全转化的条件,就会返回nullptr。

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

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

  1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失
  2. 显式类型转换将所有情况混合在一起,代码不够清晰
  3. 为了提供更安全、更明确的类型转换,使得代码意图更为清晰

       因此C++提出了自己的类型转化风格,注意因为C++要兼容C语言,所以C++中还可以使用C语言的转化风格。因此C++的转化风格并非强制性的,只不过是程序员之间的一种规范。

2.4 RTTI

RTTI:Run-time Type identification的简称,即:运行时类型识别。RTTI只适用于包含虚函数的类。
C++通过以下方式来支持RTTI:

  1. typeid运算符(返回指出对象类型的值)
  2. dynamic_cast运算符(根据虚基表判断有无继承关系,并判断是否可以转化)
  3. decltype(推断表达式返回值的类型)

2.5 扩展

 2.5.1 内置类型和自定义类型之间

由于自定义类型无法像内置类型那样转换,因此我们要实现实现一个 operator int() 的函数来进行转换,详情请看下面代码及注解(注:explicit 可禁止隐式类型转换)

/* ———————————— b. 内置类型和自定义类型之间——————————————————*/
//1、自定义类型 = 内置类型  -> 构造函数支持 (单参数和多参数的隐式类型转换时通过构造函数来支持的)
// 2、内置类型 = 自定义类型 (自定义类型,转化为内置类型)class A {
public://explicit A(int a) //禁止隐式类型转换A(int a):_a1(a), _a2(a){}A(int a1,int a2):_a1(a1), _a2(a2){}//int operator()() 中()被仿函数占用了,不能用// operator 类型实现,无返回类型// 自定义类型转换为内置类型//explicit operator int()operator int(){return _a1 + _a2;}private:int _a1 = 1;int _a2 = 1;
};int main()
{//单参数的隐式类型转换//在其对应的构造函数那加了explicit关键字,就不支持隐式类型转换//A aa1 = 1; //单参数类型转换为自定义类型,靠构造函数转的A aa1 = A(1);//但是还是支持转换//多参数的隐式类型转换A aa2 = { 2,2 };const A& aa3 = { 2,2 };//int z = aa1.operator int();//本质是这个int x = aa1; //隐式类型转换int y = (int)aa2; //显式类型转换cout << x << " " << y << endl;//智能指针的条件逻辑判断std::shared_ptr<int> foo;std::shared_ptr<int> bar(new int(34));//if (foo.operator bool())if (foo)std::cout << "foo points to " << *foo << '\n';else std::cout << "foo is null\n";if (bar)std::cout << "bar points to " << *bar << '\n';elsestd::cout << "bar is null\n";return 0;
}
2.5.2 自定义类型与自定义类型之间
/* ———————————— c. 自定义类型和自定义类型之间——————————————————*/
// c、自定义类型和自定义类型之间 -- 需要书写对应的构造函数支持class A {
public:A(int a):_a1(a), _a2(a){}A(int a1, int a2):_a1(a1), _a2(a2){}int get()const{return _a1 + _a2;}private:int _a1 = 1;int _a2 = 1;
};class B
{
public:B(int b):_b1(b){}B(const A&aa):_b1(aa.get()){}private:int _b1 = 1;
};#include "List.h"
int main()
{A aa1(1);B bb1(1);//aa1 = bb1;bb1 = aa1;B& ref1 = bb1;//B& ref2 = aa1;const B& ref2 = aa1;qian::list<int> l1 = { 1,2,3,4 };//权限的缩小? 权限的缩小和放大,仅限于const的指针和引用// 不是权限缩小,这里是类型转换qian::list<int>::const_iterator cit = l1.begin();while (cit != l1.end()){cout << *cit << " ";++cit;}cout << endl;return 0;
}

注:

🔥我们是在该文章 C++/STL】list容器的深度剖析及底层实现-CSDN博客 的完整代码作为List.h文件下,加了这一段话到其头文件中

//支持const迭代器与普通迭代器的转换
ListIterator(const ListIterator<T, T&, T*>& it):_node(it._node)
{}

2.6 建议

  🔥 强制类型转换关闭或挂起了正常的类型检查,每次使用强制类型转换前,程序员应该仔细考虑是否还有其他不同的方法达到同一目的,如果非强制类型转换不可,则应限制强制转换值的作用域,以减少发生错误的机会。强烈建议:避免使用强制类型转换

📖后言

以上就是特殊类设计及类型转换的全部类型啦

💞 💞 💞那么本篇到此就结束,希望我的这篇博客可以给你提供有益的参考和启示,感谢大家支持!!!祝大家天天开心

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

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

相关文章

Java二十三种设计模式-策略模式(13/23)

策略模式&#xff1a;灵活算法的替换与扩展 引言 策略模式&#xff08;Strategy Pattern&#xff09;是一种行为型设计模式&#xff0c;它定义了算法族&#xff0c;分别封装起来&#xff0c;让它们之间可以互相替换&#xff0c;此模式让算法的变化独立于使用算法的客户。 基础…

C#小结:如何在VS2022中使用菜单栏中的Git管理代码

目录 第一部分&#xff1a;基础操作 第一步&#xff0c;登录官网&#xff0c;设置好邮箱&#xff0c;然后右上角新建仓库 第二步&#xff0c;提交代码到远程仓库中 第三步&#xff0c;查看和比对自己修改的内容 第四步&#xff0c;查看该项目所有提交历史记录 第五步&…

嵌入式人工智能(OpenCV-基于树莓派的人脸识别与入侵检测)

1、人脸识别 人脸识别是一种技术&#xff0c;通过检测、跟踪和识别人脸上的关键特征&#xff0c;以确认人脸的身份。它通常用于安保系统、身份验证、社交媒体和人机交互等领域。 人脸识别技术的基本原理是先通过图像处理和计算机视觉算法&#xff0c;提取人脸的特征点和特征描…

【ML】Pre-trained Language Models及其各种微调模型的实现细节和特点

Pre-trained Language Models及其各种微调模型的实现细节和特点 1. Pre-trained Language Models2. semi-supervised Learning3. zero-shot4. Parameter-Efficient Fine-Tuning4.1 含义&#xff1a;4.2 实现方式&#xff1a; 5. LoRA5.1 LoRA 的主要特点&#xff1a;5.2 LoRA 的…

Pytorch人体姿态骨架生成图像

ControlNet是一个稳定扩散模型&#xff0c;可以复制构图和人体姿势。ControlNet解决了生成想要的确切姿势困难的问题。 Human Pose使用OpenPose检测关键点&#xff0c;如头部、肩膀、手的位置等。它适用于复制人类姿势&#xff0c;但不适用于其他细节&#xff0c;如服装、发型和…

Linux中apache服务安装与mysql安装

目录 一、apache安装 二、MySQL安装 一、apache安装 准备环境&#xff1a;一台虚拟机、三个安装包&#xff08;apr-1.6.2.tar.gz、apr-util-1.6.0.tar.gz、httpd-2.4.29.tar.bz2) 安装过程&#xff1a; tar xf apr-1.6.2.tar.gz tar xf apr-util-1.6.0.tar.gz tar xf http…

Burp Suite的使用和文件上传漏洞靶场试验

第一步&#xff1a;分析如何利用漏洞&#xff0c;通过对代码的查阅发现&#xff0c;代码的逻辑是先上传后删除&#xff0c;意味着&#xff0c;我可以利用webshell.php文件在上传到删除之间的间隙&#xff0c;执行webshell.php的代码&#xff0c;给上级目录创建一个shell.php木马…

IDEA右键新建时没有Java Class选项

项目场景&#xff1a; IDEA右键新建时没有Java Class选项 问题描述 IDEA右键新建时没有Java Class选项 原因分析&#xff1a; 提示&#xff1a;这里填写问题的分析&#xff1a; 例如&#xff1a;Handler 发送消息有两种方式&#xff0c;分别是 Handler.obtainMessage()和 Ha…

【扒代码】ope.py

文件目录&#xff1a; 引用方式 if not self.zero_shot: # 非零样本情况下&#xff0c;计算边界框的宽度和高度 box_hw torch.zeros(bboxes.size(0), bboxes.size(1), 2).to(bboxes.device) box_hw[:, :, 0] bboxes[:, :, 2] - bboxes[:, :, 0] # 宽度 box_hw[:, :, 1] bbox…

Docker in 100 Seconds

Docker a tool that can package software into containers that run reliably in any environment, but what is a container and why do you need one? Let’s imagine you built up an app with cobalt that runs some weird flavor of Linux. You want to share this app…

idea中好用的插件

输入法自动切换插件 自动切换输入法插件&#xff1a;Smart Input。编写代码时自动切换到英文输入法&#xff0c;注释代码自动切换为中文输入法。极大的提升我们的编码效率。 MyBatisX插件 MybatisX 是一款基于 IDEA 的快速开发插件&#xff0c;为效率而生。主要用于XML映射配…

吴恩达机器学习COURSE2 WEEK2

COURSE2 WEEK2 模型训练的细节 定义模型&#xff0c;即指定如何在给定输入特征 x x x 以及参数 w w w 和 b b b 的情况下计算输出 指定损失函数 L ( f w ⃗ , b ( x ⃗ ) , y ) L(f_{\vec w, b}(\vec x),y) L(fw ,b​(x ),y) 指定成本函数 J ( w ⃗ , b ) 1 m ∑ i 1 …

Linux系统驱动(十三)Linux内核定时器

文章目录 一、内核定时器原理二、定时器API三、使用定时器让LED灯闪烁四、使用定时器对按键进行消抖 一、内核定时器原理 内核当前时间通过jiffies获取&#xff0c;它是内核时钟节拍数&#xff0c;在linux内核启动的时候&#xff0c;jiffies开始&#xff08;按照一定频率&…

【数据结构】顺序结构实现:特殊完全二叉树(堆)+堆排序

二叉树 一.二叉树的顺序结构二.堆的概念及结构三.堆的实现1.堆的结构2.堆的初始化、销毁、打印、判空3.堆中的值交换4.堆顶元素5.堆向上调整算法&#xff1a;实现小堆的插入6.堆向下调整算法&#xff1a;实现小堆的删除7.堆的创建1.堆向上调整算法&#xff1a;建堆建堆的时间复…

CentOS 安装Redis

在 CentOS 安装 Redis 操作系统&#xff1a;centos-7.9.2009-Core 1. 更新系统 首先&#xff0c;确保你的系统是最新的&#xff1a; sudo yum update -y2. 安装 EPEL 仓库 Redis 可能不在默认的 CentOS 仓库中&#xff0c;因此你需要安装 EPEL&#xff08;Extra Packages f…

TCP详解及其在音视频传输中的应用

传输控制协议&#xff08;TCP&#xff0c;Transmission Control Protocol&#xff09;是互联网协议栈中至关重要的传输层协议。它提供了可靠、面向连接的数据传输服务&#xff0c;广泛应用于各种网络应用中。对于音视频传输&#xff0c;虽然TCP协议并不是最常用的传输协议&…

LVS实验——部署DR模式集群

目录 一、实验环境 二、配置 1、LVS 2、router 3、client 4、RS 三、配置策略 四、测试 1.Director服务器采用双IP桥接网络&#xff0c;一个是VPP&#xff0c;一个DIP 2.Web服务器采用和DIP相同的网段和Director连接 3.每个Web服务器配置VIP 4.每个web服务器可以出外网…

《Advanced RAG》-11-RAG查询分类和细化

总结 文章介绍了两种高级的检索增强生成&#xff08;RAG&#xff09;技术&#xff1a;自适应 RAG 和 RQ-RAG&#xff0c;以及它们在问题复杂性学习和查询细化方面的应用和优势&#xff0c;以及如何通过小型模型的训练来提高这些技术的性能。 摘要 传统 RAG 技术虽然能够减少大型…

「MyBatis」数据库相关操作2

&#x1f387;个人主页 &#x1f387;所属专栏&#xff1a;Spring &#x1f387;欢迎点赞收藏加关注哦&#xff01; #{} 和 ${} 我们前面都是采用 #{} 对参数进行赋值&#xff0c;实际上也可以用 ${} 客户端发送⼀条 SQL 给服务器后&#xff0c;大致流程如下&#xff1a; 1.…

51单片机之动态数码管显示

一、硬件介绍 LED数码管是一种由多个发光二极管&#xff08;LED&#xff09;封装在一起&#xff0c;形成“8”字型的显示器件。它广泛用于仪表、时钟、车站、家电等场合&#xff0c;用于显示数字、字母或符号。 通过控制点亮a b c d e f g dp来显示数字&#xff0c;本实验开发板…