【C++进阶学习】第十四弹——特殊类设计——探寻各种情况下类的应用

news2024/11/15 8:04:34

前言:

C++类是C++很重要的一个部分,在很多应用场景中都发挥着十分重要的作用,今天我们来讲解几个特殊场景下类的应用

目录

一、特殊类:只能在栈/堆上创建对象

1. 只在栈上创建对象

2. 只在堆上创建对象

二、特殊类:不能被继承

1. 使用 final 关键字

1.1 声明一个不可继承的类

1.2 声明一个不可继承的成员函数

2. 使用 private 访问控制

2.1 私有构造函数和析构函数

3. 使用 deleted 关键字

3.1 使用 deleted 关键字

三、特殊类:单例模式

1. 单例模式概述

2. 实现单例模式的基本方法

2.1 饿汉式

2.2 懒汉式

2.3 饿汉式(线程安全)

四、总结


一、特殊类:只能在栈/堆上创建对象

在C++中,对象的创建位置(栈或堆)对于程序的性能和内存管理有着重要影响。栈上创建的对象通常生命周期短,而堆上创建的对象可以拥有更长的生命周期。下面我们就来详细探讨如何设计一个类,使其对象只能在堆上或栈上创建,以及这些设计选择背后的逻辑和实践。

1. 只在栈上创建对象

设计策略:通过私有构造函数和公共静态工厂方法来实现。

实现步骤

  1. 私有构造函数:将构造函数声明为私有,禁止外部直接调用。
  2. 公共静态工厂方法:提供一个公共的静态方法,用于创建并返回对象的指针。

代码示例

class MyClass {
private:
    MyClass() {} // 私有构造函数,禁止外部直接调用
public:
    static MyClass* CreateInstance() {
        return new MyClass();
    }
    // 其他成员函数和数据成员
};

优点

  • 防止了外部代码直接创建对象,有助于控制对象的生命周期和管理。
  • 通过静态工厂方法,可以提供统一的接口来创建对象,简化了代码结构。

缺点

  • 需要外部代码调用静态工厂方法来创建对象,可能增加了调用成本。
  • 对于复杂的对象创建逻辑,静态工厂方法可能不够灵活。

2. 只在堆上创建对象

设计策略:使用智能指针(如 std::unique_ptr 或 std::shared_ptr)来管理对象的生命周期。

实现步骤

  1. 智能指针:将对象的创建和管理委托给智能指针。
  2. 对象的创建:在需要使用对象的代码中,通过调用智能指针的构造函数来创建对象。

代码示例

#include <memory>

class MyClass {
public:
    MyClass() {
        // 初始化代码
    }
    // 其他成员函数和数据成员
};

int main() {
    std::unique_ptr<MyClass> myObject = std::make_unique<MyClass>(); // 在堆上创建对象
    // 使用myObject
    return 0;
}

优点

  • 自动管理内存,避免了内存泄漏的风险。
  • 提供了异常安全的内存管理,即使在异常抛出时也能正确释放资源。

缺点

  • 与直接使用指针相比,可能引入额外的开销(如智能指针的额外检查)。
  • 对于需要频繁创建和销毁对象的场景,可能会增加性能开销。

总结

设计类以控制对象的创建位置,主要考虑了内存管理的效率、代码的可读性和可维护性。私有构造函数结合公共静态工厂方法适用于控制对象生命周期的场景,而使用智能指针则适用于需要自动内存管理的场景。选择哪种策略取决于具体的应用场景和需求,以及对性能、安全性和代码结构的权衡。

二、特殊类:不能被继承

1. 使用 final 关键字

在 C++ 中,final 关键字可以用来声明一个类或者成员函数,使其不能被进一步继承。

1.1 声明一个不可继承的类

class Base {
public:
    virtual void doSomething() = 0; // 纯虚函数,使得类成为抽象类
};

class Derived final : public Base {
    // 这里尝试继承 Base 类将会导致编译错误
};

在这个例子中,Derived 类通过继承自 Base 类,并且使用了 final 关键字,使得 Derived 类本身也不能被继承。

1.2 声明一个不可继承的成员函数

class Base {
public:
    virtual void doSomething() = 0;

