【C++高阶】解锁C++的深层魅力——探索特殊类的奥秘

news2024/11/18 15:35:17

📝个人主页🌹:Eternity._
⏩收录专栏⏪:C++ “ 登神长阶 ”
🤡往期回顾🤡:C++ 类型转换
🌹🌹期待您的关注 🌹🌹

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

❀C++特殊类

  • 📒1. 不能被拷贝的类
  • 📙2. 只能在堆上创建对象
  • 📚3. 只能在栈上创建对象
  • 📜4. 不能被继承的类
  • 📝5. 只能创建一个对象(单例模式)
    • 🌞设计模式
    • ⭐单例模式
  • 📖6. 总结


前言:在C++这片浩瀚的编程海洋中,基础语法与常用库如同岛屿与浅滩,引领着每一位初学者逐步前行。然而,当我们的航程逐渐深入,便会发现那些隐藏于波涛之下的特殊类,它们如同深海中的宝藏,等待着勇敢的探索者去发掘

特殊类,作为C++语言中的高级特性之一,不仅承载着对面向对象编程思想的深刻体现,更是解决复杂问题、优化程序性能的利器。它们包括但不限于模板类、智能指针、迭代器、多态基类(抽象类)、以及那些利用高级特性(如RAII、类型萃取等)设计的独特类。这些特殊类以其独特的设计理念和强大的功能,在C++的各个领域发挥着不可替代的作用

现在我将带领大家一同踏上这场探索之旅,通过详细解析C++中的特殊类,揭示它们的设计原理、应用场景以及使用技巧。我们将从基础概念出发,逐步深入到高级特性,通过实例演示和代码分析,帮助读者逐步掌握这些特殊类的精髓

让我们一同揭开C++特殊类的神秘面纱,领略其独特的魅力与力量吧!


📒1. 不能被拷贝的类

拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载

实现方式:

因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可

  • C++98:将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可

原因:

  • 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不
    能禁止拷贝了
  • 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写
    反而还简单,而且如果定义了就不会防止成员函数内部拷贝了
  • C++11:C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数

代码示例 (C++):

//不能被拷贝的类
//C++98
class CopyBan
{
	// ...
private:
	CopyBan(const CopyBan&);
	CopyBan& operator=(const CopyBan&);
	//...
};

//C++11
class CopyBan
{
	// ...
	CopyBan(const CopyBan&) = delete;
	CopyBan& operator=(const CopyBan&) = delete;
	//...
};

📙2. 只能在堆上创建对象

实现方式:

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


方案一:析构函数私有化:

正常创建对象,会自动调用析构函数,将析构函数私有化,外部代码将无法直接调用delete来销毁对象,因为这将尝试访问一个不可访问的析构函数,要想销毁对象就得自己定义成员函数来操作

代码示例 (C++):

//只能在堆上创建对象
class HeapOnly
{
public:
	// 对象的销毁
	/*static void Destroy(HeapOnly* ptr)
	{
		delete ptr;
	}*/
	
	// 对象的销毁
	void Destroy()
	{
		delete this;
	}

private:
	~HeapOnly()
	{
		cout << "~HeapOnly()" << endl;
	}
};

int main()
{
	HeapOnly* ptr = new HeapOnly;

	//HeapOnly::Destroy(ptr);

	ptr->Destroy();
	return 0;
}

方案二:构造函数私有化:

通过将构造函数私有化,我们可以限制对象创建的方式,从而实现只能在堆上创建对象

代码示例 (C++):

class HeapOnly
{
public:
	// 静态的成员函数,完成完成堆对象的创建
	static HeapOnly* CreateObj()
	{
		return new HeapOnly;
	}

private:
	HeapOnly()
	{
		cout << "HeapOnly()" << endl;
	}
	
	// 防止别人调用拷贝在栈上生成对象
	//C++98
	HeapOnly(const HeapOnly& copy);
	
	//C++11
	HeapOnly(const HeapOnly& copy) = delete;
};

int main()
{
	HeapOnly* ptr = HeapOnly::CreateObj();

	//HeapOnly copy(*ptr);

	return 0; 
}

📚3. 只能在栈上创建对象

实现方式:

将类的构造函数私有,提供一个静态的成员函数,在该静态成员函数中完成栈对象的创建

思考一下:这里的实现方法和上面似乎有点相同,但是我们在实现时,是否也要像上面一样delete掉拷贝构造呢?

静态成员函数 -> 完成栈对象的创建:

static StackOnly CreateObj()
	{
		StackOnly obj;
		return obj;
	}
