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

9,932次阅读
没有评论

共计 4588 个字符,预计需要花费 12 分钟才能阅读完成。

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

 在这里插入图片描述

【C  高阶】解锁 C  的深层魅力——探索特殊类的奥秘
【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++):



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


class CopyBan
{
	
	CopyBan(const CopyBan&) = delete;
	CopyBan& operator=(const CopyBan&) = delete;
	
};

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

实现方式:

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


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

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

代码示例 (C++):


class HeapOnly
{
public:
	
	
	
	
	void Destroy()
	{
		delete this;
	}

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

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

	

	ptr->Destroy();
	return 0;
}

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

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

代码示例 (C++):

class HeapOnly
{
public:
	
	static HeapOnly* CreateObj()
	{
		return new HeapOnly;
	}

private:
	HeapOnly()
	{
		cout  "HeapOnly()"  endl;
	}
	
	
	
	HeapOnly(const HeapOnly& copy);
	
	
	HeapOnly(const HeapOnly& copy) = delete;
};

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

	

	return 0; 
}

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

实现方式:

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

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

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

static StackOnly CreateObj()
	{
		StackOnly obj;
		return obj;
	}

StackOnly* ptr = new StackOnly(obj);


实现专属的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;
	}

	
	

	void* operator new(size_t size) = delete;

	
	
	

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

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

	
	

	return 0;
}

📜4. 不能被继承的类

实现方式:

代码示例 (C++):



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



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;

	mapstring, 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;

	mapstring, 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 ++ 特殊类,就是掌握了一种强大的编程工具,它将助你在编程的世界里走得更远、更稳。”
愿你在未来的编程道路上,能够勇往直前、不断突破自我,创造出更多优秀的作品和成果!

 在这里插入图片描述

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

 在这里插入图片描述

原文地址: 【C 高阶】解锁 C 的深层魅力——探索特殊类的奥秘

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于2024-10-14发表,共计4588字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)