    virtual void finalize() final {
        // 不可覆盖的成员函数
    }
};

在这个例子中,finalize 成员函数被声明为 final,这意味着它不能被派生类覆盖。

2. 使用 private 访问控制

将一个类声明为私有(private)可以防止外部代码创建该类的实例,但并不能阻止继承。为了防止继承,可以将基类的构造函数和析构函数设置为私有。

2.1 私有构造函数和析构函数

class Base {
private:
    Base() {} // 私有构造函数
    ~Base() {} // 私有析构函数

public:
    virtual void doSomething() = 0;
};

class Derived : public Base {
    // 这里尝试继承 Base 类将会导致编译错误
};

在这个例子中,由于 Base 类的构造函数和析构函数都是私有的,因此它不能被继承。

3. 使用 deleted 关键字

在 C++11 及以后的版本中,可以使用 deleted 关键字来声明一个不能被继承的类。

3.1 使用 deleted 关键字

class Base {
public:
    virtual void doSomething() = 0;

    virtual ~Base() = default;

    class Derived final : public Base {
        // 这里尝试继承 Base 类将会导致编译错误
    };
};

class DeletedBase {
public:
    class Derived : public DeletedBase {
        // 这里尝试继承 DeletedBase 类将会导致编译错误
    };
};

在这个例子中,DeletedBase 类的 Derived 类继承尝试会导致编译错误,因为 DeletedBase 类被声明为不能被继承。

总结

通过以上方法,我们可以在 C++ 中设计不可继承的类。使用 final 关键字是最直接的方式,而使用 private 访问控制或 deleted 关键字则可以提供更灵活的解决方案。在实际应用中我们可以通过场景和设计要求来选择最合适的方法

三、特殊类:单例模式

单例模式是一种常用的设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。在 C++ 中,有多种方法可以实现单例模式。

下面主要讲两种实现方法:饿汉式和懒汉式

1. 单例模式概述

单例模式的主要目的是控制一个类的实例化过程,确保在任何时候,只有一个实例被创建,并且全局所有的地方都可以通过同一个引用访问这个实例。

2. 实现单例模式的基本方法

2.1 饿汉式

饿汉式单例模式在类加载时就立即初始化单例对象。

class Singleton {
private:
    static Singleton instance; // 静态成员变量

    Singleton() {} // 私有构造函数

public:
    static Singleton& getInstance() {
        return instance; // 返回单例实例
    }

    // 其他成员函数
};

Singleton Singleton::instance; // 静态成员变量的初始化

在这个例子中,Singleton 类的构造函数是私有的,外部无法直接创建其实例。通过 getInstance 方法,我们可以获取到类的唯一实例。

2.2 懒汉式

懒汉式单例模式在第一次使用时才创建实例。

class Singleton {
private:
    static Singleton* instance; // 静态成员指针

    Singleton() {} // 私有构造函数

public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton(); // 第一次调用时创建实例
        }
        return instance; // 返回单例实例
    }

    static void releaseInstance() {
        if (instance != nullptr) {
            delete instance;
            instance = nullptr;
        }
    }

    // 其他成员函数
};

Singleton* Singleton::instance = nullptr; // 静态成员指针的初始化

在这个例子中,getInstance 方法检查实例是否已经创建,如果没有,则创建一个新的实例。这种方法在第一次使用时才创建实例,但需要注意内存泄漏的问题。

2.3 饿汉式(线程安全)

(这个涉及到线程安全的问题,如果还没有学习线程,可以先跳过这一部分)

在多线程环境下,懒汉式可能会出现问题,因为多个线程可能同时进入 if 判断,导致创建多个实例。为了解决这个问题,可以使用互斥锁(mutex)来保证线程安全。

#include <mutex>

class Singleton {
private:
    static Singleton* instance;
    static std::mutex mutex;

    Singleton() {} // 私有构造函数

public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            std::lock_guard<std::mutex> lock(mutex);
            if (instance == nullptr) {
                instance = new Singleton();
            }
        }
        return instance; // 返回单例实例
    }

    static void releaseInstance() {
        std::lock_guard<std::mutex> lock(mutex);
        if (instance != nullptr) {
            delete instance;
            instance = nullptr;
        }
    }

    // 其他成员函数
};

