拷贝只会放生在两个场景中:拷贝构造函数以及赋值运算符重载,因此想要让一个类禁止拷贝,只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
C++98
将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。
class CopyBan
{// ...private:CopyBan(const CopyBan&);CopyBan& operator=(const CopyBan&);//...
};
原因:
C++11
C++11扩展delete的用法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数
class CopyBan
{// ...CopyBan(const CopyBan&)=delete;CopyBan& operator=(const CopyBan&)=delete;//...
};
实现方式:
class HeapOnly
{
public:static HeapOnly* CreateObj(){return new HeapOnly;}HeapOnly(const HeapOnly&) = delete;// 防拷贝private:// 构造函数私有HeapOnly(){}
};int main()
{//HeapOnly h1;//static HeapOnly h2;//HeapOnly* ph3 = new HeapOnly;HeapOnly* ph4 = HeapOnly::CreateObj();HeapOnly* ph5 = HeapOnly::CreateObj();//HeapOnly copy(*ph4);delete ph4;delete ph5;return 0;
}
// 方法二:析构函数私有
class HeapOnly
{
public:static void DelObj(HeapOnly* ptr){delete ptr;}/*void DelObj(){delete this;}*/private:// 析构函数私有~HeapOnly(){}
};int main()
{HeapOnly h1;static HeapOnly h2;HeapOnly* ph3 = new HeapOnly;delete ph3;HeapOnly::DelObj(ph3);ph3->DelObj();return 0;
}
方法一:同上将构造函数私有化,然后设计静态方法创建对象返回即可
方法二:禁掉operator new
// 方法一
class StackOnly
{
public:static StackOnly CreateObj(){return StackOnly();//StackOnly st;//return st;}StackOnly(const StackOnly&) = delete;// 有时编译器优化可以在构造时不调动拷贝构造,这时才能禁掉拷贝构造void Print(){cout << "Stack Only" << endl;}
private:// 构造函数私有StackOnly(){}
};int main()
{StackOnly h1 = StackOnly::CreateObj();//StackOnly::CreateObj().Print();//static StackOnly h2;//StackOnly* ph3 = new StackOnly;return 0;
}
// 方法二
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;
};int main()
{StackOnly obj = StackOnly::CreateObj();//StackOnly* ptr3 = new StackOnly(obj);//static StackOnly copy(obj);return 0;
}
C++98方式
// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:static NonInherit GetInstance(){return NonInherit();}
private:NonInherit(){}
};
C++11方法
final关键字,final修饰类,表示该类不能被继承。
class A final
{// ....
};
设计模式:
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
单例模式:
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
单例模式有两种实现模式:
// 饿汉 -- 一开始(main函数之前)就创建
class Singleton
{
public:static Singleton* GetInstance(){return _spInst;// 饿汉不好写带参的初始化}void Print();
private:Singleton(){}Singleton(const Singleton&) = delete;// 放类里面是因为构造函数是私有的,类成员才能调用私有的//static Singleton _sInst; // 声明static Singleton* _spInst; // 声明int _a = 0;
};//Singleton Singleton::_sInst; // 定义
Singleton* Singleton::_spInst = new Singleton; // 定义void Singleton::Print()
{cout << _a << endl;
}int main()
{// GetInstance() 可以获得这个Singleton类的单例对象Singleton::GetInstance()->Print();//Singleton st1;//Singleton* st2 = new Singleton;//Singleton copy(*Singleton::GetInstance());return 0;
}
如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好
单例模式一般不需要析构,就只有一个对象,不用怕内存泄漏。进程正常结束时,资源也会还给操作系统。
但在有的情况下也需要析构函数。举个例子:假设析构时需要信息写到文件持久化(我们需要用到垃圾回收类)
// 懒汉 -- 一开始不创建对象,第一调用GetInstance再创建对象class InfoMgr
{
public:static InfoMgr* GetInstance(){// 还需要加锁,这个后面讲 -- 双检查加锁if (_spInst == nullptr){_spInst = new InfoMgr;}return _spInst;}void SetAddress(const string& s){_address = s;}string& GetAddress(){return _address;}// 实现一个内嵌垃圾回收类 class CGarbo {public:~CGarbo() {if (_spInst)delete _spInst;}};// 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象static CGarbo Garbo;private:InfoMgr(){}~InfoMgr(){// 假设析构时需要信息写到文件持久化}InfoMgr(const InfoMgr&) = delete;string _address;int _secretKey;static InfoMgr* _spInst; // 声明
};InfoMgr* InfoMgr::_spInst = nullptr; // 定义
InfoMgr::CGarbo Garbo;
总结:
饿汉特点:简单、初始化顺序不确定,如果有依赖关系就会有问题。饿汉对象初始化慢且多个饿汉单例对象会影响程序启动
懒汉特点:复杂一点、第一次调用时初始化,可以控制初始化顺序,延迟加载初始化,不影响程序启动