// 这里会让对象在堆上创建
StackOnly* ptr = new StackOnly(obj);
// 如果我们 delete 拷贝构造,会发现CreateObj()也会受到影响,所以我们只能另辟蹊径
// 我们要从 new 下手让我们不能用 new 来创建对象

实现专属的operator new

void* operator new(size_t size)
{
	cout << "void* operator new(size_t size)" << endl;
	return malloc(size);
}

当我们new这类对象时,我们会优先调用operator new,而不是全局(就近原则),因此我们delete掉我们自己实现的operator new,我们就不能在使用new创建对象

代码示例 (C++):

// 只能在栈上创建的类
class StackOnly
{
public:
	static StackOnly CreateObj()
	{
		StackOnly obj;
		return obj;
	}

	// 如果我们将拷贝构造delete,那么我们就无法创建对象,因为在CreateObj()中也会发生拷贝构造
	//StackOnly(const StackOnly& copy) = delete;

	void* operator new(size_t size) = delete;

	//实现专属的operator new
	//当我们new这类对象时,我们会优先调用operator new,而不是全局(就近原则)
	/*void* operator new(size_t size)
	{
		cout << "void* operator new(size_t size)" << endl;
		return malloc(size);
	}*/

private:
	StackOnly()
	{
		cout << "StackOnly()" << endl;
	}
};

int main()
{
	StackOnly obj = StackOnly::CreateObj();

	// 发生拷贝构造是会在堆上创建类,但是我们能不能将拷贝构造delete呢?
	//StackOnly* ptr = new StackOnly(obj);

	return 0;
}

📜4. 不能被继承的类

实现方式:

  • C++98:中构造函数私有化,派生类中调不到基类的构造函数就无法继承

  • C++11:一个类如果不希望被其他类继承,可以通过将其声明为final类来实现

代码示例 (C++):

// 不能被继承的类
//C++98
class A
{
public:
	static A GetInstance()
	{
		return A();
	}
private:
	A()
	{}
};

// 不能被继承的类
//C++11
class A final
{
	// ....
};

📝5. 只能创建一个对象(单例模式)

🌞设计模式

设计模式(Design Patterns)是一种在软件开发中用于解决常见问题的可重用的解决方案。它们不是代码本身,而是关于如何组织代码和对象之间关系的一种描述和指导,是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结

使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模
式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样

设计模式是面向对象设计原则的体现,它们通过提供可重用的解决方案来帮助开发者更好地设计和实现软件系统。在实际开发中,可以根据具体的需求和场景选择合适的设计模式来解决问题


⭐单例模式

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

单例模式的核心在于控制实例的数目,节省系统资源,并允许全局访问

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

  • 饿汉模式
  • 懒汉模式

  • 饿汉模式
    提前创建好程序,启动时就创建一个唯一的实例对象 -> (提前做好)

优点:实现简单
缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定

代码示例 (C++):

class A
{
public:
	static A* GetInstance()
	{
		return &_inst;
	}
	
	void Add(const string& key, const string& value)
	{
		_dict[key] = value;
	}

	void Print()
	{
		for (auto& kv : _dict)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
	
private:
	A() // 私有构造函数  
	{}

	//限制类外随意创建对象
	A(const A& copy) = delete;
	A& operator=(const A& aa) = delete;

	map<string, string> dict;

	static A _inst;// 静态实例  
	
};
// 类的外部定义静态实例  
A A::_inst;

int main()
{
	A::GetInstance()->Add("sort", "排序");
	A::GetInstance()->Add("left", "左边");
	A::GetInstance()->Add("right", "右边");
	A::GetInstance()->Print();
	
	return 0;
}
  • 懒汉模式
    第一次用的时候在创建 -> (现吃现做)

如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取
文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,
就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好

优点:第一次使用实例对象时,创建对象,进程启动无负载。多个单例实例启动顺序自由控制
缺点:复杂

注意:new的懒汉对象一般不需要释放,进程正常结束会释放资源

代码示例 (C++):

class B
{
public:
	static B* GetInstance()
	{
		// 第一次为空的时候,创建对象
		if (_inst == nullptr)
		{
			_inst = new B;
		}
		return _inst;
	}

	void Add(const string& key, const string& value)
	{
		_dict[key] = value;
	}