Singleton* Singleton::instance = nullptr; // 静态成员指针的初始化
std::mutex Singleton::mutex; // 互斥锁的初始化

在这个例子中,我们使用了 std::mutex 来确保在多线程环境下,单例实例只被创建一次。

总结

单例模式在 C++ 中有多种实现方式,包括饿汉式、懒汉式以及线程安全的懒汉式。选择哪种方式取决于具体的应用场景和需求。在设计单例类时,需要注意线程安全,特别是在多线程环境中使用懒汉式单例模式时。

四、总结

以上就是C++部分特殊类的设计问题,总之,在不同的场景下,类可以通过不同的设计形式来实现特殊的功能,更多的特殊类的设计方式等待我们继续去探讨

感谢各位大佬观看,创作不易,还请各位大佬点赞支持!!!

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

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

相关文章

8.20 QT

1.思维导图 2. 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTime> #include <QTimerEvent> #include <QTextToSpeech>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidge…

Zookeeper中 Server 服务器的四种工作状态详解

Zookeeper中 Server 服务器的四种工作状态详解 1. LOOKING2. FOLLOWING3. LEADING4. OBSERVING&#xff08;3.3.0及以后版本&#xff09; &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; ZooKeeper集群中的服务器主要存在以下四种工作状态&a…

32位入门级MCU(ARM Cortex-M3内核)STM32F103系列

推荐: 32位入门级MCU(ARM Cortex-M3内核)中的六边形战士——STM32F103系列 为什么MCU中需要ADC模块 原创 IPBrain平台君 集成电路大数据平台 2024年08月19日 19:18 北京 自从平台君发布了几期关于MCU的文章之后,后台有很多小伙伴们留言。其中有位读者问平台君:平台君,…

python构建一个web程序

from flask import Flaskapp Flask(__name__)app.route(/) def hello_world():return 欢迎来到我的Python Web程序!if __name__ __main__:app.run(debugTrue)1、安装flask D:\Users\USER\PycharmProjects\pythonProject1\p01>pip install flask WARNING: Ignoring invalid…

LLM才硬件(显存)需求

参考&#xff1a; https://www.hardware-corner.net/guides/computer-to-run-llama-ai-model/ GitHub - hiyouga/LLaMA-Factory: Efficiently Fine-Tune 100 LLMs in WebUI (ACL 2024) 直观的一个表&#xff1a;

多模态学习Multimodal Learning:人工智能中的多模态原理与技术介绍初步了解

多模态学习&#xff08;Multimodal Learning&#xff09;是机器学习中的一个前沿领域&#xff0c;旨在综合处理和理解来自不同模态的数据。模态可以包括文本、图像、音频、视频等。随着数据多样性和复杂性增加&#xff0c;多模态学习在自然语言处理、计算机视觉、语音识别等领域…

存储和传输/寻找大端字节序/有哪款MCU或MPU是真支持大端?

文章目录 概述ARM基础ARM 不生产芯片ARM分类和命名ARM指令集和架构ARM架构和内核ARM内核和芯片其他处理器架构 在手册中找寻字节序的踪迹ST 32 MCU 没有大端&#xff1f;Cortex-M 系列Cortex-A 系列Cortex-R 系列小结 芯片外设支持大端字节序DMAC 支持大端字节序的情况CRC 支持…

SpringBoot接入高德地图猎鹰轨迹服务API

SpringBoot接入高德地图猎鹰轨迹服务API 一、AP文档 猎鹰轨迹服务API文档 二、页面图 1、需登录账号&#xff0c;申请对应的应用key值 三、代码部分&#xff1a; 1、控制层 RestController RequestMapping("/gdTrack") public class TrackController {private …

VS Code开发C#(.NET)之快速入门

本篇快速介绍在VS Code中开发C#的完整说明和示例&#xff1a; 环境准备 安装VS Code&#xff1a; 前往Visual Studio Code官网 下载并安装VS Code。 安装.NET SDK&#xff1a; C#是基于.NET框架的&#xff0c;因此需要安装 .NET SDK。 前往 .NET官网 下载并安装适用于操…

【微信小程序】自定义组件 - 插槽

