C++ 特殊类设计

目录

0.前言

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

1.1C++98实现

1.2C++11实现

2.设计一个只能在堆上创建对象的类

3.设计一个只能在栈上创建对象的类

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

4.1C++98实现

4.2C++11实现

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

5.1设计模式简介

5.2单例模式

5.2.1饿汉模式

5.2.2懒汉模式

6.小结


(图像由AI生成) 

0.前言

在C++中,类的设计往往需要考虑到特定的使用场景和需求。为了满足这些需求,有时我们需要设计一些具备特殊性质的类,例如不能被拷贝的类、只能在堆上或栈上创建对象的类、不能被继承的类,或者是只能创建一个对象的类(单例模式)。本文将探讨如何通过C++语言的特性和不同版本的标准来实现这些特殊的类设计。

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

在C++中,有时需要设计一个类,使得该类的对象不能被拷贝或赋值。这种设计可以防止对象在不合适的上下文中被复制,确保数据的一致性和安全性。以下是如何在不同版本的C++中实现这样的类。

1.1C++98实现

在C++98中,为了禁止一个类的对象被拷贝,我们可以将类的拷贝构造函数和赋值运算符声明为私有,并且不提供这两个函数的实现。这样,当尝试对该类对象进行拷贝或赋值操作时,编译器会因为无法访问私有成员而产生编译错误,从而阻止拷贝行为。

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

实现原理:

  1. 设置成员为私有:如果拷贝构造函数和赋值运算符被声明为私有,那么它们无法在类外部被调用,无法执行拷贝操作。

  2. 只声明不定义私有成员函数:由于该函数被声明为私有且没有实现,因此即使在类内部也无法使用它们,确保了类的不可拷贝性。

1.2C++11实现

在C++11中,这种设计变得更加直观和简洁。C++11引入了= delete关键字,可以显式地删除拷贝构造函数和赋值运算符,表示这些操作不允许被调用。这样可以直接通过语法来禁止拷贝行为。

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

实现原理:

  • 使用= delete:通过在函数声明后使用= delete,我们明确告知编译器该函数不应被使用,任何试图调用这些函数的代码都会导致编译错误。

C++11的这种实现方式不仅简化了代码,还提高了代码的可读性,直接表明了类设计的意图。

2.设计一个只能在堆上创建对象的类

有时候,我们希望一个类的对象只能通过动态内存分配(即在堆上创建),而不能在栈上创建。这种设计在某些需要严格控制对象生命周期的场景中非常有用。为了实现这一目标,我们可以将类的析构函数声明为私有或保护成员。

实现方法

通过将析构函数设为私有或保护,我们可以防止对象在栈上创建。因为栈上创建的对象在作用域结束时会自动调用析构函数,如果析构函数是私有的,编译器将无法访问,从而导致编译错误。

以下是一个示例:

class HeapOnly
{
public:// 提供一个公共的静态方法用于创建对象static HeapOnly* createInstance() {return new HeapOnly();}private:// 私有构造函数,防止直接实例化HeapOnly() {}// 私有析构函数,防止栈上创建对象~HeapOnly() {}
};

实现原理:

  1. 私有化析构函数:由于析构函数是私有的,栈上创建的对象在离开作用域时将无法调用析构函数,这会导致编译错误。这样就强制要求对象只能通过new操作符在堆上创建。

  2. 提供静态工厂方法:通过提供一个静态方法createInstance,我们可以控制对象的创建过程,确保它们只在堆上创建。这个静态方法返回一个指向新分配对象的指针。

  3. 私有化构造函数:构造函数被私有化,防止类在外部被直接实例化,这也是为了确保对象只能通过工厂方法创建。

3.设计一个只能在栈上创建对象的类

在某些场景中,我们希望类的对象只能在栈上创建,而不能在堆上动态分配。这种设计可以确保对象在函数结束时自动销毁,从而简化内存管理,避免手动释放内存的麻烦。

实现方法

为了实现只能在栈上创建对象的类,我们可以禁用newdelete操作符。这可以通过将这些操作符声明为私有成员函数来实现。由于在类外部无法访问私有成员,因此无法通过new操作符在堆上创建对象。

以下是一个示例:

class StackOnly
{
public:// 公共构造函数,允许栈上创建对象StackOnly() {}private:// 禁用堆上创建对象void* operator new(size_t) = delete;void operator delete(void*) = delete;
};

