目录
一、不能被拷贝的类
二、只能在堆上创建对象的类
三、只能在栈上创建对象的类
四、不能被继承的类
五、只能创建一个对象的类(单例模式)
下面说几种特殊要求的类的设置,主要学习其中所运用的一些思想,融会贯通
一、不能被拷贝的类
C++98可以将拷贝构造函数与赋值运算符重载只声明不定义,且将其访问权限设置为私有即可
声明不定义且设置为私有,用户就无法调用,且也无法在类外重新定义
C++11直接使用delete关键字即可, 在默认成员函数后跟上 =delete,表示让编译器删除掉该默认成员函数,用法如下所示:
二、只能在堆上创建对象的类
有三种方式创建类对象,要求设计一个只能在堆上创建对象的类
方法①:析构函数设置为私有
此时前两种方式会报错,因为正常定义的对象是必须要调用析构函数的,所以前两种创建对象的方式调用不了析构函数,所以报错
而第三种new创建对象的方式不会自己调用析构,指针ptr需要我们手动调用,所以不会报错
但是这时想手动delete ptr的话,也没办法释放ptr,会报错,因为delete底层也是调用析构函数,ptr同样无法调用析构函数
解决方案就是由于析构函数私有,所以类外调用不了,但是类内是可以调用的,所以在CreHeap类中可以提供一个公有的静态成员函数Delete(),在Delete()中就可以使用delete,从而调用析构函数了,如下图:
同样下面这种方式也是可以的:
因为指针ptr调用Delete()时就传给了this,所以Delete()中delete this也就等同于上面的delete p
方法②:构造函数设置为私有
构造函数私有,即如下图:
但是此时,三种方式都会报错,因为都无法调用构造函数
解决方案就是在CreHeap类中增加一个公有的静态的Create函数,用于在堆上创建对象:
之所以用static,就是因为若不用静态,代码就变为了Create* h3 = Create();Create函数是成员函数,想要调用成员函数就得先有对象,而想要有对象,又必须先有成员函数Create
所以这里就会有冲突,这时加上static,就可以做到Create函数并不是成员函数,就不会再存在刚刚的冲突问题了
这里提供了一个公有的获取对象的方式,可以控制对象是new出来的
最后还有个小bug,如果有人进行拷贝构造,拷贝的对象又是在栈上创建的,所以这里结合第一种特殊类的处理,将拷贝构造函数与赋值运算符重载后面加上delete,避免出现这种情况:
三、只能在栈上创建对象的类
设计一个只能在栈上创建对象,还是可以从构造函数私有这个方向入手,因为构造函数私有,将三种创建对象的方式都限制了,所以依然可以在CreStack类中创建一个Create函数,进行相关设计:
但是这种方法也会有小漏洞,如果调用拷贝构造的话,还是会出现其他两种方式创建对象
例如下图的s2、s3都可以成功创建出对象,一个创建在静态区,一个创建在堆上:
这时有一种思路是也同样在拷贝构造函数与赋值运算符重载后面加上delete,可是这样做不但s2、s3会报错,s1也会报错
因为new底层会调用operator new,所以我们可以在operator new后面加上delete,这样可以限制住s3:
而剩下的static CreStack s2(s1);创建的s2对象没有好的方式处理,算是小缺陷
四、不能被继承的类
C++98中,可以将父类的构造函数私有化,子类无法调用到父类的构造函数,所以无法继承父类:
C++11则可以直接使用关键字final,表示该类不能被继承:
五、只能创建一个对象的类(单例模式)
说单例模式之前,首先谈谈设计模式,设计模式就是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
使用设计模式就是为了代码的可重用性,让代码更容易被他人理解、保证代码可靠性。
我们之前学过的迭代器、适配器都是设计模式的思想
单例模式:
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个 访问它的全局访问点,该实例被所有程序模块共享。
单例模式有两种实现模式:饿汉模式、懒汉模式
饿汉模式
饿汉模式是指:在一开始(main函数开始之前)就创建出对象
单例模式的要求是一个类只能创建一个对象,所以还是老方法,将构造函数私有化,这样在类外就不能无限制的创建对象了;然后在类中设置一个Getsingle函数来满足这个条件
饿汉模式的优点:简单、没有线程安全的问题
饿汉模式的缺点:
①在一个程序中,有多个单例(即有多个单例类),无法控制先后创建初始化的顺序
②饿汉单例类,如果初始化任务非常多的话,会影响程序的启动速度,因为饿汉单例是要在main函数之前完成这些工作的
懒汉模式
懒汉模式是指:第一次使用对象之前再创建示例对象
刚开始默认_sin为nullptr,当第一次创建对象,即调用Getsingle函数时,判断_sin是否为nullptr,如果是表明是第一次创建对象,此时才会创建对象,而当第二次第三次使用时,_sin不为nullptr了,也就不会再创建对象了,也能满足单例模式的要求。
懒汉模式的优点:
①可以控制顺序,想先创建哪个单例类的对象,就先调用哪个单例类的Getsingle函数
②不会影响程序的启动速度,因为懒汉模式并不要求main函数之前完成各种初始化工作
懒汉模式的缺点:
①是相对复杂的
②存在线程安全问题,需要处理好线程安全问题
关于单例的释放问题:
1、一般情况下,单例对象不需要释放的,因为一般整个程序运行期间都可能会用它,单例对象在进程正常结束后,也会资源释放。
2、有些特殊场景需要释放,比如单例对象析构时,要进行一些持久化(往文件、数据库写)操作。
这种情况比较好的处理方法是:可以单独在Single类中写一个内部类Del,定义一个静态的全局的回收对象del,main函数结束后del会调用它的析构函数,从而释放单例对象: