特殊类设计

news/2024/4/28 22:57:13

目录

一个不能被拷贝的类

一个只能创建在堆上的类

一个只能创建在栈上的类

一个不能被继承的类

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

饿汉模式

懒汉模式


一个不能被拷贝的类

        通常拷贝只会发生在拷贝构造和赋值运算符重载这两个场景下,所以想要一个类禁止拷贝不要让他调用这两个函数

        C++98中要这样做就需要只声明不实现,并将其设为私有

class A
{
private:A(const A& a);A& operator=(const A& a);
}
  • 如果只声明没有设置成private用户自己如果在类外定义了,就不能禁止拷贝了。

        C++11中只需要使用delete关键字就可以,表示删除这个函数。

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

一个只能创建在堆上的类

        这种类在之前C++11的篇章也提到过,那是一种处理析构函数的方法,下面介绍一种处理构造函数的方法。

  • 将类的构造函数私有拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
  • 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建。

如果不使用static,那么这个类没有实例化,那就不能调用这个创建对象的函数。

class A
{
public:static A* CreateObj() // 控制这个类只能在堆上创建{return new A;}
private:A() {}// C++98是把拷贝构造也私有// A(const A& a) {}// C++11是使用deleteA(const A& a) = delete;
};int main()
{A* pa;pa->CreateObj();delete pa;return 0;
}

一个只能创建在栈上的类

        还是可以将构造函数私有,使用静态函数创建在栈上的对象并返回。

class A
{
public:static A CreateObj(){return A();}// 这里不可以禁用拷贝和赋值运算符,如果禁掉那么A pa = A::CreateObj();这种方式就不能实现了// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉,但是这样禁用有的方式还是可以创建在静态区,算是一个缺陷// A obj = A::CreateObj();// A* ptr3 = new A(obj);void* operator new(size_t size) = delete;void operator delete(void* p) = delete;
private:A() {}
};int main()
{A pa = A::CreateObj();return 0;
}

一个不能被继承的类

C++98中是把构造函数私有,子类调用不到父类的构造函数,就没有办法继承。

class A
{
public:static A GetInstance(){return A();}
private:A() {}
};

C++11使用final关键字表示类不能被继承

class A final
{//
}

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

        单例模式是一种设计模式,设计模式是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。它的目的就是为了代码可重用性、让代码更容易被他人理解保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石。

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

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

  • 饿汉模式

        在main函数之前就创建出对象,就像饿汉一样,一开始就“全吃了”。

class Singleton
{
public:static Singleton* GetInstance(){return _pinst;}void* Alloc(size_t n){void* ptr = nullptr;// ...return ptr;}void Dealloc(){// ...}
private:// 构造函数私有Singleton(){}char* _ptr = nullptr;// static Singleton _inst; // 声明一个在静态区的Singleton对象static Singleton* _pinst; // 这是该类的对象,可以访问类中的函数
};// 定义
// Singleton Singleton::_inst;
Singleton* Singleton::_pinst = new Singleton;int main()
{void* ptr = Singleton::GetInstance()->Alloc(2);return 0;
}

        饿汉模式是一个静态成员在main函数之前就创建出来的,所以不存在多线程,也就不会有线程安全的问题,不允许随便创建、不允许拷贝。

        饿汉模式优点就是简单。缺点一是没有办法确定几个单例类的实例顺序;二是假如一个类的启动之前在构造函数进行了很多步骤,可能会导致进程启动慢

  • 懒汉模式

        如果单例对象构造十分耗时或者占用很多资源,但是有的对象用不到,饿汉模式也要在一开始初始化,这样就会导致启动非常慢,这种就要使用懒汉模式,懒汉模式对应饿汉模式的缺点,就像懒汉一样,什么时候用什么时候实例化,什么时候有什么时候吃。它就可以控制实例顺序。懒汉模式是使用延迟加载的思想。但是它很复杂。

class Singleton
{
public:static Singleton* GetInstance(){if (_pinst == nullptr){_pinst = new Singleton;}return _pinst;}void* Alloc(size_t n){void* ptr = nullptr;// ...return ptr;}void Dealloc(){// ...}
private:// 构造函数私有Singleton(){}Singleton(Singleton const&) = delete;Singleton& operator=(Singleton const&) = delete;char* _ptr = nullptr;// 声明static Singleton* _pinst;
};// 定义
Singleton* Singleton::_pinst = nullptr;

