🥒特殊类设计
2022-6-10
| 2023-8-2
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
Property

设计一个类,不能被拷贝

拷贝只会出现在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
C++98:将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可
  • 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就不能禁止拷贝了
  • 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了
 
C++11C++11拓展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后加上=delete,即表示让编译器删除掉该默认成员函数。
经典的防拷贝:unique_ptr、thread线程、mutex锁、istream、ostream
 
 

设计一个类,不能被继承

C++98:将该类的构造函数私有化即可,因为子类的构造函数被调用时,必须调用父类的构造函数初始化父类的那一部分成员,但无论是何种继承方式,父类的私有成员在子类是不可见的,所以创建子类对象时子类就无法调用父类的构造函数对父类的成员初始化,继而该类无法被继承:
C++98的这种方式其实不够彻底,因为这个类仍然可以被继承(编译器不会报错),只不过被继承后无法实例化出对象而已
 
C++11:使用final关键字,final修饰类,表示该类不能被继承。此时就算继承后没有创建对象也会编译出错。
 

设计一个类,只能在堆上创建对象

平时创建对象,常见有如下三种在不同区域创建对象的方式:
既然只能在堆上创建对象,也就是只能通过new操作创建对象,有如下两种方式。
法一:
  1. 将类的构造函数私有,拷贝构造声明成私有,防止别人调用拷贝在栈上生成对象
  1. 提供一个静态的成员函数,在该静态成员函数中完成对象的创建
  • 没有必要对赋值运算符重载设置为私有&&只声明不实现或者加上=delete(禁掉),因为赋值运算符重载是两个已经存在的对象,既然已经存在,那势必这俩对象就已经在堆区创建好了,所以它们之间进行赋值操作并不会出错。除非你不想用,那你可以把赋值运算符重载给禁掉。
  • 而拷贝构造是拿一个已经存在的对象去构造一个对象,此对象是先前未存在的,且拷贝构造后是在栈上的,自然不符合题意,因此需要把拷贝构造给禁掉,而赋值运算符重载不需要禁掉
 
法二:将析构函数私有化
C++是一个静态绑定的语言。在编译过程中,所有的非虚函数调用都必须分析完成。即使是虚函数,也需检查可访问性。因此, 当在栈上生成对象时,对象会自动调用析构函数释放对象,也就说析构函数必须可以访问 ,否则编译出错。而在堆上生成对象,由于析构函数由程序员调用(通过使用delete),所以不一定需要析构函数。
既然析构函数私有化,如何delete你new出的资源呢?
因为delete操作会调用析构函数,而析构函数已经被置为私有了,那就无法调用,为了解决此问题,只需要在类的内部提供一个静态成员函数,既然类外不能调用私用成员,但是类里是可以调用的,因此在此成员函数中调用析构函数完成delete操作
当然,也可以使用delete this来释放new出的资源:
delete this--对象请求自杀,执行后不能再访问this指针。换句话说,你不能去检查它、将它和其他指针比较、和NULL比较、打印它、转换它,以及其它的任何事情。
 

请设计一个类,只能在栈上创建对象

法一:
  • 将构造函数设为私有,防止外部直接调用构造函数在堆上创建对象
  • 提供静态成员函数,内部调用私有的构造函数完成对象的创建
此法有一缺陷,无法避免外部调用拷贝构造函数在静态区、堆区……创建对象
但是又不能将拷贝构造函数设为私有,因为上述的静态成员函数CreateObj是传值返回,势必会调用拷贝构造函数,为了解决此问题,推出法二。
 
法二:
  1. 把构造函数设为公有
  1. 屏蔽operator new函数和operator delete函数
newdelete默认调用的是全局的operator new函数和operator delete函数,但如果一个类重载了专属的operator new函数和operator delete函数,那么newdelete就会调用这个专属的函数。所以只要把operator new函数和operator delete函数屏蔽掉,那么就无法再使用new在堆上创建对象了。
上述做法虽然成功避免了在堆区创建对象,但是无法避免在静态区或全局创建对象。
 

设计一个类,只能创建一个对象(单例模式)

一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
单例模式有两种实现模式:饿汉模式懒汉模式
 
饿汉模式
饿汉模式就是不管将来用不用,程序启动时就创建一个唯一的实例对象。
实现方式如下:
  1. 将构造函数私有化,并将拷贝构造和拷贝赋值设为私有或删除,防止外部随意创建对象或拷贝
  1. 在类里创建一个static静态对象的指针,在进入程序入口之前就完成单例对象的初始化
  1. 提供一个static静态成员函数,用来获取单例对象的指针
  1. 将拷贝构造和拷贝赋值私有化,防止类外调用拷贝构造创建对象
 
比如现在有一个信息管理的类,需要保证进程里只有一份这样的信息,那么就需要把它设定为单例,整体框架和上面差不多其实,具体实现细节有所变动罢了:
如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。
 
 
懒汉模式
如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。
还是以上述信息管理的类为例,懒汉模式的实现方式如下:
  1. 将构造函数置为私有,并将拷贝构造函数和赋值运算符重载函数设为私有或删除,防止外部创建或拷贝对象。
  1. 提供一个指向单例对象的static指针,并在程序入口之前先将其初始化为空。
  1. 提供一个static静态成员函数,只有当static指针为空时才初始化(也就是第一次调用此成员函数才创建对象),最后返回单例对象的指针
  1. 将拷贝构造和拷贝赋值私有化,防止类外调用拷贝构造创建对象
懒汉模式这样写是有问题的,还需要加锁(双检查加锁),饿汉模式不需要加锁
  • C++
  • 运行时类型识别异常处理
    目录