实现原理:

  1. 禁用new操作符:通过将operator new声明为私有并使用= delete,我们明确告诉编译器不允许在堆上创建该类的对象。任何试图通过new操作符创建对象的代码都会导致编译错误。

  2. 禁用delete操作符:同样地,将operator delete声明为私有并使用= delete,防止对象在堆上被错误地销毁。

  3. 公共构造函数:构造函数是公共的,允许对象在栈上正常创建。

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

有时候,我们希望设计一个类,使得它不能被继承。这在防止类的行为被修改或确保类的接口不被破坏时非常有用。在不同的C++版本中,实现这个目标的方式有所不同。

4.1C++98实现

在C++98中,设计一个不能被继承的类相对繁琐。通常的做法是将类的构造函数和析构函数声明为私有成员,并通过友元类或静态工厂方法来控制类的实例化。这种方法虽然有效,但实现上比较复杂。

class NonInheritable {
private:NonInheritable() {}~NonInheritable() {}// 声明一个友元类或静态工厂函数,用于实例化对象friend class Factory;
};class Factory {
public:static NonInheritable createInstance() {return NonInheritable();}
};

实现原理:

  1. 私有化构造函数和析构函数:将类的构造函数和析构函数声明为私有,防止其他类从该类继承,因为子类无法访问基类的私有构造函数和析构函数。

  2. 通过友元类或静态工厂方法创建实例:由于构造函数是私有的,我们需要通过友元类或静态工厂方法来创建对象,从而确保类不能被继承,同时还能创建实例。

这种方法虽然有效,但代码相对复杂,而且增加了类的维护成本。

4.2C++11实现

C++11引入了final关键字,大大简化了设计不可继承类的过程。只需在类声明时使用final关键字即可直接禁止该类被继承。

class NonInheritable final {
public:NonInheritable() {}~NonInheritable() {}
};

实现原理:

  • 使用final关键字:在类声明时使用final关键字,表明该类不能被继承。任何尝试继承该类的操作都会导致编译错误。

这种方法不仅简单明了,还提高了代码的可读性,明确了类设计的意图。final关键字的引入,使得在C++11及以后的标准中设计不可继承类变得更加容易和直观。

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

单例模式(Singleton Pattern)是一种非常重要的设计模式,用于确保一个类在整个程序运行期间只能有一个实例,并提供一个全局访问点。这种模式在需要唯一性对象的场景中非常有用,如配置管理器、日志管理器等。

5.1设计模式简介

设计模式是软件开发中反复出现的解决特定问题的通用解决方案。它们不是现成的代码,而是可以在各种情况下被灵活应用的代码设计思想。单例模式就是其中的一种,旨在控制对象的创建数量,确保一个类只有一个实例,并且提供一个方法来访问这个唯一实例。

5.2单例模式

单例模式可以通过多种方式实现,常见的有饿汉模式和懒汉模式。下面将分别介绍这两种模式的实现方法。

5.2.1饿汉模式

饿汉模式是一种单例模式的实现方式,它在类加载时就创建单例对象,无论该对象是否会被使用。由于实例在类加载时就完成了初始化,这种方式是线程安全的,不需要额外的同步机制。

实现方式:

class Singleton {
public:// 提供一个全局访问点static Singleton& getInstance() {static Singleton instance; // 静态局部变量,在类加载时创建实例return instance;}// 禁止拷贝构造和赋值Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;private:// 私有化构造函数,防止外部实例化Singleton() {}
};

实现原理:

  1. 静态局部变量getInstance方法中使用了静态局部变量instance,它在第一次调用时被创建,并在程序的整个生命周期内存在。

  2. 私有化构造函数:构造函数是私有的,防止类在外部被直接实例化,确保只有一个实例存在。

  3. 删除拷贝构造函数和赋值运算符:通过删除拷贝构造函数和赋值运算符,防止类实例被复制,确保单例的唯一性。

5.2.2懒汉模式

懒汉模式是一种延迟初始化的单例模式实现方式,它在第一次需要使用单例对象时才创建实例。这种方式节省了资源,避免了在不需要单例对象时的提前创建,但需要额外的同步机制来保证线程安全。

实现方式:

#include <mutex>class Singleton {
public:// 提供一个全局访问点static Singleton* getInstance() {if (instance == nullptr) {std::lock_guard<std::mutex> lock(mtx); // 锁定,以保证线程安全if (instance == nullptr) {instance = new Singleton(); // 延迟创建实例}}return instance;}// 禁止拷贝构造和赋值Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;private:Singleton() {} // 私有化构造函数static Singleton* instance; // 静态指针,指向单例对象static std::mutex mtx;      // 互斥量,确保线程安全
};// 初始化静态成员
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;