        懒汉模式和饿汉模式的特点差不多,无非就是懒汉模式需要的时候才创建,那这就会带来线程安全的问题,多个线程同时访问,第一次多个线程要竞争的创建类,创建好之后就不要再创建了。可以拿线程池作参考。

class Singleton
{
public:static Singleton* GetInstance(){// 只有第一次为空的时候才创建,如果不为空直接返回,这样就只new了一次// 双检查加锁if (_pinst == nullptr) // 已经创建了就直接返回,没有就加锁创建{unique_lock<mutex> lock(_mutex); // RAII的加锁方式if (_pinst == nullptr)_pinst = new Singleton;}return _pinst;// ...// 声明static Singleton* _pinst;static mutex _mutex;
};// 定义
Singleton* Singleton::_pinst = nullptr;
mutex Singleton::_mutex;

特殊的懒汉模式:

class Singleton
{
public:static Singleton* GetInstance(){// 局部静态对象是在调用的时候初始化static Singleton _s;return &_s;}
};

        上面的这种方法在C++11之前是不能保证线程安全的,因为在这之前局部静态对象的构造函数调用初始化并不能保证线程安全,不能保证创建是否是原子的,所以在C++11后修复了这个问题,这种写法只能在支持C++11的编译器上。

单例对象的释放问题:

  1. 一般情况下,单例对象不需要释放,一般整个程序运行期间都会用它。
  2. 单例对象在进程正常结束后也会释放。有些特殊场景也需要释放,比如单例对象析构时,要进行持久化操作,如往磁盘上写,这时就可以写一个内部类,等到程序结束,判断对象是否还在,在就释放。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

<爬虫部署,进阶Docker>----第二章 安装Docker

前言: 安装docker---本章是只针对windows的Docker! 如果你需要你就往下看,不需要 就换频道~ 正文: 1.安装Docker前配置 a.开启虚拟化功能(VT) -如果你电脑有这个 (虚拟化已启用) 直接跳过这一步; 如果没有,那你就去对照自己电脑开启虚拟化 ; 相关链接:win10打开虚拟化的…

解决electron打包vue-element-admin项目页面无法跳转的问题

解决electron打包vue-element-admin项目页面无法跳转的问题 说明之前通过这个教程已经打包成功&#xff0c;但是发现进行账号密码登录后页面无法跳转的问题。现在已经解决&#xff0c;所以记录一下。 1、检查路由模式是否为hash模式&#xff0c;如果不是改成hash模式。 new Ro…

汽车控制臂的拓扑优化-abaqus操作过程

前言 本示例详细讲解使用abaqus实现汽车控制臂的拓扑优化的操作过程。 本页讨论 前言一、创建模型1.创建几何部件2.定义材料属性3.创建装配实体4.创建分析步5.创建相互作用6.创建边界条件及载荷7.创建分析作业并提交分析8.可视化后处理 二、设置优化1.创建优化任务2.创建设计…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Swiper)

滑块视图容器&#xff0c;提供子组件滑动轮播显示的能力。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 可以包含子组件。 说明&#xff1a; 子组件类型&#xff1a;系统组件和自定义组…

学习vue3第五节(reactive 及其相关)

1、定义 reactive() 创建一个响应式代理对象&#xff0c;不同于ref()可以创建任意类型的数据&#xff0c;而reactive()只能是对象&#xff0c;会响应式的深层次解包任何属性&#xff0c;将其标注为响应式 响应式是基于ES6的proxy实现的代理对象&#xff0c;该proxy对象与原对象…

CAN总线协议:遥控帧与错误帧

一. 简介 通过 CAN 总线传输数据是需要按照一定协议进行的。CAN 协议提供了 5 种帧格式来传输数据&#xff1a;数据帧、遥控帧、错误帧、过载帧和帧间隔。 前面一篇文章学习了其中的一种数据帧传输格式&#xff1a;数据帧。文章如下&#xff1a; CAN总线协议&#xff1a;数…