1. 什么是插槽 2. 单个插槽 在小程序中&#xff0c;默认每个自定义组件中只允许使用一个 进行占位&#xff0c;这种个数上的限制叫做单个插槽。 3. 启用多个插槽 在小程序的自定义组件中&#xff0c;需要使用多 插槽时&#xff0c;可以在组件的 .js 文件中&#xff0c;通过…

数据库之间表的迁移

什么时候用&#xff1f; 相信大家在开发中&#xff0c;会先在本地和测试环境的数据库去建立表&#xff0c;没问题了再去正式环境建立相同的表。或者我们有旧系统和新系统&#xff0c;要把旧系统里面的数据库某个功能的相关的表给导出来&#xff0c;这时候就会发生表的数据迁移…

【网络安全】漏洞挖掘:IDOR实例

未经许可&#xff0c;不得转载。 文章目录 正文 正文 某提交系统&#xff0c;可以选择打印或下载passport。 点击Documents > Download后&#xff0c;应用程序将执行 HTTP GET 请求&#xff1a; /production/api/v1/attachment?id4550381&enamemId123888id为文件id&am…

Vue3 的 expose 介绍

在 Vue 3 中&#xff0c;expose 是一个用于控制组件内部方法和属性暴露给父组件的新功能。这使得父组件可以调用子组件内部的方法或访问其数据&#xff0c;尤其在使用组合式 API&#xff08;Composition API&#xff09;时&#xff0c;这种能力非常有用。 1. 基本用法 expose…

目标检测 | yolov9 原理和介绍

相关系列&#xff1a; 目标检测 | yolov1 原理和介绍 目标检测 | yolov2/yolo9000 原理和介绍 目标检测 | yolov3 原理和介绍 目标检测 | yolov4 原理和介绍 目标检测 | yolov5 原理和介绍 目标检测 | yolov6 原理和介绍 目标检测 | yolov7 原理和介绍 目标检测 | yolov8 原理和…

【LeetCode每日一题】——301.删除无效的括号

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 广度优先搜索 二【题目难度】 困难 三【题目编号】 301.删除无效的括号 四【题目描述】 给…

C++ 内存布局 - Part4: 多继承与this指针调整

1. 多继承代码 #include <iostream> #include <cstdio> using namespace std;class Base1 { public:virtual void fooA() { cout << "Base1::fooA" << endl; }virtual void fooB() { cout << "Base1::fooB" << endl;…

叉车ai行人防撞预警系统,前后盲区防撞报警,让行车更安全!

标准配件&#xff1a;主机前/后/内置3个镜头喇叭电源线保险丝摄像头固定支架&#xff08;含螺丝&#xff09;天线主机固定胶条 九盾叉车ai行人防撞预警系统功能主要透过三颗独立摄像头&#xff0c;结合深度学习算法以完成机器视觉的识别工作。其中两颗具备补光设计的超广角摄像…

文本匹配任务(上)

文本匹配任务 1.文本匹配介绍1.1文本匹配定义1.1.1狭义定义1.1.2广义定义 1.2文本匹配应用1.2.1问答对话1.2.1信息检索 2.文本匹配--智能问答2.1基本思路2.2技术路线分类2.2.1按基础资源划分2.2.2 答案生成方式2.2.3 NLP技术 2.3智能问答-Faq知识库问答2.3.1运行逻辑2.3.2核心关…

DC-DC FB分压电阻计算 (MP1584 SY8205为例)

【本文发布于https://blog.csdn.net/Stack_/article/details/141371702&#xff0c;未经许可不得转载&#xff0c;转载须注明出处】 获取文件 【MP1584 MP2451 SY8205 SY8201 FB分压电阻计算】 一般DC-DC芯片对输出电压的调节&#xff0c;是以FB引脚达到0.6V或者0.8V为止的&…

【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第六篇 嵌入式GUI开发篇-第八十五章 Qt控制硬件

i.MX8MM处理器采用了先进的14LPCFinFET工艺&#xff0c;提供更快的速度和更高的电源效率;四核Cortex-A53&#xff0c;单核Cortex-M4&#xff0c;多达五个内核 &#xff0c;主频高达1.8GHz&#xff0c;2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT…