当前位置: 首页 > news >正文

【C++ 第二十一章】特殊类的设计(学习思路)




在这里插入图片描述



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

设计思路

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

C++98 的做法

将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。(不定义:则拷贝操作无法实际的实现;设置成私有:避免公有被调用出来实现)

class A
{
private:A(const A&);A& operator=(const A&);
};

原因:

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



C++11 的做法

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

class A
{A(const A&) = delete;A& operator=(const A&) = delete;
};



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


方法一:构造函数私有化

  1. 构造、拷贝和赋值私有化:将类的构造函数私有,拷贝构造和赋值声明成私有。防止别人调用拷贝在栈上生成对象。或者将构造私有化,将拷贝和赋值 delete 禁用
  2. 同一提供对外接口:提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建

这个有点封装的味道了,将一些功能封装起来,自己提供对外接口,就可以控制外界可以使用的功能(控制权限)

为什么要设置成静态函数:

关于为什么对外功能接口要设置成静态函数?

思路:

1、首先,我们将构造函数私有化,拷贝构造和赋值 delete 禁用掉,外界就不可以调用这几个函数在栈上构造一个对象
2、其次,该对外功能接口函数 也是成员函数,调用一个成员函数需要一个对象来调用,但是我们这里都没有创建对象,正等着该功能函数来创建对象呢?何来一个对象?
3、这里就产生:先有鸡,还是先有蛋的问题
4、因此就需要避免使用通过对象调用的方式
可以设置成 静态成员,在外部通过类域指定调用(这时静态成员的特性,就无需通过对象调用)

class HeapOnly
{
public:// 设置成静态函数:static HeapOnly* CreateObj() {return new HeapOnly;}// 将拷贝与赋值重载都使用 delete 禁用掉HeapOnly(const HeapOnly&) = delete;HeapOnly& operator=(const HeapOnly&) = delete;
private:// 将构造函数设置成私有:避免外部调用,在栈上构造对象HeapOnly() {};
};int main() {HeapOnly* p = HeapOnly::CreateObj();  // 通过类域调用类的静态成员return 0;
}



方法二:析构函数私有化

注释都解释清楚了

// 析构函数私有化
class HeapOnly
{
public:void Destroy() {delete this;}
private:// 将析构函数设置成私有~HeapOnly() {};
};int main() {HeapOnly obj;  // 报错:创建一个类对象,销毁时会自动调用析构,但是这里调用不了(因为析构函数被"禁用"了),因此也不允许这个对象被创建出来HeapOnly* p = new HeapOnly();  // 不报错:new 出来的对象,不会自动调用析构,需要手动 deletedelete p;  //报错:调用不了析构 p->Destroy(); // 通过类中的功能接口销毁对象return 0;
}



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

这个比限制只能在堆上,还要麻烦一些些

实现第一步:构造函数私有化

class StackOnly
{
public:// 对外功能接口:该函数可以调用私有成员,构造函数,创建对象,返回匿名对象static StackOnly CreateObj() {return StackOnly();}private:// 将构造函数设置成私有:避免外部通过 new,在堆上创建对象StackOnly() {};
};int main() {StackOnly obj1 = StackOnly::CreateObj();StackOnly* p_obj2 = new StackOnly(obj1); return 0;
}

禁掉了 构造函数,还可以走拷贝构造的路:通过CreateObj() 函数,先创建一个对象出来,再 拷贝+new 生成一个新对象


因此,还要完善


实现第二步:拷贝与赋值用 delete 禁用掉

class StackOnly
{
public:// 对外功能接口:该函数可以调用私有成员,构造函数,创建对象,返回匿名对象static StackOnly CreateObj() {return StackOnly();}// 将拷贝与赋值重载都使用 delete 禁用掉StackOnly(const StackOnly&) = delete;
private:// 将构造函数设置成私有:避免外部通过 new,在堆上创建对象StackOnly() {};
};int main() {StackOnly obj1 = StackOnly::CreateObj();//StackOnly* p_obj2 = new StackOnly(obj1); return 0;
}




但是!

