时间:2022-08-22 09:23:47 | 栏目:C代码 | 点击:次
拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。
class CopyBan { //... private: CopyBan(const CopyBan&); CopyBan& operator=(const CopyBan&); //... };
原因:
设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了
只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。
C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。
class CopyBan { //... CopyBan(const CopyBan&) = delete; CopyBan& operator = (const CopyBan&) = delete; //... };
实现方式:
将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。
提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建
class HeapOnly { public: //提供一个static公有函数创建对象,对象创建的都在堆上 static HeapOnly* CreateObject() { return new HeapOnly; } private: HeapOnly(){} //C++98 防拷贝 只声明,不实现 HeapOnly(const HeapOnly&); //C++11 HeapOnly(const HeapOnly&) = delete; };
方法一:同上将构造函数私有化,然后设计静态方法创建对象返回即可。
class StackOnly { public: StackOnly(){} public: //C++11 void* operator new(size_t size) = delete; void operator delete(void* p) = delete; private: //C++98 防调用 void* operator new(size_t size); void operator delete(void* p); };
屏蔽new
因为new在底层调用void* operator new(size_t size)函数,只需将该函数屏蔽掉即可。注意:也要防止定位new
class StackOnly { public: StackOnly(){} private: void* operator new(size_t size); void operator delete(void* p); };
//C++98这种方式不够直接 //这里是可以继承的,但是Derive不能创建对象,因为Derive的构造函数必须要调用父类NonOnherit构造,但是NonInherit的构造函数私有了,私有在子类不可见,那么这里继承不会报错,继承的子类创建对象会报错 class NonInherit { public: static NonInherit GetInstance() { return NonInherit(); } private: NonInherit() {} };
final关键字,final修饰类,表示该类不能被继承。
class A final { //... };
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。为什么会产生设计模式这样的东西呢?就像人类历史发展会产生兵法。最开始部落之间打仗时都是人拼人的对砍。后来春秋战国时期,七国之间经常打仗,就发现打仗也是有套路的,后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
定义一个全局对象,大家都能用,也能保证单例,但这种方式存在很大的缺陷,你要让大家都能用,这个对象就只能定义在一个.h,如果这个.h在多个.cpp包含,那么链接会报错。全局静态,只在当前文件可见,不再是同一个对象,每个xxx.cpp中各自是一个对象。 extern可以使链接不报错,但不能保证全局只有唯一一个v,可能某个地方又重新定义了一个变量v 所以我们可以在.h中声明,在.cpp中定义,声明和定义分离。否则在.h中定义,多个cpp包含就会有多份。
某些类, 只应该具有一个对象(实例), 就称之为单例.
在很多服务器开发场景中, 经常需要让服务器加载很多的数据 (上百G) 到内存中. 此时往往要用一个单例的类来管理这些数据
单例模式有两种实现模式:饿汉实现方式和懒汉实现方式
[洗碗的例子]: 吃完饭, 立刻洗碗, 这种就是饿汉方式. 因为下一顿吃的时候可以立刻拿着碗就能吃饭. 吃完饭, 先把碗放下, 然后下一顿饭用到这个碗了再洗碗, 就是懒汉方式. 懒汉方式最核心的思想是 “延时加载”. 从而能够优化服务器的启动速度.
在main函数之前,一开始就创建对象
.h:
//饿汉模式:main函数之前,一开始就创建对象 //全局只要唯一的Singleton实例对象,那么他里面的成员也就是单例的 class Singleton { public: //3.提供一个获取单例对象的static成员函数 static Singleton& GetInstance(); //如果vector对象是私有,想访问,只能再封装一层 //void PushBack(int x) //{ // _v.push_back(x); //} vector<int> _v; private: //vector<int> _v; //1.构造函数私有化,不能随意创建对象 Singleton() {} //防拷贝 Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; //2.类里面声明一个static Singleton对象,在cpp定义这个对象 //保证全局只要一个唯一对象 //这里的static类比的是全局变量,只是受类域的限制,没有改变链接属性 static Singleton _sinst; };
.cpp:
#include "Singleton.h" //定义 Singleton Singleton::_sinst; Singleton& Singleton::GetInstance() { return _sinst; }
优点:简单
缺点:main函数之前创建初始化的。如果单例对象的构造函数中要做很多工作,可能会导致进程启动慢。
且如果有多个单例类对象实例启动顺序不确定。
如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。
饿汉式在应用启动时就创建了 实例,饿汉式是线程安全的,是绝对单例的。懒汉式在对外提供的获取方法被调用时会实例化对象。在多线程情况下,懒汉模式不是线程安全的。
第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控制。
//定义 Singleton* Singleton::_spinst = nullptr; mutex Singleton::_mtx; Singleton& Singleton::GetInstance() { //双检查加锁 提高效率 if (_spinst == nullptr) { _mtx.lock(); if (_spinst == nullptr) { //第一次调用 _spinst = new Singleton; } _mtx.unlock(); } return *_spinst; } void Singleton::DelInstance() { if (_spinst != nullptr) { _mtx.lock(); if (_spinst != nullptr) { delete _spinst; _spinst = nullptr; } _mtx.unlock(); } }
#pragma once #include <vector> #include <iostream> #include <mutex> using namespace std; //懒汉模式:第一次调用GetInstance时,才会创建初始化单例对象 //相对于饿汉,不存在可能会导致启动慢的问题,也可以控制顺序依赖的问题了 class Singleton { public: //3.提供一个获取单例对象的static成员函数 static Singleton& GetInstance(); //如果vector对象是私有,想访问,只能再封装一层 //void PushBack(int x) //{ // _v.push_back(x); //} vector<int> _v; //或实现一个内嵌垃圾回收类 class CGarbo { public: ~CGarbo() { if (Singleton::_spinst) delete Singleton::_spinst; } }; //定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象 static CGarbo Garbo; private: //vector<int> _v; //1.构造函数私有化,不能随意创建对象 Singleton() {} //防拷贝 Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; //2.类里面声明一个static Singleton对象,在cpp定义这个对象 //保证全局只要一个唯一对象 static Singleton* _spinst; static mutex _mtx; };