1.不能被拷贝的类
一个类不能被拷贝,说明,不能利用已经存在的对象去创建新的对象,要想实现这一点,只需要让拷贝构造函数失效即可。
C++98的方式:将构造函数私有化,并且,只声明,不定义;如果只是不实现,没有将构造函数私有化,防不住有人在类外定义;由于构造函数不能使用,所以也就没有实现的必要了。如下代码:
// 设计一个不能被拷贝的类
class NoCopy
{
public:
NoCopy(){}
private:
NoCopy(const NoCopy& obj);
int _x;
int _y;
string _str;
};
C++11的方式:C++11提供一个关键字delete,用来禁止生成指定的默认成员函数。
class NoCopy
{
public:
NoCopy()
{}
NoCopy(const NoCopy& obj) = delete;
private:
int _x;
int _y;
string _str;
};
2.只能在堆上创建的类
方式一:在堆上创建一个对象,我们只能通过malloc之类的函数,以及new操作符来完成;如果一个类只能在堆上创建,说明只能由我们在堆上申请,那么我们就需要在该类中提供一个对外的接口,通过该接口在堆上创建对象。 那么这个类原来的创建对象的方式就都不能对外提供了,但是也不能禁用,因为new的底层会做两件事情 1.开辟空间;2.调用构造函数完成对象的初始化。所以构造函数需要设置成私有。但是一个类的构造函数可能不止一个,可能会有不同的参数,所以我们可以利用 模版的可变参数 以及 万能引用 来解决这个问题。具体实现可以参考下面这份代码:
// 只能在堆上创建对象
class OnlyHeapCreate
{
public:
template<class... Args>
static OnlyHeapCreate* CreateObj(Args&&... args)
{
return new OnlyHeapCreate(args...);
}
// 需要自己实现以下两个函数,我就不实现了~
OnlyHeapCreate(const OnlyHeapCreate&) = delete;
OnlyHeapCreate& operator=(const OnlyHeapCreate&) = delete;
private:
// 构造私有化
OnlyHeapCreate(){}
OnlyHeapCreate(int x, int y)
:_x(x)
,_y(y)
{}
int _x;
int _y;
vector<int> _v;
};
方式二:将析构函数私有化,在栈上申请的对象销毁的时候,需要调用析构函数清理资源,会发现无法调用私有的析构函数, 所以我们需要提供对外的清理资源的接口,将该接口实现成delete 形式,倒逼该类只能在堆上创建。实现方式如下:
class OnlyHeapCreate
{
public:
OnlyHeapCreate() {}
OnlyHeapCreate(int x, int y)
:_x(x),_y(y)
{}
void Destroy() {delete this;}
private:
// 封掉析构函数
~OnlyHeapCreate() {cout << "~OnlyHeapCreate()" << endl;}
int _x;
int _y;
vector<int> _v;
};
3.只能在栈上创建的类
一个类要想只能在栈上创建对象,那么他就不能通过new在堆上申请空间,所以,我们只需要让new操作符失效即可;如何让new操作符失效呢?new的底层会去调用operator new(),我们没办法直接让new失效,但是我们可以让operator new() 失效,从而间接让new操作符失效,所以该类只能在栈上创建对象了。new的底层还会去调用构造函数初始化对象,但是我们并不清楚会调用哪个构造函数,所以,所以我们可以利用 模版的可变参数 以及 万能引用 来解决这个问题。示例代码如下:
// 只能在Stack
class OnlyStackCreate
{
public:
template<class... Args>
static OnlyStackCreate CreateObj(Args&&... args)
{
return OnlyStackCreate(args...);
}
OnlyStackCreate& operator=(const OnlyStackCreate&) = delete;
// 重载一个类专属的operator new
void* operator new(size_t n) = delete;
private:
// 构造私有化
OnlyStackCreate() {}
OnlyStackCreate(int x, int y)
:_x(x)
,_y(y)
{}
int _x;
int _y;
vector<int> _v;
};
4.不能被继承的类
C++98的方式:将父类的构造函数私有化;因为,子类会继承父类中的成员,创建子类对象时,子类对象中继承自父类的成员需要调用父类的构造函数初始化,如果父类的构造函数是私有的,子类就无法调用,因此,该类就不能被继承。实现方式如下:
class A
{
private:
A() {}
};
C++11的方式:C++11提供了一个final关键字,如果一个类不想被继承,可以使用final关键字表明该类不能被继承。
class A final
{};
5.只能创建一个对象的类
有些类要求只能创建一个对象,在设计模式中,这样的类被称为单例模式;具体来说,单例模式下,一个类只能创建一个对象,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
实现单例模式有两种方法,分别是 懒汉模式 和 饿汉模式。(什么是设计模式?林子大了,什么鸟都有,IT界也是一样,对于相同的问题,总有不同的解决方案,方案多了,就有好的有坏的,将好的方案总结出来,大家都遵守,就形成了设计模式)
为什么需要单例呢?对于程序中一些只需要一份的数据,就可以把这些数据放在一个类里面,再把这个类设计成单例,那么这些数据就只有一份了。简单来说,单例是用来管理程序中只需要一份的数据。
饿汉模式
什么是饿汉模式呢?就好比人吃饭,如果一个人很饿,他肯定希望去吃饭的时候就有干净的碗,直接拿来用就可以了;这里的碗就相当于数据,这个人就相当于程序;所以饿汉模式下,数据的加载在程序运行之前。饿汉模式代码如下:
class Single
{
public:
static Single* GetInstance()
{
return &_sin;
}
void AddStr(const string& s) {_vstr.push_back(s);}
Single(Single const&) = delete;
Single& operator=(Single const&) = delete;
private:
Single(int x = 0, int y = 0, const vector<string>& vstr = { "hello","world" })
:_x(x)
, _y(y)
, _vstr(vstr)
{}
int _x;
int _y;
vector<string> _vstr;
static Single _sin;
};
Single Single::_sin(1, 1, { "大佬","你好" });
- 整个单例中,提供一个静态的相同的类类型的指针,对该类中所有成员的操作都通过该指针来操作,提供一个对外的接口GetInstance()用来获取该指针;由于该指针被static修饰,虽然定义在类中,受到类域的限制,但是它依旧是一个静态的全局变量;
- 对于静态全局变量,如果其初始化表达式是编译时常量或者只涉及其他静态变量(不调用函数),则它们通常在程序启动时的一个早于
main
函数执行的阶段被初始化。这个初始化过程发生在程序的所有动态初始化之前,以及任何线程开始执行之前。 - 在程序执行之前,单例中的数据都准备好了,所以这就是饿汉模式。
饿汉的缺陷
- 如果单例对象数据较多,构造初始化成本较高,那么会影响程序启动的速度。迟迟进不了main函数
- 如果多个单例类有初始化启动有依赖关系,饿汉无法控制。假设:A和B两个单例,假设要求A先初始化,B再初始化,饿汉无法保证。
- 这些问题可以通过懒汉模式来解决。
懒汉模式
什么是懒汉模式呢?同样类比于人吃饭,如果一个人很懒,他每次吃饭都要等到准备吃的时候再去洗碗;这个人还是程序,碗还是数据,只不过数据加载的时机不同,相当于程序跑起来之后再去加载数据。懒汉模式代码如下:
class Single
{
public:
static Single* GetInstance()
{
if (_psin == nullptr)
{
_psin = new Single;
}
return _psin;
}
static void DelInstance()
{
if (_psin)
{
delete _psin;
_psin = nullptr;
}
}
void AddStr(const string& s) {_vstr.push_back(s);}
Single(Single const&) = delete;
Single& operator=(Single const&) = delete;
private:
Single(int x = 0, int y = 0, const vector<string>& vstr = { "yyyyy","xxxx" })
:_x(x)
, _y(y)
, _vstr(vstr)
{}
~Single() {cout << "~Singleton()" << endl;}
int _x;
int _y;
vector<string> _vstr;
static Single* _psin;
};
Single* Single::_psin = nullptr;
- 懒汉模式中,获取实例对象是通过,new操作符来获取的,并且是第一次使用实例对象时,创建对象,而且也只能是在第一次使用实例对象时,创建对象。
- 可以看出来,懒汉模式是一种延时加载的技术。