将 拷贝构造禁用掉:会导致 CreateObj 函数失效,因为该成员函数返回的是局部对象,需要拷贝生成临时对象,而拷贝构造失效,导致生成失败

因此,还要完善


实现第三步:operator new 用 delete 禁用掉

可以尝试从 new 的本质入手:new = 全局函数 operator new(malloc+抛异常)+ 构造

我们若自己显式实现 operator new ,则 new 优先使用我们自己的

因此可以在这里将 new 的 operator new 禁掉,使得 new 无法调用


// 只能在栈上创建对象
class StackOnly
{
public:// 该函数可以调用私有成员:构造函数,创建对象,返回匿名对象static StackOnly CreateObj() {return StackOnly();}// 可以使用/*void* operator new(size_t size) {return malloc(size*sizeof(StackOnly));}*/void* operator new(size_t size) = delete;void operator delete(void* p) = delete;
private:// 将构造函数设置成私有:避免外部通过 new,在堆上创建对象StackOnly() {};
};int main() {StackOnly obj1 = StackOnly::CreateObj();StackOnly* p_obj2 = new StackOnly(obj1);  // 报错return 0;
}




但是问题又回来了:

只要没有将 拷贝构造禁用掉,还是可以通过拷贝构造创建一个在静态区的对象

StackOnly obj1 = StackOnly::CreateObj();static StackOnly obj3(obj1); // 不报错



同时,还可以通过 移动构造 创建静态区的对象

StackOnly obj1 = StackOnly::CreateObj();static StackOnly obj4(move(obj1));

这里为什么创建静态区的对象? 仅仅是将栈区对象区别开


实现最终大法:直接使用返回对象进行操作

既然我们的目的是设计一个只能在栈上对象的类,
我们直接从这里思考,我们先将拷贝、赋值、移动构造私有化 或 delete 禁用。
既然直接将 CreateObj() 函数返回的匿名对象拷贝给新对象会触发拷贝或移动构造

StackOnly obj1 = StackOnly::CreateObj(); // 这里会触发拷贝或移动构造

干脆别拷贝给新对象,而是直接使用这个 匿名对象进行操作
(其实这个方法有点取巧,但是不也是达到了题目要求吗?😎)

int main() {StackOnly::CreateObj().Print();  // 直接使用该返回对象进行操作//StackOnly obj1 = StackOnly::CreateObj();  // 会触发拷贝或移动构造//StackOnly* p_obj2 = new StackOnly(obj1);  // 报错:operator new 和 拷贝构造 不能用了//StackOnly obj3(obj1); // 报错:拷贝构造 不能用了//static StackOnly obj4(move(*obj1)); // 报错:拷贝构造 和 移动构造 不能用了//static StackOnly obj2(obj1);return 0;
}



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

C++98 方式

C++98 中构造函数私有化,派生类中调不到基类的构造函数,则无法继承

class A
{
private:A() {};
};



C++11 方式

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

class A final
{
private:A() {};
};



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

5.1 设计模式:

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

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

我们之前其实已经接触过一些设计模式了,比如迭代器模式、适配器/配接器模式
下面我们要学习的是设计模式中的 单例模式

5.2 单例模式:

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

单例模式有两种实现模式:饿汉模式 和 懒汉模式

5.3 饿汉模式

就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象。(即程序一开始就实例化一个该类对象给你了)

为什么叫做饿汉?:饿汉就好比你放学饿着肚子回家,在你回家前妈妈就已经准备好饭菜给你了

设计思路:
1、构造函数私有化
2、将 拷贝构造、移动构造、赋值重载 封死:确保我们只有 GetInstance() 可以放出去唯一一个实例化对象。
3、创建一个自己这个类的静态成员对象:一个类的静态成员只能创建一个,而且static数据会在程序启动时创建好(刚好符合 饿汉模式 的理念)

// 单例模式:一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
class InfoMgr
{
public:// 只有这个函数可以向外提供唯一一个实例化对象static InfoMgr& GetInstance() {return _ins;}void Print() {cout << _ip << '\n';cout << _port << '\n';cout << _buffSize << '\n';}// 将 拷贝构造、移动构造、赋值重载 封死:确保我们只有 GetInstance() 可以放出去唯一一个实例化对象InfoMgr(const InfoMgr&) = delete;InfoMgr(InfoMgr&&) = delete;InfoMgr& operator=(const InfoMgr&) = delete;
private:// 将构造函数私有化:外部无法直接构造该类对象InfoMgr() {cout << "InfoMgr()" << '\n';}private:string _ip = "127.0.0.1";int _port = 80;size_t _buffSize = 1024 * 1024;// 这不能说是在类中创建一个自己,否则就套娃乱透了// 静态成员不存储在一个类对象里面static InfoMgr _ins;
};
InfoMgr InfoMgr::_ins;int main() {// 调试程序可以发现:调试还没有开始走就已经打印 "InfoMgr()" ,说明 在main函数程序执行前,对象就已经构造好了(这是因为该对象是 static,全局域)InfoMgr::GetInstance().Print();return 0;
}

饿汉模式的缺陷

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

由于饿汉模式的对象在 main 函数前就被创建,所以它不存在线程安全问题,但是它也存在一些缺点:

1、多个饿汉模式的单例,某个对象初始化内容较多(读文件),会导致程序启动慢


2、A 和 B 两个饿汉,对象初始化存在依赖关系,要求A先初始化,B再初始化,饿汉无法保证其初始化顺序


5.4 懒汉模式

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


饿汉模式的特点就是先创建好对象,这也容易引发一些问题

懒汉模式 可以解决这个问题:不先创建对象,而是需要时再创建对象

这样就可以按需创建,即你要吃的东西,我不提前给你准备好,只会在你需要吃时再做,这就是懒汉

懒汉模式写法一:定义类对象指针

在 main 函数中,程序一般都会按顺序执行(不像懒汉模式中全局变量执行顺序不定),而且按需调用即可,这样也可以解决 懒汉模式中的 依赖关系的先后问题

// 懒汉模式
class InfoMgr
{
public:// 若对象指针为 nullptr,就给你 new 一个对象// 若不为空,就返回该对象给你static InfoMgr& GetInstance() {if (_pIns == nullptr) {_pIns = new InfoMgr();}return *_pIns;}void Print() {cout << _ip << '\n';cout << _port << '\n';cout << _buffSize << '\n';}// 将 拷贝构造、移动构造、赋值重载 封死:确保我们只有 GetInstance() 可以放出去唯一一个实例化对象InfoMgr(const InfoMgr&) = delete;InfoMgr(InfoMgr&&) = delete;InfoMgr& operator=(const InfoMgr&) = delete;
private:// 将构造函数私有化:外部无法直接构造该类对象InfoMgr() {cout << "InfoMgr()" << '\n';}private:string _ip = "127.0.0.1";int _port = 80;size_t _buffSize = 1024 * 1024;// 这不能说是在类中创建一个自己,否则就套娃乱透了// 静态成员不存储在一个类对象里面static InfoMgr* _pIns;
};
InfoMgr* InfoMgr::_pIns;int main() {// 调试程序可以发现:类里面的那个对象是在 GetInstance() 函数调用时创建的InfoMgr::GetInstance().Print();return 0;
}




有没有发现 懒汉模式存在一个问题:该模式中的对象是 new 出来的,就需要手动 delete 释放

而我们上面的类中,默认的析构只会将 _pIns 这个指针置空,而不会 delete 指向的资源,相当于 ”浅析构“,会造成内存泄漏


实际上,只有单例对象内存泄漏问题并没有这么严重

如果想要delete,这里有个很好的方法:定义内部类对象,当本项目程序结束后,该对象销毁会调用自己的析构函数,我们就可以在析构函数里面设置 delete 相关程序

这其实是一种解决问题的 思想:自己类无法做到的事,可以定义内部类,利用类的特性间接完成一些功能


// 定义一个内部类:用于析构单例对象
class DestroyIns
{public:~DestroyIns() {if (InfoMgr::_pIns != nullptr) {delete InfoMgr::_pIns;cout << "delete InfoMgr::_pIns;" << '\n';}}
};InfoMgr::DestroyIns desIns;  // 全局对象:程序结束后会销毁,自动调用析构函数,则会执行析构函数里面 delete 的程序

