这个我们前面已经说过了
有两种方法
将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。
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:
void Destory()
{
delete this;
}
private:
~HeapOnly()
{
//...
}
};
int main()
{
HeapOnly hp1;
static HeapOnly hp2;
HeapOnly* hp3 = new HeapOnly;
hp3->Destory();
return 0;
}
我们可以明显的注意到,前两个是报错的
这里需要注意的是,我们也要封住拷贝构造函数。因为可能会通过拷贝构造函数去创建栈区上的对象
class HeapOnly
{
public:
static HeapOnly* CreatObj()
{
return new HeapOnly;
}
private:
HeapOnly()
{
//...
}
//C++11的方法,拷贝构造必须封
HeapOnly(const HeapOnly& hp) = delete;
//赋值运算符重载可封可不封
HeapOnly& operator=(const HeapOnly& hp) = delete;
};
int main()
{
HeapOnly hp1;
static HeapOnly hp2;
HeapOnly* hp3 = HeapOnly::CreatObj();
//封住拷贝构造是为了防止下面的情形
HeapOnly hp4(*hp3);
return 0;
}
我们显然看到,我们这个只能在堆区创建了
如下代码所示
为了让只在栈上创建对象,我们肯定不可以封住析构函数,因为栈区的一定会调用析构函数。
所以我们只能从构造函数下手,于是我们可以将构造函数私有化,然后提供一个接口去接收这个对象。
这里还需要注意的是,我们的new也可以是拷贝构造。但是我们是不可以封住拷贝构造的,因为我们返回一个对象的时候,需要调用拷贝构造。
我们注意到operator new是在全局中的一个函数重载,所以我们可以利用它会优先访问类域的特性,我们在类里面实现一个专属的operator new,然后我们就可以将这个函数给删掉。也就是无法使用new了。
最终我们就彻底屏蔽了堆区的创建。
但是这里我们其实还有一个静态区如果调用拷贝构造怎么办?这里如果还有屏蔽掉静态就比较麻烦了。虽然无法彻底解决问题,但是也已经可以了。
class StackOnly
{
public:
static StackOnly CreatObj()
{
return StackOnly();
}
private:
StackOnly()
{
//...
}
//对一个类实现专属的operator new
void* operator new(size_t size) = delete;
};
int main()
{
StackOnly st1;
static StackOnly st2;
StackOnly* st3 = new StackOnly;
StackOnly st4 = StackOnly::CreatObj();
StackOnly st5(st4);
StackOnly* st6 = new StackOnly(st4);
return 0;
}
我们可以注意到,确实只可以在栈上创建对象
由于继承的派生类必须调用基类的构造函数。所以我们可以封住构造函数
// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
private:
NonInherit()
{}
};
final关键字可以禁止某个虚函数无法被重写
final还可以修饰类,表示该类不能被继承。
class A final
{
// ....
};
饿汉模式就是:一开始(main函数之前)就创建单例对象
问题:
饿汉模式的实现如下
要注意,为了防止拷贝构造或者赋值运算符重载去创建新的对象,要将他们给封住
class Singleyton
{
public:
static Singleyton* GetInstance()
{
return &_sinst;
}
private:
Singleyton()
{}
//禁止拷贝
Singleyton(const Singleyton& s) = delete;
//禁止赋值
Singleyton& operator=(const Singleyton& s) = delete;
map<string, string> _dict;
//可以放到静态区,注意这里只是声明
static Singleyton _sinst;
};
//定义,这里的_sinst是在类里面声明的,所以可以调用类里面的构造函数
//就像一个函数在类里面声明,在外面定义是可以直接使用类里面的成员变量一样的
Singleyton Singleyton::_sinst;
int main()
{
Singleyton* s1 = Singleyton::GetInstance();
Singleyton* s2 = Singleyton::GetInstance();
Singleyton* s3 = Singleyton::GetInstance();
Singleyton* s4 = Singleyton::GetInstance();
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
cout << s4 << endl;
return 0;
}
我们先看下面的代码
懒汉模式其实就是一开始先不创建创建对象,而是在第一次去获取这个对象的时候去创建的。
对于单例模式它一般是不需要释放的。但是在一些特殊场景还是需要的。这时候我们需要显示释放,程序结束时,需要做一些持久化的动作(写到文件中去)
如下中,是写了一个Delnstance函数去释放这块资源的。
为了方便,我们可以专门去写一个类,定义一个全局对象去专门来释放它。
namespace lazy
{
class Singleyton
{
public:
static Singleyton* GetInstance()
{
//如果还没有创建,就创建一下这个对象
if (_psinst == nullptr)
{
_psinst = new Singleyton;
}
return _psinst;
}
//一般而言单例不用释放,
//在一些特殊场景: 1. 需要显示释放, 2. 程序结束时,需要做一些特殊动作(如持久化)
static void DelInstance()
{
if (_psinst)
{
delete _psinst;
_psinst = nullptr;
}
}
~Singleyton()
{
cout << "~Singleyton()" << 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);
}
}
void ADD(string s1, string s2)
{
_dict[s1] = s2;
}
void Print()
{
for (auto& e : _dict)
{
cout << e.first << ":" << e.second << endl;
}
}
private:
Singleyton()
{}
//禁止拷贝
Singleyton(const Singleyton& s) = delete;
//禁止赋值
Singleyton& operator=(const Singleyton& s) = delete;
map<string, string> _dict;
//可以放到静态区,注意这里只是声明
static Singleyton* _psinst;
};
Singleyton* Singleyton::_psinst = nullptr;
}
class GC
{
public:
~GC()
{
lazy::Singleyton::DelInstance();
}
};
GC g;
int main()
{
lazy::Singleyton* s1 = lazy::Singleyton::GetInstance();
s1->ADD("xxx", "111");
s1->ADD("yyy", "222");
s1->ADD("zzz", "333");
s1->ADD("abc", "444");
s1->Print();
//s1->DelInstance();
return 0;
}
运行结果为
不过我们也可以将这个类写到内部类里面,这样的话这个也是可以的
namespace lazy
{
class Singleyton
{
public:
class GC
{
public:
~GC()
{
lazy::Singleyton::DelInstance();
}
};
static Singleyton* GetInstance()
{
//如果还没有创建,就创建一下这个对象
if (_psinst == nullptr)
{
_psinst = new Singleyton;
}
return _psinst;
}
//一般而言单例不用释放,
//在一些特殊场景: 1. 需要显示释放, 2. 程序结束时,需要做一些特殊动作(如持久化)
static void DelInstance()
{
if (_psinst)
{
delete _psinst;
_psinst = nullptr;
}
}
~Singleyton()
{
cout << "~Singleyton()" << 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);
}
}
void ADD(string s1, string s2)
{
_dict[s1] = s2;
}
void Print()
{
for (auto& e : _dict)
{
cout << e.first << ":" << e.second << endl;
}
}
private:
Singleyton()
{}
//禁止拷贝
Singleyton(const Singleyton& s) = delete;
//禁止赋值
Singleyton& operator=(const Singleyton& s) = delete;
map<string, string> _dict;
//可以放到静态区,注意这里只是声明
static Singleyton* _psinst;
static GC _gc;
};
Singleyton* Singleyton::_psinst = nullptr;
Singleyton::GC Singleyton::_gc;
}
int main()
{
lazy::Singleyton* s1 = lazy::Singleyton::GetInstance();
s1->ADD("xxx", "111");
s1->ADD("yyy", "222");
s1->ADD("zzz", "333");
s1->ADD("abc", "444");
s1->Print();
//s1->DelInstance();
return 0;
}