实现原理:

  1. 静态指针:使用静态指针instance来指向单例对象,初始值为nullptr,表明尚未创建对象。

  2. 延迟初始化:在getInstance方法中,第一次调用时检查instance是否为空,如果为空则创建单例对象。

  3. 线程安全:通过std::mutexstd::lock_guard实现线程安全的延迟初始化,防止多个线程同时创建多个实例。

  4. 私有化构造函数和删除拷贝操作:和饿汉模式一样,通过私有化构造函数和删除拷贝构造函数及赋值运算符,确保单例的唯一性。

 以上是单例模式的两种常见实现方式,饿汉模式简单且线程安全,适合在启动时即可确定需要单例的场景;懒汉模式则更为灵活,适合在资源紧张且单例对象并非总是必要的场景。选择哪种实现方式取决于具体的应用需求。

6.小结

在这篇博客中,我们探讨了如何在C++中设计几种具有特殊性质的类,包括不可拷贝类、只能在堆上或栈上创建的类、不可继承的类,以及单例模式的实现。通过这些设计技巧,我们能够更好地控制对象的创建、使用和生命周期,从而编写出更健壮、更易维护的代码。理解并灵活应用这些模式和技巧,不仅可以满足特定的编程需求,还能提升整体代码质量。

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

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

相关文章

Jupyter nbextensions安装与使用

这里写自定义目录标题 Jupyter nbextensions安装与使用安装7以下版本&#xff0c;安装插件包推荐使用的插件 Jupyter nbextensions安装与使用 目前&#xff0c;jupyter版本升级到了7以上版本&#xff0c;导致其界面非常难看&#xff0c;因此&#xff0c;为了重回之前的使用界面…

buuctf-crypto

前言 查找资料的时候,意外翻出之前刷的一些ctf题目,算是简单记录一下,当然因为常用typeo去写md文件,所以其中有很多当时记录的图片都失效了,可惜了 题目1:一眼就解密 ZmxhZ3tUSEVfRkxBR19PRl9USElTX1NUUklOR30 base64解密 flag:flag{THE_FLAG_OF_THIS_STRING} 题目2:MD5 …

全球化浪潮下的数据库革新:嘉里物流 TiDB 实践价值的设想

导读 本文来自 TiDB 社区武汉站——嘉里物流架构团队负责人肖飞老师的演讲《嘉里物流 & TiDB 在全球化业务场景中应用设想》。本次分享探讨了嘉里物流在全球化扩展中&#xff0c;将如何通过 TiDB 的强大功能应对海量数据挑战&#xff0c;优化技术架构&#xff0c;并提升决…

【Linux】详解自定义Shell管道 | 构建简易进程池

目录 续&#xff1a;通信 4 种情况 应用场景 1. 自定义 shell 管道 1. 包含头文件 2. 解析命令函数 详细步骤 3. 执行命令函数 4. 主函数 总结 2. 使用管道实现一个简易版本的进程池 代码结构 代码实现 channel.hpp tasks.hpp main.cc 子进程读取任务&#xff…

十九、虚拟机VMware Workstation(CentOSDebian)的安装

目录 &#x1f33b;&#x1f33b; 一、安装 VMware Workstation1.1 安装 VMware Workstation1.2 虚拟机上安装 CentOS1.3 虚拟机安装 Debian 二、配置Debian方便第三方工具远程连接2.1 配置debian2.2 安装远程SSH工具并连接 一、安装 VMware Workstation 官网下载 本地资源库…

你好! Git——企业级开发模型

企业级开发模型&#xff08;6&#xff09; 一、删除远程分支&#xff0c;git branch -a &#xff08;查看所有本地分支与远程分支&#xff09;还能看到已经删除的分支&#xff0c;怎么解决&#xff1f;二、企业级开发流程2.1 企业级开发流程2.2 系统开发环境 三、Git分支设计模…

RabbitMQ面试题汇总

RabbitMQ面试题 一、RabbitMQ基础1. 什么是RabbitMQ&#xff0c;它的基本架构是怎样的&#xff1f;2. RabbitMQ支持哪些协议&#xff1f;3. 说一下AMQP协议&#xff1f;4. 为什么要使用RabbitMQ&#xff1f;5. MQ的应用场景有哪些&#xff1f;6. 解耦、异步、削峰是什么&#x…

