常见手撕项目C++

常见手撕项目C++

  • 设计模式
    • 单例模式
      • 饿汉模式
      • 懒汉模式
    • 策略模式
      • 策略接口
      • 实现具体的策略(虚函数重写)
      • 定义上下文
      • 用户调用
  • 代码
    • 使用函数模板写冒泡排序
    • 写一个类模板

设计模式

单例模式

在这里插入图片描述
单例模式是一种常用的软件设计模式,其目的是确保一个类只有一个实例,并提供一个全局访问点来获取该实例。

  • 优点:
    • 资源控制:单例模式能够确保一个类只有一个实例存在,这对于控制资源的使用非常有用,如配置文件的读取、数据库的连接等,可以避免由于多个实例造成的资源浪费或冲突。
    • 全局访问点:单例对象可以被全局访问,方便其他对象对其进行访问,而无需持有单例类的引用。
    • 数据共享:由于整个应用程序共享一个单例实例,它自然地提供了一个共享数据的环境,这在某些场合下是非常有用的。
  • 缺点:
    • 全局变量(同步)问题:单例模式本质上提供了一个全局可访问的实例,但全局变量(或对象)容易被误用,特别是涉及多个线程进行访问的时候,还会出现同步问题。
    • 违背单一职责原则:单例类除了管理自己的实例外,还承担了业务逻辑的职责,违反了单一职责原则。

介绍完单例模式,我们来看看单例模式的两种实现方式,分别是饿汉模式与懒汉模式。

饿汉模式

在这里插入图片描述

饿汉模式指的是单例实例在程序启动时就立即创建(迫不及待的感觉)。这种方式避免了线程安全问题,但可能会增加程序的启动时间,同时如果实例最终未被使用,则会造成资源的浪费。

class EagerSingleton{
private:// 将自己的实例化对象申明为静态资源static EagerSingleton instance;
protected:// 隐藏自己的构造函数以及析构函数,防止用户调用EagerSingleton() = default; // 这里构造函数设置为默认EagerSingleton(const EagerSingleton&) = default;EagerSingleton& operator= (const EagerSingleton&) = default;~EagerSingleton() = default;
public:EagerSingleton& getInstance(){return instance;}
}// 静态的私有成员变量可以在类外进行初始化(一般在main()函数之前进行初始化),在这里,你可以理解instance是类内成员,可以访问私有以及保护成员。
EagerSingleton EagerSingleton::instance();

懒汉模式

在这里插入图片描述
懒汉模式指的是单例实例在第一次被使用时才进行创建(不叫我,那我就懒,不创建)。这种方式可以减少资源的消耗,但需要考虑线程安全问题(例如多个线程同时是第一次使用,所以一般需要锁)。

#include <mutex>class lazySingleton{
private:static lazySingleton* instance; // 懒汉模式一般使用指针static mutex my_mu; // 考虑到线程安全,需要有锁。
protected:// 不给用户调用构造函数和析构函数的机会lazySingleton() = default;lazySingleton(const lazySingleton&) = default;lazySingleton& operator=(const laySingleton&) = default;~lazySingleton() = default;
public:lazySingleton* getInstance(){if(instance == nullptr){ // 第一次检查std::lock_guard<std::mutex> lock(my_mu); //作用域锁,离开作用域后,自动解锁if(instance == nullptr){ // 第二次检查instance = new lazySingleton();}}return *this;}// my_mu会在这里结束后,自动解锁
}// 静态成员变量类外初始化
lazySingleton lazySingleton::instance = nullptr;
lazySingleton lazySingleton::my_mu; // 调用锁的自动初始化方法

这里可能会好奇,为什么需要两次判断instance == nullptr

  • 第一次检查 (instance == nullptr)
    第一次检查是在锁外进行的。这个检查的目的是避免在单例实例已经创建之后的每次调用都需要进行昂贵的锁操作。如果实例已经存在,就直接返回实例,这样大部分时间可以避免锁的开销。
  • 获取锁
    如果第一次检查发现实例为nullptr,即单例尚未被创建,那么就需要进入同步块(通过获取锁)来确保只有一个线程可以创建单例实例。这是必要的,因为可能有多个线程同时通过了第一次的nullptr检查。
  • 第二次检查 (instance == nullptr)
    即使线程成功获取了锁,仍然需要再次检查实例是否为nullptr。这是因为在当前线程等待锁的同时,可能有另一个线程已经获取了锁、创建了实例并释放了锁。第二次检查确保了即使在多个线程同时尝试创建单例实例的情况下,单例实例仍然是唯一的。

策略模式

在这里插入图片描述
策略模式是一种行为设计模式,它允许在运行时选择算法或行为的最佳策略。策略模式定义了一系列的算法,并将每一个算法封装起来,使它们可以互换。这种模式让算法的变化独立于使用算法的客户。

策略接口

首先,要定义一个策略接口,表示可以执行的操作

class Strategy{
public:virtual ~Strategy(){};virtual void excute() const = 0; // 操作定义为纯虚函数
}

实现具体的策略(虚函数重写)

然后,利用继承,实现不同且具体的策略

class StrategyA : public Strategy{
public:void excute() const override{ //override 关键字表示一定是重写虚函数,避免出现覆盖的情况(如果有存在的话)cout << "StrategyA" << endl;}
}class StrategyB : public Strategy{
public:void excute() const override{ //override 关键字表示一定是重写虚函数,避免出现覆盖的情况(如果有存在的话)cout << "StrategyB" << endl;}
}class StrategyB : public Strategy{
public:void excute() const override{ //override 关键字表示一定是重写虚函数,避免出现覆盖的情况(如果有存在的话)cout << "StrategyB" << endl;}
}

定义上下文

接着,定义一个上下文类,用于从客户端接收策略,并使用它执行操作

#include <iostream>
#include <memory> // 智能指针共享库
using namespace std;class Context{
public:unique_ptr<Strategy> strategy; // 使用独占的智能指针// 使用右值引用来接收一个unique_ptr,并提供默认参数(即调用这个函数的时候可以不用传入参数)Context(unique_ptr<Strategy> &&strategy = {}): strategy(std::move(strategy)){}// 设置接口到底执行哪一个函数void setStrategy(unique_ptr<Strategy> &&strategy){strategy = std::move(strategy);}// 执行具体函数void excuteStrategy() const{if(strategy){strtegy->excute();}}
}

用户调用

int main(){// 需要注意make_unique<StrategyA>()是一个不具名的右值,所以可以正确调用右值构造函数Context context(make_unique<StrategyA>());context.excuteStrategy(); // 输出:StrategyA// 动态切换策略context.setStrategy(make_unique<StrategyB>());context.excuteStrategy(); // 输出:StrategyBcontext.setStrategy(make_unique<StrategyC>());context.excuteStrategy(); // 输出:StrategyCreturn 0;
}

代码

使用函数模板写冒泡排序

#include <iostream>// 函数模板定义冒泡排序
template<typename T>
void bubbleSort(T arr[], int n) {for (int i = 0; i < n-1; i++) {for (int j = 0; j < n-i-1; j++) {// 如果当前元素大于下一个元素,则交换它们if (arr[j] > arr[j+1]) {T temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}}
}// 打印数组元素的函数
template<typename T>
void printArray(T arr[], int size) {for (int i = 0; i < size; i++) {std::cout << arr[i] << " ";}std::cout << std::endl;
}int main() {int arr[] = {64, 34, 25, 12, 22, 11, 90};int n = sizeof(arr)/sizeof(arr[0]);bubbleSort(arr, n);std::cout << "Sorted array: \n";printArray(arr, n);// 可以用相同的函数模板对不同类型的数组进行排序double arrDouble[] = {4.3, 2.5, -0.9, 10.2, 3.0};int m = sizeof(arrDouble)/sizeof(arrDouble[0]);bubbleSort(arrDouble, m);std::cout << "Sorted array of doubles: \n";printArray(arrDouble, m);return 0;
}

写一个类模板

#include <iostream>
using namespace std;// Pair类模板定义
template <typename T1, typename T2>
class Pair {
private:T1 first;T2 second;public:Pair(T1 a, T2 b) : first(a), second(b) {}void setFirst(T1 a) {first = a;}void setSecond(T2 b) {second = b;}T1 getFirst() const {return first;}T2 getSecond() const {return second;}void print() const {cout << "(" << first << ", " << second << ")" << endl;}
};int main() {// 使用类模板创建int和double类型的PairPair<int, double> p1(6, 7.8);p1.print();// 使用类模板创建string和string类型的PairPair<string, string> p2("Hello", "World");p2.print();// 使用类模板创建char和int类型的PairPair<char, int> p3('A', 100);p3.print();return 0;
}

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

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

相关文章

2024 批量下载雪球文章导出pdf,以市场高标解读这个号为例

之前分享过我开发的雪球文章批量下载工具 2023 年苏生不惑开发的第 1 个工具 &#xff0c;最近又更新了下&#xff0c;今天以市场高标解读这个号为例。 首先登陆雪球账号&#xff0c;获取工具后打开工具输入雪球主页地址和浏览器cookie &#xff0c;如何获取cookie见教程 总有人…

Gitea 的详细介绍

什么是 Gitea&#xff1f; Gitea 是一个开源、轻量级的自托管 Git 服务&#xff0c;它允许用户搭建类似于 GitHub 或 GitLab 的代码托管平台。由于采用 Go 语言开发&#xff0c;Gitea 具有高效的性能和跨平台特性&#xff0c;适合个人开发者或小团队使用。 Gitea 的特点 轻量…

机器学习实验作业一----knn算法

机器学习课程的第一个算法knn算法&#xff0c;全称K-Nearest Neighbor&#xff0c;k最邻近算法&#xff0c;为机器学习中最常用&#xff0c;也是最简单的算法。KNN通过测量不同特征值之间的距离来进行分类。本文实现的是较为简单的knn算法&#xff0c;包括测试集&#xff0c;训…

【c++】类和对象(五)赋值运算符重载

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章带大家认识赋值运算符重载&#xff0c;const成员&#xff0c;取地址及const取地址操作符重载等内容 目录 1.赋值运算符重载1.1运算符重载1.1.1特性&#…

C语言循环结构的程序设计

在C语言中&#xff0c;循环结构是一种重要的控制结构&#xff0c;用于重复执行特定的代码块&#xff0c;直到满足特定的条件为止。循环结构使得程序可以更加灵活和高效地处理重复性的任务&#xff0c;从而提高了程序的可读性和可维护性。本文将深入介绍C语言中循环结构的程序设…

空间数据结构(四叉树,八叉树,BVH树,BSP树,K-d树)

下文参考&#xff1a;https://www.cnblogs.com/KillerAery/p/10878367.html 游戏编程知识课程 - 四分树(quadtree)_哔哩哔哩_bilibili 利用空间数据结构可以加速计算&#xff0c;是重要的优化思想。空间数据结构常用于场景管理&#xff0c;渲染&#xff0c;物理&#xff0c;…

【Java】LinkedList模拟实现

目录 整体框架IMyLinkedList接口IndexNotLegalException异常类MyLinkedList类成员变量(节点信息)addFirst(头插)addLast(尾插)在指定位置插入数据判断是否存在移除第一个相等的节点移除所有相等的节点链表的长度打印链表释放回收链表 整体框架 IMyLinkedList接口 这个接口用来…

QT+Opencv+yolov5实现监测

功能说明&#xff1a;使用QTOpencvyolov5实现监测 仓库链接&#xff1a;https://gitee.com/wangyoujie11/qt_yolov5.git git本仓库到本地 一、环境配置 1.opencv配置 将OpenCV-MinGW-Build-OpenCV-4.5.2-x64文件夹放在自己的一个目录下&#xff0c;如我的路径&#xff1a; …

【RedHat】使用cron安排周期性任务——周期性创建用户实例

cron用来管理周期性重复执行的任务调度&#xff0c;非常适合日常系统维护工作。计划任务分为系统的计划任务和用户自定义的计划任务。 cron服务每分钟都检查/etc/crontab文件、/etc/cron.d目录和/var/spool/cron目录中的变化。/var/spool/cron目录下的任务需要通过crontab -e 命…

SEH异常之编译器原理探究(1)

_try_except原理 调用_except_handle3这个异常处理函数&#xff0c;这里并不是每个编译器的异常处理函数都是相同的&#xff0c;然后存入结构体&#xff0c;将esp的值赋给fs:[0]&#xff0c;再就是提升堆栈的操作 每个使用 _try _except的函数&#xff0c;不管其内部嵌套或反复…

系统资源紧缺?不用担心,Linux命令和Shell脚本帮你搞定

在之前的文章中介绍了如何申请AWS免费主机使用WordPress搭建自己的个人网站&#xff0c;但是在我使用过程中发现了一个问题&#xff0c;由于陆陆续续安装了好几个插件&#xff0c;偶尔在访问网站时会出现数据库连接出错的异常情况&#xff0c;导致页面无法访问。稍等一会儿刷新…

基于Springboot+vue的鲜花销售商城网站

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;鲜花销售商城当然也不能排除在外。鲜花销售商城是以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#x…