	void Print()
	{
		for (auto& kv : _dict)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
	
	static void DelInstance()
	{
		if (_inst)
		{
			delete _inst;
			_inst = nullptr;
		}
	}
private:
	B()
	{}

	//限制类外随意创建对象
	B(const B& copy) = delete;
	B& operator=(const B& bb) = delete;

	map<string, string> _dict;

	static B* _inst; // 指针的形式

	class gc
	{
	public:
		~gc()
		{
			DelInstance();
		}
	};
	static gc _gc;
};

B* B::_inst = nullptr;
B::gc B::_gc;

int main()
{
	B::GetInstance()->Add("sort", "排序");
	B::GetInstance()->Add("left", "左边");
	B::GetInstance()->Add("right", "右边");

	B::GetInstance()->Print();
	return 0;
}

懒汉模式并非这么简单,后面还要牵扯到线程安全,线程安全需要额外处理


📖6. 总结

随着我们一同探索了C++中特殊类的广阔天地,相信你已经对这些高级特性有了更深的理解与感悟。特殊类不仅是C++语言复杂性和强大功能的体现,更是编程艺术的结晶,它们以独特的方式解决了传统编程中难以处理的问题,提升了代码的效率、安全性和可维护性

学习特殊类的过程,实际上是一个不断挑战自我、深化编程思维的过程。它要求我们不仅要掌握语法层面的知识,更要理解其背后的设计思想和实现原理。只有这样,我们才能真正驾驭这些特殊类,将它们灵活地应用于实际开发中,解决实际问题

用一句话总结一下就是:“掌握C++特殊类,就是掌握了一种强大的编程工具,它将助你在编程的世界里走得更远、更稳。”
愿你在未来的编程道路上,能够勇往直前、不断突破自我,创造出更多优秀的作品和成果!

在这里插入图片描述

希望本文能够为你提供有益的参考和启示,让我们一起在编程的道路上不断前行!
谢谢大家支持本篇到这里就结束了,祝大家天天开心!

在这里插入图片描述

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

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

相关文章

外贸网站建设该怎么做

外贸网站已成为企业拓展国际市场、促进贸易合作的重要工具。然而&#xff0c;要想在竞争激烈的国际贸易舞台上脱颖而出&#xff0c;就需要一套科学有效的外贸网站建设方案。本文将为您介绍成功打造外贸网站的关键步骤&#xff0c;助您在数字化时代实现更广阔的国际商机。 第一步…

嗨! 大叔,什么时候退休?

先祝各位中秋快乐&#xff01;&#xff01;&#xff01;接下我们吃瓜。娱乐至上&#xff01; 纯属娱乐&#xff0c;没验证是否正确&#xff01; 2024年9月13日第十四届全国人民代表大会常务委员会第十一次会议通过关于实施渐进式延迟法定退休年龄的决定,我将规则给ChatGPT&am…

如何绕过Cloudflare的403 禁止错误?

Cloudflare 的 403 错误与常规 HTTP 403 错误代码并无二致&#xff0c;都表示禁止访问。这通常意味着你没有权限访问该文档。然而&#xff0c;在使用 Cloudflare 的情况下&#xff0c;当你尝试网页抓取时&#xff0c;可能会遇到这种情况&#xff0c;因为它可能表明你的 IP 地址…

【练习10】链表相加

链接&#xff1a;链表相加(二)_牛客题霸_牛客网 (nowcoder.com) 分析&#xff1a; 算法原理是逆序高精度算法 逆序的原因是为了实现从低位&#xff08;个位&#xff09;开始相加。 public class Solution {//逆序链表public ListNode reverse(ListNode head){ListNode newHead …

尤雨溪推荐的拖拽插件,支持Vue2/Vue3 VueDraggablePlus

大家好,我是「前端实验室」爱分享的了不起~ 今天在网上看到尤雨溪推荐的这款拖拽组件,试了一下非常不错,这里推荐给大家。 说到拖拽工具库,非大名鼎鼎的的 Sortablejs 莫属。它是前端领域比较知名的,且功能强大的工具。但我们直接使用Sortablejs的情况很少,一般都是使用…

java项目之基于Spring Boot智能无人仓库管理源码(springboot+vue)

项目简介 智能无人仓库管理实现了以下功能&#xff1a; 基于Spring Boot智能无人仓库管理的主要使用者分为&#xff1a; 管理员的功能有&#xff1a;员工信息的查询管理&#xff0c;可以删除员工信息、修改员工信息、新增员工信息 &#x1f495;&#x1f495;作者&#xff1a…

容器镜像同步工具image-migrator

1 概述 image-migrator是一个用于容器镜像同步的可执行二进制命令行工具&#xff08;不依赖于docker命令&#xff09;&#xff0c;能够自动将基于Docker Registry v2镜像仓库&#xff08;registry、云厂商容器镜像服务、docker hub、Quay、Harbor &#xff09;中的镜像迁移到基…

MADE A PIE 之动态树形图

MADE A PIE api地址 他那个api里的代码可以直接拿过来用&#xff0c;只是他没有写怎么使用 使用时我发现如果节点太多会导致节点名称重叠&#xff0c;所以我在他这个基础之上加了一个放大缩小和随意拖动&#xff08;dragstart&#xff0c;dragend&#xff0c;flTop&#xff0…

收银员权限-收银系统源码

收银系统对于门店来说是收银员每天日常高频使用的软件工具&#xff0c;但很多门店老板不想给收银员开放很多权限&#xff0c;如商品改价、订单打折、会员充值、订单退款等都需要门店给其开通权限或者有权限码才能操作。 1. 收银员交接班 收银系统要支持交接班&#xff0c;收银…

OJ题——迷宫问题

&#x1f36c;个人主页&#xff1a;Yanni.— &#x1f308;数据结构&#xff1a;Data Structure.​​​​​​ &#x1f382;C语言笔记&#xff1a;C Language Notes &#x1f3c0;OJ题分享&#xff1a; Topic Sharing 前言&#xff1a; 在笔试或者竞赛的过程中&#xff0c;经…

剖析 MySQL 数据库连接池(C++版)

目录 ☀️0. 前言 &#x1f324;️1. 数据库连接池概述 ⛅1.1 服务器与数据库交互 ⛅1.2 MySQL 数据库网络模型 ⛅1.3 MySQL 连接驱动安装 ⛅1.4 同步&#xff08;synchronous&#xff09;连接池与异步&#xff08;asynchronous&#xff09;连接池 ⛅1.5 同步连接池和异…

Spring 循环依赖原理及解决方案

一、什么是循环依赖 循环依赖指的是一个实例或多个实例存在相互依赖的关系&#xff08;类之间循环嵌套引用&#xff09;。 举例&#xff1a; Component public class AService {// A中注入了BAutowiredprivate BService bService; }Component public class BService {// B中也…

通信工程学习:什么是PC永久连接、SPC软永久连接

一、PC永久连接 PC&#xff08;Permanent Connection&#xff09;永久连接是一种由网管系统通过网管协议建立的长期稳定的连接方式。在ASON&#xff08;自动交换光网络&#xff09;中&#xff0c;PC永久连接沿袭了传统光网络的连接建立形式&#xff0c;其特点主要包括&#xff…

【Canvas与密铺】正六边形、正方形和正三角形的密铺

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>正六边形正方形和正三角形的密铺1920x1080</title><style t…

SpringSecurity原理解析(四):SpringSecurity初始化过程

1、对 SpringSecurity初始化时的几个疑问 通过对前边一个请求流转的分析&#xff0c;我们知道一个请求要想到达服务端Servlet需要经过n多个 拦截器处理&#xff0c;请求处理流程如下所示&#xff1a; 对于一个请求到来后会通过FilterChainProxy来匹配一个对应的过滤器链来处理该…

PEST分析法包括哪些内容?用在线白板工具轻松绘制,简单好用!

在当今瞬息万变的商业环境中&#xff0c;企业需要一个全面而系统的方法来分析外部环境对其经营的影响。PEST分析模型恰好提供了这样一个强大的工具&#xff0c;帮助企业洞察政治、经济、社会和技术因素对其发展的潜在影响。 然而&#xff0c;如何高效地创建PEST分析模型一直是…

Unity 第一人称游戏的武器被其他物体覆盖解决方案

在第一人称游戏的时候&#xff0c;会出现渲染过程中&#xff0c;主角的手持武器可能会被其他物体挡住。 解决方法 在主摄像机下再创建一个摄像机&#xff0c;负责渲染不同图层 Main Camera的参数&#xff1a;我们这个摄像机不渲染equipable层&#xff08;自定义武器为equipab…

前后端分离项目实现SSE

SSE介绍 在日常web开发中经常会遇到查看数据最新状态的业务场景&#xff0c;例如查看任务状态与日志内容等。比较场景的解决方案是轮循和SSE。 Server-Sent Events (SSE) 是一种允许服务器通过单向通道向客户端推送更新的技术。它基于HTTP协议&#xff0c;客户端使用一个标准…

2024CCPC网络预选赛

vp链接&#xff1a;Dashboard - The 2024 CCPC Online Contest - Codeforces B. 军训 II 序列 a 从小到大排列或者从大到小排列时&#xff0c;不整齐度是最小的。方案数是所有相同数字的个数的排列数的乘积。如果首尾的数字不同的话&#xff0c;还要再乘个 2。 #include <…