购物系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;商品分类管理&#xff0c;商品信息管理&#xff0c;特价商品管理&#xff0c;用户管理&#xff0c;留言板管理&#xff0c;订单管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&…

uni-app总结

1. <u-form-item label"报废人" ><u--input v-model"model.remark" border"bottom" placeholder"请输入"></u--input> </u-form-item> border"bottom" 报废日期 为了

后端Web开发之Maven

1.java项目构建工具maven介绍 Maven是apache旗下的一个开源项目。Apache软件基金会&#xff0c;成立于1999年7月&#xff0c;是目前世界上最大的最受欢迎的开源&#xff08;源代码开放&#xff09;软件基金会也是一一个专门为支持开源项目而生的非盈利性组织。 apache开源项目…

PDO在CANopen协议同步传输和异步传输

PDO&#xff08;过程数据对象&#xff09;在CANopen协议中有两种主要的传输方式&#xff1a;同步传输和异步传输。这两种方式决定了PDO数据的传输时机和条件。下面分别举例说明这两种传输方式&#xff1a; 1. 同步传输 (Synchronous Transmission) 概念&#xff1a; 在同步传输…

3GPP 4G 5G 主要协议

4G LTE的协议主要是36 series 5G NR的协议主要是38 series

RustScan:开源端口扫描器

RustScan 是一款开源端口扫描器&#xff0c;专为速度和多功能性而设计。 它结合了时尚的界面和随时间推移而适应和改进的能力。 借助 RustScan 的自适应学习功能&#xff0c;该工具不断优化其性能&#xff0c;使其成为最高效的端口扫描器。 在几秒钟内发现开放端口&#xff…

解决端口号被占用问题

第一种&#xff1a; 最简单有效的方法&#xff0c;重启一下电脑&#xff0c;占用此端口的程序就会释放端口。 第二种&#xff1a; 使用命令找到占用端口的程序&#xff0c;把它关闭。 1、打开运行窗口输入&#xff1a;CMD &#xff0c;进入命令窗口。 2、输入&#xff1a;n…

Candance Allegro 入门教程笔记:如何绘制原理图和原理图库?

文章目录 一、用 Capture CIS 17.4 绘制原理图库 Cadence Allegro QQ交流学习裙&#xff1a;173416628 1、凡亿教育的Candance Allegro 17.4基础教程 2、小哥Cadence Allegro 132讲 技巧视频 3、小哥Cadence Allegro 两层板 基础视频 4、小哥Cadence Allegro 四层板 提高视频…

23.10 Django 事务的使用

1. 事务 事务(Transaction): 是一种将多个数据库操作组合成一个单一工作单元的机制. 如果事务中的所有操作都成功完成, 则这些更改将永久保存到数据库中. 如果事务中的某个操作失败, 则整个事务将回滚到事务开始前的状态, 所有的更改都不会被保存到数据库中. 这对于保持数据的…

3.串口(UART)

串口理论部分可看51部分&#xff1a;链接 数据帧 帧头(2字节&#xff0c;例如AA、BB) 数据长度&#xff08;2字节&#xff09; 数据 CRC16校验&#xff08;2字节&#xff09; 帧尾&#xff08;2字节&#xff09; 代码编写 串口一发送命令控制LED灯(PB5、PE5) LED灯、串口、…

【书生·浦语大模型实战营】第三期 入门岛作业

入门岛作业 Linux闯关任务&#xff1a;完成 SSH 连接与端口映射并运行 hello_world.py。配置vscode作业内容 可选任务1&#xff1a;将Linux基础命令在开发机上完成一遍作业内容 可选任务 2&#xff1a;使用 VSCODE 远程连接开发机并创建一个conda环境作业内容 可选任务 3&#…

Selenium + Python 自动化测试10(unittest概念)

我们的目标是&#xff1a;按照这一套资料学习下来&#xff0c;大家可以独立完成自动化测试的任务。 上几篇我们讨论了元素的定位方法、操作方法以及一些特殊元素的操作。 在实际的测试项目组中每个模块会写多条案例&#xff0c;如第一条用例那里我们的登录。登录的话就可以有多…

面试经典算法150题系列-接雨水

接雨水 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff1a;上面是由数组 [0,1,0,2,1,0,1,3,2,…