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

景德镇网站建设公司商城外贸网站设计

景德镇网站建设公司,商城外贸网站设计,网站流水怎么做,pedl中文模组网站1. 不可拷贝类 我们知道#xff0c;某些资源只能有一个对象持有#xff0c;拷贝可能导致资源混乱。例如智能指针std::unique_ptr独占管理动态分配对象#xff0c;文件句柄、网络套接字、数据库连接等资源通常也是独占的#xff0c;不允许拷贝。 在C11之前#xff0c;要创…1. 不可拷贝类 我们知道某些资源只能有一个对象持有拷贝可能导致资源混乱。例如智能指针std::unique_ptr独占管理动态分配对象文件句柄、网络套接字、数据库连接等资源通常也是独占的不允许拷贝。 在C11之前要创建一个不可拷贝的类通常的做法是将拷贝构造函数和赋值运算符重载声明为private并且只进行声明而不提供定义。例如下面这个NonCopyable类 class NonCopyable { public:NonCopyable() {} private:// C11之前只声明不实现NonCopyable (const NonCopyable){}NonCopyable operator(const NonCopyable){} };当代码中某个地方尝试对该类的对象进行拷贝构造时由于拷贝构造函数和赋值运算符重载是私有的在类外部无法访问这些私有成员函数所以从访问权限层面就阻止了拷贝行为。而只声明不实现的原因在于如果只是声明为私有但不小心提供了定义那么在类内部或者友元函数中还是有可能进行拷贝操作的从而彻底禁止拷贝行为。 C11引入了delete关键字利用这个关键字可以更清晰、简洁地实现不可拷贝的类。像下面这样的NonCopyable类定义 class NonCopyable { public:NonCopyable() default;// 使用delete关键字禁用拷贝构造函数和赋值运算符NonCopyable(const NonCopyable) delete;NonCopyable operator(const NonCopyable) delete;void doSomething() {//...} };通过将拷贝构造函数和赋值运算符重载直接标记为 delete明确告知编译器要禁用这两个函数。当代码中出现对该类对象进行拷贝构造或者赋值的操作时编译器会直接报错指出对应的函数已被删除不允许调用从而实现禁止拷贝的功能。 我们还可以定义一个通用的不可拷贝的基类如nocopy类然后让具体需要禁止拷贝的类去继承这个基类示例代码如下 class nocopy { public:nocopy() {}nocopy(const nocopy) delete;const nocopy operator(const nocopy) delete; };// 继承 class NonCopyable:public nocopy { public:NonCopyable() {}; private: };当派生类如NonCopyable类的对象进行拷贝构造或者赋值操作时在派生类的拷贝构造函数和赋值运算符重载的实现过程中即使没有显式定义编译器也会默认生成相应的函数尝试调用基类的对应函数来处理基类部分的拷贝和赋值逻辑会先在初始化列表中去调用基类的拷贝构造函数和赋值运算符重载。但由于基类nocopy类已经通过delete关键字或者类似在C11之前将其设为私有等方式禁止了这些操作所以派生类也就无法顺利完成拷贝构造和赋值操作从而实现了不可拷贝的效果。 2. 只能在堆上创建的类 对于一些对象大小在编译时未知或生命周期不确定时动态内存分配更合适。并且多个模块或对象需共享同一实例堆上对象可通过指针或引用共享。例如大型数据结构大数组、链表、树等、资源管理器文件处理类、数据库连接类等、单例模式确保对象唯一性和生命周期管理。所以我们有时需要设计一个只能在堆上创建的类一般我们有两种方式实现 以下是关于只能在堆上创建的类的详细介绍包含其不同实现方式、原理、优缺点以及使用时的注意事项等内容 首先第一种方式我们可以通过私有构造函数结合静态创建方法实现 class HeapOnly { public:// 静态成员函数用于在堆上创建对象并返回指针static HeapOnly* create() {return new HeapOnly();} private:// 构造函数私有化HeapOnly() {}// 拷贝构造函数私有化禁止拷贝构造HeapOnly(const HeapOnly) delete;// 拷贝赋值运算符私有化禁止赋值操作HeapOnly operator(const HeapOnly) delete; };由于构造函数是私有的像HeapOnly obj;这样直接在栈上创建对象的语句就无法通过编译因为编译器会检查到在类外部没有权限调用构造函数。而对于拷贝构造函数和拷贝赋值运算符也进行了私有化或者删除的处理避免了通过拷贝操作来间接创建对象的可能性保证了对象创建的唯一性和可控性只能按照规定的静态create函数在堆上创建。 第二种方式我们可以通过私有化析构函数实现 实现方式 class HeapOnly { public:HeapOnly() default;void Destroy(){delete this;} private:// 析构函数私有化~HeapOnly() {}// 拷贝构造函数私有化禁止拷贝构造HeapOnly(const HeapOnly) delete;// 拷贝赋值运算符私有化禁止赋值操作HeapOnly operator(const HeapOnly) delete; };对于栈上对象编译器会自动在对象生命周期结束比如离开作用域时调用析构函数进行资源清理等操作但因为析构函数是私有的在类外部代码中没有访问权限去执行这个操作所以直接在栈上创建对象的语句就无法编译通过。而在堆上创建对象时内存分配由new操作符完成只是后续需要手动释放内存通过提供Destroy函数来解决手动释放的问题从而强制使对象只能在堆上创建并按照规定的方式进行内存释放管理。 3. 只能在栈上创建的类 栈上创建对象的生命周期与包含函数的作用域相同函数结束时自动销毁无需显式调用析构函数。并且栈上内存分配比堆上快无需额外内存管理开销。例如锁类、作用域守卫类常设计为只能在栈上创建。所以有时候我们也需要设计一个只能在栈上创建的类。 我们首先肯定想到的是将构造函数或析构函数声明为私有提供静态方法创建对象并返回。 //错误示例 class StackOnly { public:static StackOnly Create(){static StackOnly obj;return obj;} private:// 构造函数私有化StackOnly() {} }; int main() {//StackOnly* obj new StackOnly(); errorStackOnly obj StackOnly::Create(); //间接在堆上创建对象StackOnly* n new StackOnly(obj); //okreturn 0; }但此方法不能完全防止使用new创建对象因为静态工厂方法返回对象时可能会用到拷贝构造函数编译器可能优化所以不能禁用/私有拷贝构造函数。而一旦不能禁止拷贝构造我们可以使用类似 StackOnly* obj new(栈上创建的对象)调用拷贝构造间接创建一个堆上的对象所以这种方式其实是不可取的。 所以我们可以换种思路 既然要禁止在堆上开辟空间来创建类对象我们可以采取直接禁止使用new和delete操作符的思路。因为在C中new与delete默认会调用全局的operator new与operator delete函数来进行内存分配和释放操作。所以我们只需在类内对operator new进行重载并通过显式删除的方式将其禁用同时也把operator delete删除掉如此一来就能确保无法通过new和delete这两个操作符去创建该类的对象了。 class StackOnly { public:StackOnly() default;~StackOnly() default;private:// 显式删除operator new操作符禁止在堆上通过new创建对象void* operator new(std::size_t) delete;// 显式删除operator delete操作符禁止在堆上释放对象void operator delete(void*) delete;// 同样也可以删除数组形式的new和delete操作符防止通过new[]和delete[]来操作对象void* operator new[](std::size_t) delete;void operator delete[](void*) delete; };int main() {// 正确的创建方式在栈上创建对象StackOnly obj;// 以下方式会报错因为new和delete操作符被删除不能在堆上创建和释放对象// StackOnly* ptr new StackOnly();// delete ptr;// StackOnly* arr new StackOnly[5];// delete[] arr;return 0; }4. 不可被继承的类 在C98标准下可以通过将类的构造函数声明为private来阻止类被继承。因为当一个类想要继承另一个类作为子类时子类的构造函数会默认隐式或显式地调用父类的构造函数来初始化从父类继承下来的那部分成员。如果父类的构造函数是private的子类就无法访问它也就没办法完成这个初始化过程从而导致编译错误实现了禁止继承的效果。 以如下代码为例 class NonInherit { public:static NonInherit GetInstance(){return NonInherit();} private:NonInherit(){} };这里定义了NonInherit类它的构造函数是私有的唯一对外提供了一个静态成员函数GetInstance用于获取该类的实例返回一个临时的NonInherit对象。 C11引入了final关键字当在类定义时使用final关键字修饰该类就明确告知编译器这个类是不允许被继承的例如 class A final {// 类的成员定义等内容 };如果后续有其他类试图去继承这个被final修饰的类像下面这样 class B : public A {// 编译会报错因为A类不能被继承 };编译器会直接报错提示类A是final的不允许作为基类被继承这种方式更加直观、明确地表达了类不可继承的意图而且语法简洁明了。 5. 控制可创建对象的类 有时候我们也需要一个可以控制可创建对象的类 首先我们将禁止构造函数拷贝构造函数以及赋值重载。并单独提供一个静态方法来创建对象方便我们管理。 class LimitedObjectCreator { public:// 静态方法用于创建对象返回指向创建对象的指针static LimitedObjectCreator* create() {if (count 0) {--count;std::cout 对象创建成功还可创建的对象数量 count std::endl;return new LimitedObjectCreator();}else {std::cerr 已超出对象创建限制数量无法创建对象。 std::endl;return nullptr;}}// 析构函数用于释放对象资源这里可以添加具体的资源释放逻辑如果有需要~LimitedObjectCreator() {count;std::cout 对象已销毁还可创建的对象数量 count std::endl;}static void setObjectLimit(int num) {count num;}private:// 将构造函数声明为私有禁止外部直接调用构造函数创建对象LimitedObjectCreator() {}// 拷贝构造函数也声明为私有禁止拷贝构造LimitedObjectCreator(const LimitedObjectCreator) delete;// 赋值运算符重载同样声明为私有禁止赋值操作LimitedObjectCreator operator(const LimitedObjectCreator) delete;static int count; };// 初始化静态成员变量 int LimitedObjectCreator::count 0;int main() {LimitedObjectCreator::setObjectLimit(3);LimitedObjectCreator* obj1 LimitedObjectCreator::create();LimitedObjectCreator* obj2 LimitedObjectCreator::create();LimitedObjectCreator* obj3 LimitedObjectCreator::create();LimitedObjectCreator* obj4 LimitedObjectCreator::create();if (obj1 ! nullptr) {delete obj1;}if (obj2 ! nullptr) {delete obj2;}if (obj3 ! nullptr) {delete obj3;}if (obj4 ! nullptr) {delete obj4;}return 0; }我们可以通过一个静态成员变量 count来控制可创建对象的个数其可以通过专门的函数 setObjectLimit设置每次创建时可创建对象个数会减一--count释放资源时可创建对象个数会相应增一count。 6. 单例模式 单例模式是一种设计模式其核心特点是一个类只能创建一个对象该模式可以保证系统中该类仅有一个实例并提供一个访问它的全局访问点此实例能被所有程序模块共享。 例如在服务器程序中服务器的配置信息存放在文件里可由一个单例对象统一读取配置数据服务进程中的其他对象再通过这个单例对象获取配置信息这简化了复杂环境下的配置管理。 单例模式有饿汉模式和懒汉模式两种实现方式 6.1 饿汉模式 饿汉模式的核心思想是在程序启动阶段也就是 main 函数执行之前就创建单例类的唯一实例对象无论后续程序运行过程中是否会实际使用到这个实例。这种方式就好像一个人很饿提前把食物都准备好不管后面吃不吃。 优点实现起来较为简单直观代码结构清晰不需要考虑复杂的线程同步问题因为实例在一开始就创建好了后续只是获取这个已存在的实例而已。缺点可能会导致进程启动变慢尤其是当单例对象的构造过程比较复杂、耗时例如需要加载大量配置文件、初始化很多资源等在程序启动时就执行这些操作会拖慢启动速度。另外当存在多个单例类对象实例时它们的创建顺序是不确定的这在一些对实例初始化顺序有严格要求的场景下可能会带来问题。 namespace Hungry_Man {// 饿汉模式--main函数之前就创建对象class Singleton {public:static Singleton* GetInstance() {return _inst;}private:Singleton(const Singleton) delete;Singleton operator(const Singleton) delete;Singleton() {}static Singleton _inst;};Singleton Singleton::_inst; }在这个代码中Singleton 类定义了一个私有的静态成员变量 _inst它就是单例类的唯一实例。在类外进行了 Singleton::_inst 的定义这使得在程序启动阶段这个实例就会被创建出来在 main 函数执行之前就已经存在了。 GetInstance 函数很简单它只是返回这个已经创建好的实例的地址。由于构造函数被声明为私有外部无法随意创建 Singleton 类的其他对象保证了整个程序中只有这一个实例存在符合单例模式的要求。而且通过 delete 关键字删除了拷贝构造函数和赋值运算符重载函数防止了通过拷贝或赋值的方式产生额外的对象实例。 6.2 懒汉模式 懒汉模式采取的是延迟加载的策略只有在第一次真正需要使用单例对象时才去创建它就好比一个人很懒等到要吃东西了才去准备食物。这种模式适用于单例对象构造比较耗时或者占用资源较多比如加载插件、初始化网络连接、读取文件等情况并且程序运行时有可能根本不会用到该对象的场景这样可以避免在程序启动阶段就消耗不必要的资源。 优点因为是在第一次使用时才创建对象所以不会影响程序启动的速度进程启动时没有额外的负载。同时多个单例实例的启动顺序可以根据实际使用的先后情况自由控制比较灵活。缺点实现相对复杂一些需要充分考虑线程安全问题在多线程环境下如果多个线程同时尝试获取单例对象要保证只有一个线程能够创建实例避免重复创建。另外还需要考虑对象的释放问题要合理地进行内存管理确保单例对象在合适的时候被正确释放避免内存泄漏等问题。 namespace Lazy_MAN {// 懒汉模式class Singleton {public:static Singleton* GetInstance() {// 双检查加锁方式if (_pinst nullptr) { // 第一次判断是防止对象创建好以后还要每次加锁就浪费了unique_lockmutex lock(_mtx);if (_pinst nullptr) { // 第二次判断是为了防止多个线程一起写不安全现象_pinst new Singleton;}}return _pinst;}static void DelInstance() {delete _pinst;_pinst nullptr;}class GC{public:~GC(){Singleton::DelInstance();}};private:Singleton() {}Singleton(const Singleton) delete;Singleton operator(const Singleton) delete;static Singleton* _pinst;static mutex _mtx;static GC gc;// 定义一个静态成员变量程序结束时系统会自动调用它的析构函数从而释放单例对象};Singleton* Singleton::_pinst nullptr;mutex Singleton::_mtx; }值得强调的是在 GetInstance 函数中采用了双检查加锁机制 第一次 if (_pinst nullptr) 判断从性能优化角度出发如果单例对象已经被创建即 _pinst 不为 nullptr那么直接返回已存在的实例即可无需再进行加锁和后续创建实例的操作。因为加锁解锁本身是有一定开销的如果每次调用 GetInstance 都进行加锁会影响程序性能尤其是在单例对象已经创建好的情况下这种开销是不必要的。第二次 if (_pinst nullptr) 判断主要是从线程安全角度考虑在多线程环境下可能会出现多个线程同时通过了第一次 if 判断因为此时 _pinst 确实为 nullptr单例对象还未创建然后这些线程都尝试获取锁并进入到临界区由 unique_lock 保护的代码块。如果没有第二次 if 判断那么每个线程都会执行 _pinst new Singleton; 这一语句从而导致创建多个单例对象违背了单例模式的初衷。而第二次判断确保只有一个线程能够真正执行创建单例对象的操作其他线程在等待锁释放后再次检查 _pinst 时会发现单例对象已经被创建从而避免重复创建保证了在多线程环境下单例对象的唯一性。 除此之外我们还定义了一个内部类 GC其析构函数中调用了 DelInstance 函数这样在程序结束时系统会自动调用 GC 的析构函数进而释放单例对象解决了对象释放的问题避免内存泄漏。同时通过将拷贝构造函数和赋值运算符重载函数声明为 delete同样防止了外部创建多个对象实例的情况保证单例模式的正确实现。 当然我们也可以采用如下方式实现一个简单的懒汉单例模式 class Singleton { public:static Singleton GetInstance(){//C11之后局部静态变量是线程安全的static Singleton inst;return inst;} private:Singleton() default;Singleton(const Singleton) delete;Singletonoperator(const Singleton) delete; }; int main() {Singleton inst Singleton::GetInstance();return 0; }最后懒汉模式和饿汉模式的区别总结如下 懒汉模式需要考虑线程安全和释放问题实现相对复杂饿汉模式不存在这些问题实现简单。懒汉是懒加载模式在需要时初始化创建对象不影响程序启动饿汉模式在程序启动阶段就创建初始化实例对象可能导致程序启动慢影响体验。如果有多个单例类且存在依赖关系如B依赖A要求A单例先创建初始化B单例再创建初始化则不能用饿汉模式无法保证创建初始化顺序这时懒汉模式可手动控制。 而且在实际应用中懒汉模式通常更实用。
http://www.zqtcl.cn/news/479240/