应用进去

// 懒汉模式
class InfoMgr
{
public:// 若对象指针为 nullptr,就给你 new 一个对象// 若不为空,就返回该对象给你static InfoMgr& GetInstance() {if (_pIns == nullptr) {_pIns = new InfoMgr();}return *_pIns;}void Print() {cout << _ip << '\n';cout << _port << '\n';cout << _buffSize << '\n';}// 将 拷贝构造、移动构造、赋值重载 封死:确保我们只有 GetInstance() 可以放出去唯一一个实例化对象InfoMgr(const InfoMgr&) = delete;InfoMgr(InfoMgr&&) = delete;InfoMgr& operator=(const InfoMgr&) = delete;// 定义一个内部类:用于析构单例对象class DestroyIns{public:~DestroyIns() {if (InfoMgr::_pIns != nullptr) {delete InfoMgr::_pIns;cout << "delete InfoMgr::_pIns;" << '\n';}}};private:// 将构造函数私有化:外部无法直接构造该类对象InfoMgr() {cout << "InfoMgr()" << '\n';}private:string _ip = "127.0.0.1";int _port = 80;size_t _buffSize = 1024 * 1024;// 这不能说是在类中创建一个自己,否则就套娃乱透了// 静态成员不存储在一个类对象里面static InfoMgr* _pIns;
};
InfoMgr* InfoMgr::_pIns;
InfoMgr::DestroyIns desIns;int main() {// 调试程序可以发现:类里面的那个对象是在 GetInstance() 函数调用时创建的InfoMgr::GetInstance().Print();return 0;
}



懒汉模式二:利用 局部静态变量(推荐写这个)


局部静态变量

函数内的静态变量也称为局部静态变量,其作用域只限于函数内部,别的函数不能访问。

局部静态变量存储在全局数据区,只允许初始化一次,但它的生命周期和全局变量一样,自它们被定义时就一直存在,直到程序结束时才会被销毁。不会随着函数的结束而被销毁,会一直存在


特性:只允许初始化一次

作用域:在函数内部

存储区:全局静态区

生命周期:全局,不会随着函数的结束而被销毁,程序结束时才会被销毁



由于局部静态变量的特性,也可以达到 第一次调用 GetInstance() 函数,就定义一个类对象,其他时候调用不会重新定义,只允许定义一次 的目的

同时,程序结束时会该对象也会自动销毁,不用再定义内部类对齐处理了!!



这个写法简单明了,相比前一种写法更加巧妙

// 懒汉模式二:利用 局部静态变量
class InfoMgr
{
public:// 若对象指针为 nullptr,就给你 new 一个对象// 若不为空,就返回该对象给你static InfoMgr& GetInstance() {static InfoMgr pIns;return pIns;}void Print() {cout << _ip << '\n';cout << _port << '\n';cout << _buffSize << '\n';}// 将 拷贝构造、移动构造、赋值重载 封死:确保我们只有 GetInstance() 可以放出去唯一一个实例化对象InfoMgr(const InfoMgr&) = delete;InfoMgr(InfoMgr&&) = delete;InfoMgr& operator=(const InfoMgr&) = delete;private:// 将构造函数私有化:外部无法直接构造该类对象InfoMgr() {cout << "InfoMgr()" << '\n';}private:string _ip = "127.0.0.1";int _port = 80;size_t _buffSize = 1024 * 1024;
};int main() {// 调试程序可以发现:类里面的那个对象是在 GetInstance() 函数调用时创建的InfoMgr::GetInstance().Print();return 0;
}

相关文章:

【C++ 第二十一章】特殊类的设计(学习思路)

1.请设计一个类&#xff0c;不能被拷贝 设计思路 拷贝只会使用在两个场景中&#xff1a;拷贝构造函数以及赋值运算符重载&#xff0c;因此想要让一个类禁止拷贝&#xff0c;只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。 C98 的做法 将拷贝构造函数与赋值运算符…...

