成都网站建设龙兵,wordpress 编辑权限,中国风网站建设,wordpress 前台发文章#x1f308;欢迎来到C专栏~~特殊类设计 (꒪ꇴ꒪(꒪ꇴ꒪ )#x1f423;,我是Scort目前状态#xff1a;大三非科班啃C中#x1f30d;博客主页#xff1a;张小姐的猫~江湖背景快上车#x1f698;#xff0c;握好方向盘跟我有一起打天下嘞#xff01;送给自己的一句鸡汤欢迎来到C专栏~~特殊类设计 (꒪ꇴ꒪(꒪ꇴ꒪ ),我是Scort目前状态大三非科班啃C中博客主页张小姐的猫~江湖背景快上车握好方向盘跟我有一起打天下嘞送给自己的一句鸡汤真正的大师永远怀着一颗学徒的心作者水平很有限如果发现错误可在评论区指正感谢欢迎持续关注 文章目录欢迎来到C专栏~~特殊类设计请设计一个类不能被拷贝请设计一个类不能被继承✨请设计一个类只能在堆上创建对象✨请设计一个类只能在栈上创建对象请设计一个类只能创建一个对象单例模式饿汉模式懒汉模式写在最后请设计一个类不能被拷贝
拷贝只会放生在两个场景中拷贝构造函数以及赋值运算符重载因此想要让一个类禁止拷贝只需让该类不能调用拷贝构造函数以及赋值运算符重载即可
1️⃣C98
将拷贝构造函数与赋值运算符重载只声明不定义并且将其访问权限设置为私有即可
class CopyBan
{
public://...
private://C98CopyBan(const CopyBan);CopyBan operator(const CopyBan);//C11CopyBan(const CopyBan) delete;CopyBan operator(const CopyBan) delete;
};原因
设置成私有如果只声明没有设置成private用户自己如果在类外定义了就可以不 能禁止拷贝了只声明不定义不定义是因为该函数根本不会调用定义了其实也没有什么意义不写反而还简单而且如果定义了就不会防止成员函数内部拷贝了
2️⃣C11 delete除了释放new申请的资源外如果在默认成员函数后跟上delete表示让编译器删除掉该默认成员函数
class CopyBan
{// ...CopyBan(const CopyBan)delete;CopyBan operator(const CopyBan)delete;//...
};请设计一个类不能被继承
C98
因为子类的构造函数被调用时必须调用父类的构造函数初始化父类的那一部分成员但父类的私有成员在子类当中是不可见的所以在创建子类对象时子类无法调用父类的构造函数对父类的成员进行初始化因此该类被继承后子类无法创建出对象。
class NonInherit
{
public:static NonInherit CreateObj(){return NonInherit();}
private://将构造函数设置为私有NonInherit(){}
};C11
final关键字final修饰类表示该类不能被继承
class A final
{// ....
};✨请设计一个类只能在堆上创建对象
只能在堆上创建对象也就是只能通过new操作符创建对象方式如下
析构函数私有化将类的构造函数私有拷贝构造声明成私有或者delete掉。防止别人调用拷贝在栈上生成对象提供一个静态static的成员函数在该静态成员函数中完成堆对象的创建
class HeapOnly
{
public://3.提供一个公有的获取对象的方式对象控制是new出来的static HeapOnly* CreatObj(){return new HeapOnly;}//防止拷贝HeapOnly(const HeapOnly hp) delete;HeapOnly operator(const HeapOnly hp) delete;
private://1.析构函数私有化~HeapOnly(){cout ~HeapOnly endl;}//2.构造函数、拷贝构造都私有化 ~ new 也不可用了HeapOnly():_a(0){}
private:int _a;
};
int main()
{//1.析构函数私有了//HeapOnly hp1;//static HeapOnly hp2;//指针不会创建实际对象所以不会调用到析构//HeapOnly* ptr new HeapOnly;//ptr-Delete(ptr);HeapOnly* hp3 HeapOnly::CreatObj();return 0;
}注意
向外部提供的CreateObj函数必须设置为静态成员函数因为外部调用该接口就是为了获取对象的而非静态成员函数必须通过对象才能调用这就变成鸡生蛋蛋生鸡的问题了C98通过将拷贝构造函数声明为私有以达到防拷贝的目的C11可以在拷贝构造函数后面加上delete表示让编译器将拷贝构造函数删除此时也能达到防拷贝的目的
✨请设计一个类只能在栈上创建对象
方法1️⃣
将构造函数设置为私有防止外部直接调用构造函数在堆上创建对象。向外部提供一个获取对象的static接口该接口在栈上创建一个对象并返回。
class StackOnly
{
public:static StackOnly CreateObj(){StackOnly st;return st;}
private://构造函数私有StackOnly():_a(0){}
private:int _a;
};int main()
{StackOnly st1 StackOnly::CreateObj();return 0;
}但该方法有一个缺陷就是无法防止外部调用拷贝构造函数创建对象
int main()
{StackOnly st1 StackOnly::CreateObj();//调用拷贝构造static StackOnly copy(st1);StackOnly* copy2 new StackOnly(st1);return 0;
}但是我们不能将构造函数设置为私有也不能用delete的方式将拷贝构造函数删除因为CreateObj函数当中创建的是局部对象返回局部对象的过程中势必需要调用拷贝构造函数
此路不通换一条
方法2️⃣
屏蔽operator new函数和operator delete函数或者自己声明一个不实现
class StackOnly
{
public:StackOnly(){}
private://C98void* operator new(size_t size);void operator delete(void* p);//C11//void* operator new(size_t size) delete;//void operator delete(void* p) delete;
};new和delete默认调用的是全局的operator new函数和operator delete函数但如果一个类重载了专属的operator new函数和operator delete函数那么new和delete就会调用这个专属的函数。所以只要把operator new函数和operator delete函数屏蔽掉那么就无法再使用new在堆上创建对象了。
但该方法也有一个缺陷就是无法防止外部在静态区创建对象。
static StackOnly obj; //在静态区创建对象请设计一个类只能创建一个对象单例模式
什么是单例模式
一个类只能创建一个对象即单例模式该模式可以保证系统中该类只有一个实例并提供一个访问它的全局访问点该实例被所有程序模块共享。比如在某个服务器程序中该服务器的配置信息存放在一个文件中这些配置数据由一个单例对象统一读取然后服务进程中的其他对象再通过这个单例对象获取这些配置信息这种方式简化了在复杂环境下的配置管理
单例模式有两种实现方式分别是饿汉模式和懒汉模式
饿汉模式
就是说不管你将来用不用程序启动时就创建一个唯一的实例对象
优点简单没有线程安全问题缺点 1️⃣可能会导致进程启动慢且如果有多个单例类对象实例启动顺序无法控制2️⃣饿汉单例类初始化时任务多会影响程序启动速度
//饿汉模式 : 一开始main函数之前就创建出对象
class MemoryPool
{
public://3、提供一个全局访问点获取单例对象static MemoryPool* GetInstance(){return _pinst;}void* Alloc(size_t n){void* ptr nullptr;return ptr;}void* Dealloc(void* ptr){//...}private://构造函数私有,并且防止拷贝MemoryPool(){}char* _ptr nullptr;//2、提供一个指向单例对象的static指针static MemoryPool* _pinst;//声明
};//在程序入口之前完成单例对象的初始化
MemoryPool* MemoryPool::_pinst new MemoryPool;int main()
{void* ptr1 MemoryPool::GetInstance()-Alloc(10);MemoryPool::GetInstance()-Dealloc(ptr1);return 0;
}懒汉模式
特点延迟加载第一次使用对象再创建实例化对象
优点控制顺序、不影响启动速度缺点 相对复杂线程安全问题、线程安全问题要处理好、
//懒汉模式 : 第一次使用对象时再创建实例对象
class MemoryPool
{
public://3、提供一个全局访问点获取单例对象static MemoryPool* GetInstance(){//第一次来就newif (_pinst nullptr){_pinst new MemoryPool;}return _pinst;}void* Alloc(size_t n){void* ptr nullptr;return ptr;}void* Dealloc(void* ptr){//...}private://构造函数私有,并且防止拷贝MemoryPool(){//...}char* _ptr nullptr;//2、提供一个指向单例对象的static指针static MemoryPool* _pinst;//声明
};//在程序入口之前对象初始化为空
MemoryPool* MemoryPool::_pinst nullptr;int main()
{void* ptr1 MemoryPool::GetInstance()-Alloc(10);MemoryPool::GetInstance()-Dealloc(ptr1);return 0;
}我们发现以上代码中我们new出来好像都没有去释放这就涉及到了单例对象释放问题
一般情况下单例对象是不需要释放的整个程序运行区间都要用到单例对象在进程正常结束后也会资源释放特殊场景对象析构时要进行持久化操作
ps往磁盘文件上写、往数据库中写就是持久化操作
写在最后