相关文章:

  • 网站怎么做弹幕播放器自助免费网站制作
  • 网站咨询弹窗是怎么做的网站建设的目标客户
  • 搞好姓氏源流网站建设Wordpress 商城主题过于臃肿
  • 如何网站客户案例上海网站备案查询
  • 沈阳大熊网站建设制作北京门户网站制作公司
  • 如何制作自己的网站免费最好的建站平台
  • 自己网站做电子签章有效么网站的规划与建设案例分析
  • 945新开传奇网站深圳动画营销推广的原因
  • 网站制作加盟网站推广 知乎
  • 广西东晋建设有限公司网站电商详情页模板免费套用
  • dedecms 做影网站商丘家居网站建设
  • 七里港网站建设商城购物网站有哪些模块
  • 中英网站怎么做网站域名的作用是什么
  • 网站建设开题报告ppt模板重庆建设工程信息网址
  • 做个什么类型网站百度云资源链接分享群组
  • 商务网站建设的主流程西安建设局网站
  • 邢台营销型网站建设做网站的保证承诺
  • 关于茶叶的网站模板免费下载wordpress多边形按钮
  • 贵阳市网站建设手工制作花
  • 娄底哪里做网站免费永久不限空间
  • 网站标签怎么做wordpress 后台模板
  • 内江移动网站建设网站内部链接的作用有哪些
  • 阿里云的wordpress建站wordpress 批量定时发布
  • 网站院长信箱怎么做海报设计制作网站
  • 办网站如何备案怎么制作微信链接网页
  • 聊城门户网站建设怎样能有个人网站
  • 营销网站建设资料创意网站 模板
  • 免费的短网址生成器深圳外贸网站优化
  • 网站推广哪个平台最好图库
  • 在东莞做网站vue做网站好吗