目录
前言
对于特殊类的实现,我们首先要会设计一个不能被拷贝的类,其思路就是让类不能调用拷贝构造函数以及赋值运算符操作即可。
在C++98中,可以只声明不定义构造函数以及赋值运算符操作,并将他们设为私有防止在类外定义它们。
class CopyBan
{
// ...
private:
CopyBan(const CopyBan&);
CopyBan& operator=(const CopyBan&);
//...
};
在C++11中,扩展了delete写法,delete除了释放new申请的资源外,如果在默认成员函数后跟上=delete,表示让编译器删除掉该默认成员函数。
class CopyBan
{
// ...
CopyBan(const CopyBan&)=delete;
CopyBan& operator=(const CopyBan&)=delete;
//...
};
一、HeapOnly
设计一个只能在堆上创建对象的类:
方法一、析构函数私有化
我们可以将析构设为私有,这样就很巧妙的规避了析构函数的调用,new的对象是不会调用析构的,因此为了删除,在类内部添加一个Destroy函数。
方法二、构造函数私有化
我们将构造函数私有化,然后自己提供一个Create函数用于new一个对象,但是需要在前面加static,这样在类外即使最开始没有对象也可以通过调用CreateObj来创造一个对象。同时要把拷贝构造和赋值运算符操作都delete,因为在外部调用它们时创造的对象是在栈上的。
//构造函数私有化
class HeapOnly
{
public:
static HeapOnly* CreateObj() //加了satic才能在外部不用声明对象直接创造对象
{
return new HeapOnly;
}
private:
HeapOnly()
{
//...
}
HeapOnly(const HeapOnly& hp) = delete;
HeapOnly& operator = (const HeapOnly& hp) = delete;
};
int main()
{
HeapOnly hp1; //栈
static HeapOnly hp2; //静态区
HeapOnly* hp3 = HeapOnly::CreateObj(); //堆
return 0;
}
二、StackOnly
设计一个只能在栈上创建的类:
我们对其构造函数私有化并提供自己的创建对象的函数,同时delete new操作符,防止如StackOnly* hp4 = new StackOnly(hp3)情况发生。
class StackOnly
{
public:
static StackOnly CreateObj() //加了satic才能在外部不用声明对象直接创造对象
{
StackOnly st;
return st;
}
private:
StackOnly()
{
//...
}
//对一个类实现专属operator new
void* operator new(size_t size) = delete;
};
int main()
{
//StackOnly hp1;
//static StackOnly hp2;
//StackOnly* hp3 = new StackOnly;
StackOnly hp3 = StackOnly::CreateObj();
StackOnly copy(hp3);
// StackOnly* hp4 = new StackOnly(hp3);
return 0;
}
三、NonInherit
设计一个不能被继承的类:
方法一、final修饰
class A final
{
//...
};
方法二、构造函数私有化
class NonInherit
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
private:
NonInherit(){}
};
当把构造函数私有化时,对于派生类是不可见的,而派生类又必须调用父类的构造函数,因此就无法继承了。
四、单例模式
单例模式即设计一个类,只能创建一个对象
4.1 饿汉模式
就是说不管你将来用不用,程序启动时(main函数之前)就创建一个唯一的实例对象。
//饿汉模式
namespace hungry
{
class Singleton
{
public:
//2.提供获取单例对象的接口函数
static Singleton& GetInstance()
{
return _sinst;
}
private:
//1.构造函数私有化
Singleton()
{
}
map<string, string> _dict;
static Singleton _sinst; //类里声明
// 3、防拷贝
Singleton(const Singleton& s) = delete;
Singleton& operator=(const Singleton& s) = delete;
};
Singleton Singleton::_sinst; //类外定义
}
缺点:
- 如果单例对象初始化内容很多,就会影响启动速度
- 如果两个单例类互相有依赖关系,这个模式是不能保证初始化的先后顺序的
4.2 懒汉模式
//懒汉模式
namespace lazy
{
class Singleton
{
public:
// 2、提供获取单例对象的接口函数
static Singleton& GetInstance()
{
if (_psinst == nullptr)
{
// 第一次调用GetInstance的时候创建单例对象
_psinst = new Singleton;
}
return *_psinst;
}
// 一般单例不用释放。
// 特殊场景:1、中途需要显示释放 2、程序结束时,需要做一些特殊动作(如持久化)
static void DelInstance()
{
if (_psinst)
{
delete _psinst;
_psinst = nullptr;
}
}
void Add(const pair<string, string>& kv)
{
_dict[kv.first] = kv.second;
}
void Print()
{
for (auto& e : _dict)
{
cout << e.first << ":" << e.second << endl;
}
cout << endl;
}
class GC
{
public:
~GC()
{
lazy::Singleton::DelInstance();
}
};
private:
// 1、构造函数私有
Singleton()
{
// ...
}
~Singleton()
{
cout << "~Singleton()" << endl;
// map数据写到文件中
FILE* fin = fopen("map.txt", "w");
for (auto& e : _dict)
{
fputs(e.first.c_str(), fin);
fputs(":", fin);
fputs(e.second.c_str(), fin);
fputs("\n", fin);
}
}
// 3、防拷贝
Singleton(const Singleton& s) = delete;
Singleton& operator=(const Singleton& s) = delete;
map<string, string> _dict;
// ...
static Singleton* _psinst;
static GC _gc;
};
Singleton* Singleton::_psinst = nullptr;
}
优点:
- 延迟加载:懒汉模式实现了延迟加载,即在需要使用实例时才进行创建。这样可以节省系统资源,提高程序的性能和效率。
- 节省内存:由于懒汉模式在首次使用时创建实例,因此避免了在程序启动时就创建实例并占用内存的情况。这对于一些资源消耗较大的对象来说尤为重要。
缺点:
- 线程安全性:懒汉模式在多线程环境下可能存在线程安全问题。如果多个线程同时调用获取实例的方法,可能会导致创建多个实例,违反了单例模式的原则。
- 需要加锁:为了解决线程安全问题,懒汉模式需要在获取实例的方法中进行加锁操作。这会引入额外的开销,并可能导致性能下降。
- 非线程安全的优化:为了避免每次获取实例都进行加锁操作,可以对懒汉模式进行线程安全的优化,例如使用双重检查锁定(Double-Checked Lock