Java设计模式【命令模式】-行为型

1. 介绍 命令模式&#xff08;Command Pattern&#xff09; 是一种行为型设计模式&#xff0c;它将一个请求封装为一个对象&#xff0c;从而使我们可以用不同的请求对客户端进行参数化&#xff0c;并且支持请求的排队、记录日志以及撤销、重做等功能。命令模式将请求的发送者与…...

【HarmonyOS】一键扫码功能

【HarmonyOS】一键扫码功能 前言 鸿蒙在api10之后&#xff0c;对系统api的基础上&#xff0c;封装了较为复杂功能的开发工具包&#xff0c;统一称之为Kit。这些Kit根据功能定义的不同&#xff0c;划分为不同的种类Kit。如下图所示&#xff1a; 其实可以理解为集成在系统中的…...

Spring Boot应用中集成与使用多数据源

Spring Boot应用中集成与使用多数据源 1. 前言 通过定义和使用多个数据源&#xff0c;能在Spring Boot应用中实现更复杂的数据管理场景&#xff0c;比如读写分离、数据冗余等。 2. 准备工作 环境准备&#xff1a;确保已经准备好Spring Boot的开发环境。数据库准备&#xff…...

探索 JavaScript 中的 instanceof 关键字

在 JavaScript 这门灵活而强大的编程语言中&#xff0c;instanceof 是一个非常重要的操作符&#xff0c;它用于检测一个对象是否在其原型链的原型构造函数的 prototype 属性中出现。简而言之&#xff0c;instanceof 用于测试一个对象是否是其父类或者其原型链上某个构造函数的实…...

Python爬虫02

xml 和html 区别 jsonpath模块 场景 多层嵌套的复杂字典直接提取数据 安装 pip install jsonpath使用 from jsonpath import jsonpathret jsonpath(dict, jaonpath语法规则字符串)语法规则 eg: lxml模块&xpath语法 谷歌浏览器 xpath helper 插件 作用对当前页面…...

HTTP/3

http相关知识点 HTTP/3是超文本传输协议&#xff08;HTTP&#xff09;的最新版本&#xff0c;旨在进一步提高Web性能和安全性。HTTP/3的显著变化是它基于QUIC&#xff08;Quick UDP Internet Connections&#xff09;协议&#xff0c;而不是之前版本中使用的TCP协议。QUIC是由…...

MySQL 字符串操作详解和案例示范

MySQL 字符串操作详解 MySQL 提供了丰富的字符串操作函数&#xff0c;能够对这些字符串进行截取、定位、替换等操作。本文将详细讲解 MySQL 中的字符串操作函数&#xff0c;包括 SUBSTRING()、SUBSTR()、LEFT()、RIGHT()、LOCATE()、POSITION()、FIND_IN_SET()、ELT()、INSERT…...

全双工语音交互

