亚马逊跨境电商官方网站,长沙网站优化推广,网站建设学院,广东东莞职业技术学院#x1f308; 个人主页#xff1a;Zfox_ #x1f525; 系列专栏#xff1a;C从入门到精通 目录 #x1f680; 前言 一#xff1a; #x1f525; 不能被拷贝的类 二#xff1a; #x1f525; 只能在堆上创建对象的类 三#xff1a; #x1f525; 只能在栈上创建对象的… 个人主页Zfox_ 系列专栏C从入门到精通 目录 前言 一 不能被拷贝的类 二 只能在堆上创建对象的类 三 只能在栈上创建对象的类 四 不能被继承的类 五 设计一个类只能创建一个对象(单例模式) 六 共勉 前言 在C中类的设计往往需要考虑到特定的使用场景和需求。为了满足这些需求有时我们需要设计一些具备特殊性质的类例如不能被拷贝的类、只能在堆上或栈上创建对象的类、不能被继承的类或者是只能创建一个对象的类单例模式。本文将探讨如何通过C语言的特性和不同版本的标准来实现这些特殊的类设计。 一 不能被拷贝的类 拷贝只会放生在两个场景中拷贝构造函数以及赋值运算符重载因此想要让一个类禁止拷贝只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。 在C98中 将拷贝构造函数与赋值运算符重载 只声明不定义并且将其访问权限设置为私有即可。
class CopyBan
{// ...
private:CopyBan(const CopyBan);CopyBan operator(const CopyBan);//...
}原因
设置成私有如果只声明没有设置成private用户自己如果在类外定义了就不能禁止拷贝了。只声明不定义不定义是因为该函数根本不会调用定义了其实也没有什么意义不写反而还简单而且如果定义了 就不会防止成员函数内部拷贝了。
在C11中 C11扩展 delete 的用法delete 除了释放new申请的资源外如果在默认成员函数后跟上 delete表示让编译器删除掉该默认成员函数。
class CopyBan
{// ...CopyBan(const CopyBan)delete;CopyBan operator(const CopyBan)delete;//...
}二 只能在堆上创建对象的类 实现方式 1. 将类的构造函数私有拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。2. 提供一个静态的成员函数在该静态成员函数中完成堆对象的创建。 class HeapOnly
{
public:static HeapOnly* CreateObject(){return new HeapOnly;}
private:HeapOnly() {}// C98// 1.只声明,不实现。因为实现可能会很麻烦而你本身不需要// 2.声明成私有HeapOnly(const HeapOnly)// or// C11HeapOnly(const HeapOnly) delete;
}说明 通过将构造函数声明为私有我们可以防止在类外部构造对象不管是在栈区、堆区还是静态区但是我们的目的是要能够在堆上创建对象所以我们需要 单独提供一个 CreateObj 成员函数由于在类内部那么就可以调用构造函数来创建一个堆上的对象并返回指向它的指针。 但是 CreateObj 函数必须是静态的因为如果是普通成员函数则其第一个参数是 隐藏的 this 指针所以想要调用这个函数来创建对象就必须先有一个对象然而在构造私有的情况下我们是不可能在类外通过其他方式创建出对象的这就好比先有鸡还是先有蛋的问题但 静态成员函数没有 this 指针所以可以通过类名 域作用限定符 的方式进行调用而不需要通过通过对象调用。 最后我们需要删除拷贝构造函数防止在类外通过下面这种取巧的方式来创建栈区或静态区的对象 三 只能在栈上创建对象的类
class StackOnly
{
public:static StackOnly CreateObj(){return StackOnly();}// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉// StackOnly obj StackOnly::CreateObj();// StackOnly* ptr3 new StackOnly(obj);void* operator new(size_t size) delete;void operator delete(void* p) delete;
private:StackOnly():_a(0){}private:int _a;
}说明 在类中禁用 operator new 和 operator delete 函数 new 和 delete 是 C 中的关键字其底层通过调用 operator new 和 operator delete 函数来开辟与释放空间如果类中没有重载 operator new 和 operator delete 函数那么 new 和 delete 会去调用全局的 operator new 和 operator delete 函数特别注意这两个函数是普通的全局函数而不是运算符重载只是它们的函数名是这样。 所以我们可以在类中重载 operator new 和 operator delete 函数然后将它们声明为删除函数这样就不能通过 new 和 delete 在堆上创建与销毁对象了但是这样有一个缺陷我们只是禁止了在堆上创建对象但是我们仍然可以在静态区创建对象与类的要求不符所以还需要下面一个步骤。 构造私有提供一个在栈上创建对象的静态成员函数 这种设计方式和设计一个只能在堆上创建对象的类的思路一样但是注意不能删除拷贝构造函数否则就不能通过下面这种方式来构造栈对象了 StackOnly st StackOnly::CreateObj() 但是不禁用拷贝构造又会导致可以通过拷贝构造创建出静态区上的对象所以我们设计出的只能在栈上创建对象的类是有缺陷的。
四 不能被继承的类
在C98中 将构造函数私有化派生类中调不到基类的构造函数。则无法继承。
class NonInherit
{
public:static NonInherit GetInstance(){return NonInherit();}
private:NonInherit(){}
}在C11中 final关键字final修饰类表示该类不能被继承。
class A final
{// ....
}五 设计一个类只能创建一个对象(单例模式)
设计模式 设计模式Design Pattern是一套被反复使用的、多数人知晓的、经过分类的代码设计经验的总结。设计模式的产生过程类似于兵法的产生过程 – 在夏商周时代由于打仗比较少所以每次打仗基本都是单纯的对砍人多就能获胜但是随着周朝分封制的推行以及周王朝的衰落各诸侯国进入春秋战国时代经常互相征战仗大多了就发现打仗也是要动脑子的有许多的套路于是有人就总结出了《孙子兵法》。设计模式也是如此代码写的多了自然也就有人去总结一些固定的套路。 使用设计模式的目的是为了提高代码可重用性、让代码更容易被他人理解、保证代码可靠性设计模式使代码编写真正工程化设计模式是软件工程的基石脉络如同大厦的结构一样。
单例模式: 我们之前其实已经接触过一些设计模式了比如迭代器模式、适配器/配接器模式而 只能创建一个对象的类被称为单例模式。单例模式可以保证系统中该类只有一个实例并提供一个访问它的全局访问点该实例被所有程序模块共享。比如在某个服务器程序中该服务器的配置信息存放在一个文件中这些配置数据由一个单例对象统一读取然后服务进程中的其他对象再通过这个单例对象获取这些配置信息这种方式简化了在复杂环境下的配置管理。 单例模式有两种实现方式饿汉模式 和 懒汉模式。
饿汉模式 就是说不管你将来用不用程序启动时就创建一个唯一的实例对象。
// 饿汉模式
// 优点简单
// 缺点可能会导致进程启动慢且如果有多个单例类对象实例启动顺序不确定。
class Singleton
{
public:static Singleton* GetInstance(){return m_instance;}
private:// 构造函数私有Singleton() {};// C98 防拷贝Singleton(Singleton const);Singleton operator(Singleton const);// or// C11Singleton(Singleton const) delete;Singleton operator(Singleton const) delete;static Singleton m_instance;
}Singleton Singleton::m_instance; // 在程序入口之前就完成单例对象的初始化1. 如果这个单例对象在多线程高并发环境下频繁使用性能要求较高那么显然使用饿汉模式来避免资源竞争提高响应速度更好。 2. 由于饿汉模式的对象在 main 函数前就被创建所以它不存在线程安全问题但是它也存在一些缺点 有的单例对象构造十分耗时或者需要占用很多资源比如加载插件、 初始化网络连接、读取文件等等会导致程序启动时加载速度慢。 饿汉模式在程序启动时就创建了单例对象所以即使在程序运行期间并没有用到该对象它也会一直存在于内存中浪费了一定的系统资源。 当多个单例类存在初始化依赖关系时饿汉模式无法控制。比如A、B两个单例类存在于不同的文件中我们要求先初始化A再初始化B但是A、B谁先启动初始化是由OS自动进行调度控制的我们无法进行控制。 这些情况使用 懒汉模式延迟加载更好。 懒汉模式
// 懒汉
// 优点第一次使用实例对象时创建对象。进程启动无负载。多个单例实例启动顺序自由控制。
// 缺点复杂
class Singleton {
public:static Singleton GetInstance(){//第一次进入时创建类对象以后进入直接返回类对象if (_psins nullptr){_psins new Singleton;}return *_psins;}//功能示例函数void func(){//对类中的成员变量进行增删查改或进行其他操作}Singleton(const Singleton sin) delete;Singleton operator(const Singleton sin) delete;private:Singleton() {}private:static Singleton* _psins; //静态单例对象指针的声明private://类的其他成员变量 -- 此类要管理的数据
};
Singleton* Singleton::_psins nullptr; //单例对象指针的定义
}由于懒汉模式是在第一次使用单例对象时才去创建单例对象所以就不存在程序启动加载慢以及不使用对象浪费系统资源的问题了同时我们也可以通过在程序中先使用A对象再使用B对象的方式来控制有初始化依赖关系的单例对象的实例化顺序。 最后需要说明的是在实际开发中单例模式的应用场景非常广泛但是绝大多数情况下我们都是使用饿汉模式只有在极少数的特殊场景下才会使用懒汉模式。 六 共勉
以上就是我对 【C】 特殊类设计 的理解觉得这篇博客对你有帮助的可以点赞收藏关注支持一波~