文章目录 微软小冰全双工字节大模型语音交互[Language Model Can Listen While Speaking](https://arxiv.org/html/2408.02622v1) 微软小冰全双工 全双工的定义&#xff1a;一路持续的听&#xff0c;upload audio&#xff1b;一路持续的输出&#xff0c;download audio&#xf…...

nginx中如何设置gzip

前言 Nginx通过配置gzip压缩可以提升网站整体速度 Nginx的gzip功能是用于压缩HTTP响应内容的功能。当启用gzip时&#xff0c;在发送给客户端之前&#xff0c;Nginx会将响应内容压缩以减小其大小。这样可以减少数据传输的带宽消耗和响应时间&#xff0c;提高网站的性能和速度。…...

借老系统重构机会我写了个groovy规则引擎

公司老系统的重构计划早就有了&#xff0c;为了对Java硬编码的各种校验规则进行重构&#xff0c;特地参考了相关技术&#xff0c;最终选择了groovy进行了系统的学习&#xff0c;并编写了一个即插即用的轻量级规则引擎。 文章目录 项目背景技术选型groovy的性能groovy脚本执行线…...

C#利用ffmpeg借助NVIDIA GPU实现实时RTSP硬解码+硬编码录制MP4

目录 说明 效果 项目 代码 下载 说明 利用周杰的开源项目 Sdcb.FFmpeg 项目地址&#xff1a;https://github.com/sdcb/Sdcb.FFmpeg/ 代码实现参考&#xff1a;https://github.com/sdcb/ffmpeg-muxing-video-demo 效果 C#利用ffmpeg借助NVIDIA GPU实现实时RTSP硬解码硬…...

第4章 汇编语言和汇编软件

第4章 汇编语言和汇编软件 该章主要介绍了汇编语言和汇编语言编译器的安装和使用。 汇编语言程序 该小节主要介绍了为什么要有汇编语言和汇编语言程序的一些基础写法。 书中有提到CPU有不同的架构&#xff0c;汇编语言有不同的风格&#xff0c;那么不同的CPU架构和不同的汇…...

网络安全在2024好入行吗?

前言 024年的今天&#xff0c;慎重进入网安行业吧&#xff0c;目前来说信息安全方向的就业对于学历的容忍度比软件开发要大得多&#xff0c;还有很多高中被挖过来的大佬。 理由很简单&#xff0c;目前来说&#xff0c;信息安全的圈子人少&#xff0c;985、211院校很多都才建立…...

C++练习

要求 1. 函数命名清晰 使用描述性的命名&#xff0c;准确反映函数的功能。例如&#xff0c;使用 CalculateSum() 而不是 sum()。避免使用缩写或模糊不清的名字&#xff0c;确保变量和函数名有明确的含义。 2. 参数传递 根据需要选择按值传递、按引用传递或按指针传递。如果…...

3. GIS后端工程师岗位职责、技术要求和常见面试题

本系列文章目录&#xff1a; 1. GIS开发工程师岗位职责、技术要求和常见面试题 2. GIS数据工程师岗位职责、技术要求和常见面试题 3. GIS后端工程师岗位职责、技术要求和常见面试题 4. GIS前端工程师岗位职责、技术要求和常见面试题 5. GIS工程师岗位职责、技术要求和常见面试…...

Linux学习笔记(4)----Debian压力测试方法

使用命令行终端压力测试需要两个实用工具&#xff1a;s-tui和stress sudo apt install s-tui stress 安装完成后&#xff0c;在终端中启动 s-tui实用工具&#xff1a; s-tui 执行后如下图&#xff1a; 你可以使用鼠标或键盘箭头键浏览菜单&#xff0c;然后点击“压力选项(Str…...

xml详解

一、XML是什么 XML&#xff08;可扩展标记语言&#xff09;是一种非常常用的数据存储和交换格式。 二、XML 的基本结构 声明 XML 文件通常以 XML 声明开始&#xff0c;例如&#xff1a;<?xml version"1.0" encoding"UTF-8"?>。它指定了 XML 的版…...

C140 杨辉三角

C140 杨辉三角 题目题解(94)讨论(102)排行面经 new 简单 通过率&#xff1a;29.57% 时间限制&#xff1a;1秒 空间限制&#xff1a;256M 知识点C工程师牛客 校招时部分企业笔试将禁止编程题跳出页面&#xff0c;为提前适应&#xff0c;练习时请使用在线自测&#xff0c;…...

C++字符串操作中的陷阱

休对故人思故国&#xff0c;且将新火试新茶。诗酒趁年华。 ——《望江南超然台作》【宋】苏轼 目录 正文&#xff1a; 首先我们要明白出现问题的原因: 1. 缓冲区溢出 2. 错误的字符串声明方式 3. 缺乏对NULL指针的检查 解决方案&#xff1a; 下期预告&#xff1a;C字符串…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

conda相比python好处

Conda 作为 Python 的环境和包管理工具&#xff0c;相比原生 Python 生态&#xff08;如 pip 虚拟环境&#xff09;有许多独特优势&#xff0c;尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处&#xff1a; 一、一站式环境管理&#xff1a…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多&#xff0c;如何一步解决&#xff0c;采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集&#xff08;每个目录代表一个类别&#xff0c;目录下是该类别的所有图片&#xff09;&#xff0c;你需要进行以下配置步骤&#x…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...