目录
设计一个类,不能被拷贝
通过前面的学习,我们知道拷贝会发生在两种情况下,拷贝构造和赋值运算符重载,因此,想要一个类禁止拷贝,只需要让这个类不能调用拷贝构造函数和赋值重载即可。
C++98
将拷贝构造函数与赋值运算符重载只声明不定义,并且将其访问权限设置为私有即可。
class Config
{
//...
private:
Config(const Config& con);
Config& operator=(const Config& con);
};
C++11
C++11中,在默认成员函数后面加上“=delete”,就可以让改编译器删除该成员函数。
class Config
{
//...
Config(const Config& con) = delete;
Config& operator=(const Config& con) = delete;
};
设计一个类,只在堆上创建对象
class HeapOnly
{
public:
//设计成静态成员函数,不需要对象就能调用
static HeapOnly* CreateObj()
{
return new HeapOnly();
}
private:
//构造函数不能delete,因为CreateObj还需要用到,只能设为私有
HeapOnly() {};
//C++98
HeapOnly(const HeapOnly&);
//C++11
HeapOnly(const HeapOnly&) = delete;
HeapOnly& operator=(const HeapOnly&) = delete;
};
int main()
{
HeapOnly* h = HeapOnly::CreateObj();
return 0;
}
还有人提出一种奇葩的写法:将析构函数私有化,
class HeapOnly
{
public:
void Release()
{
this->Release();
}
private:
//析构函数私有化
~HeapOnly()
{
}
};
int main()
{
HeapOnly* obj = new HeapOnly;
obj->Release();
return 0;
}
这样,构造、拷贝构造、赋值因为不能调用析构函数而不能创建对象,但是,在堆上创建的对象可以调用Release,Release再调用析构函数。
设计一个类,只能在栈上创建对象
class StackOnly
{
public:
//不能把拷贝构造delete,这里传值返回需要用到
static StackOnly CreateObj()
{
return StackOnly();
}
// 禁掉operator new可以把下面用new 调用拷贝构造申请对象给禁掉
// StackOnly obj = StackOnly::CreateObj();
// StackOnly* ptr3 = new StackOnly(obj);//这里的new不会调全局的operator new,而是调自己实现的
// operator new是针对这个类的局部的
void* operator new(size_t size) = delete;
void operator delete(void* p) = delete;
private:
StackOnly()
:_a(0)
{}
private:
int _a;
};
设计一个类,不能被继承
C++98
// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
private:
NonInherit()
{}
};
C++11
final关键字,final修饰类,表示该类不能被继承。
class A final
{
// ....
};
设计一个类,只能创建一个对象(单例模式)
单例模式,就是希望全局只有一个对象,不能被创建出两份,比如,配置信息的类,只要有一份就行。
单例模式有两种设计模式:
饿汉模式:不管你用不用,程序启动时就会创建一个唯一的实例对象。
所谓饿汉模式,就是早早地创建对象,在进入main函数之前就创建。
class ConfigInfo
{
public:
static ConfigInfo* GetInstance()
{
return &_sInfo;
}
string GetIp()
{
return _ip;
}
void SetIp(const string& ip)
{
_ip = ip;
}
private:
//把构造私有,这样就不能在外部创建
ConfigInfo() {};
//拷贝构造和赋值delete
ConfigInfo(const ConfigInfo&) = delete;
ConfigInfo& operator=(const ConfigInfo&) = delete;
private:
string _ip = "127.0.0.1";
int _port = 80;
//...
//声明,不属于类对象,是在静态区,只不过受类域限制
static ConfigInfo _sInfo;
};
//饿汉模式,在进入main之前就创建对象
//定义
ConfigInfo ConfigInfo::_sInfo;
int main()
{
cout << ConfigInfo::GetInstance() << endl;
ConfigInfo::GetInstance()->SetIp("196.13.1");
cout << ConfigInfo::GetInstance()->GetIp() << endl;
return 0;
}
但是,饿汉模式存在两个问题:
1.如果很多单例类都是饿汉模式,或者有些单例类初始化资源很多,导致程序启动慢,迟迟进不了main函数。
2.如果两个单例类有初始化依赖关系,饿汉也无法解决。比如,A类和B类是单例类,A单例要连接数据库,B单例要用A单例初始化数据库。
为了解决以上问题,又有了懒汉模式。
懒汉模式:第一次调用GetInstance时创建对象。
懒汉模式不在main函数前创建对象,这就不会导致程序启动慢,另外,尽管A单例类和B单例类存在依赖关系,我可以在main函数中通过改变A和B的GetInstance创建对象的顺序,来适应它们的依赖关系,懒汉完美解决了饿汉的问题。
class ConfigInfo
{
public:
static ConfigInfo* GetInstance()
{
//定义局部静态单例对象,只会创建一次
static ConfigInfo _sInfo;
return &_sInfo;
}
string GetIp()
{
return _ip;
}
void SetIp(const string& ip)
{
_ip = ip;
}
private:
//把构造私有,这样就不能在外部创建
ConfigInfo()
{
cout << "ConfigInfo()" << endl;
};
//拷贝构造和赋值delete
ConfigInfo(const ConfigInfo&) = delete;
ConfigInfo& operator=(const ConfigInfo&) = delete;
private:
string _ip = "127.0.0.1";
int _port = 80;
};
这种方式有一点小小的缺陷,C++11之前,多线程调用GetInstance,局部静态单例对象构造初始化,无法保证进程安全,编译器支持C++11才可以用,因为C++11对这个问题单独处理了。但是现在编译器基本都支持C++11,所以这种写法很完美!
那在C++11之前,我们是怎么实现懒汉的呢?(了解)
和饿汉模式类似,但是是定义一个静态指针_pInfo=nullptr,指针在main函数前初始化问题不大,初始化一个指针而已,然后第一次调用GetInstance的时候,判断_pInfo是否为空,为空则创建对象,等下一次调用GetInstance的时候,_pInfo不为空,就不会创建对象。为了保证多线程调用时的安全,需要加锁。为了避免每次调用GetInstance都要加锁,使用双检查机制加锁。
//C++11前的懒汉模式
class ConfigInfo
{
public:
static ConfigInfo* GetInstance()
{
//只有第一次调用时,_pInfo才不等于空,才会进入
//多线程调用需要考虑线程安全的问题
//这样,当t1 t2两个线程来的时候,只有一个线程能进
//但是这样会每次调用GetInstace的时候,都会加这个锁,但其实没必要,只用第一次加锁就行
/*unique_lock<mutex> lock(_mtx);
if (_pInfo == nullptr)
{
_pInfo = new ConfigInfo;
}*/
//所以,使用双检查机制
if (_pInfo == nullptr) //性能
{
unique_lock<mutex> lock(_mtx);
if (_pInfo == nullptr)//线程安全
{
_pInfo = new ConfigInfo;
}
}
return _pInfo;
}
string GetIp()
{
return _ip;
}
void SetIp(const string& ip)
{
_ip = ip;
}
private:
//把构造私有,这样就不能在外部创建
ConfigInfo()
{
cout << "ConfigInfo()" << endl;
};
//拷贝构造和赋值delete
ConfigInfo(const ConfigInfo&) = delete;
ConfigInfo& operator=(const ConfigInfo&) = delete;
private:
string _ip = "127.0.0.1";
int _port = 80;
static ConfigInfo* _pInfo;
static mutex _mtx;
};
ConfigInfo* ConfigInfo::_pInfo = nullptr;
mutex ConfigInfo::_mtx;
int main()
{
cout << ConfigInfo::GetInstance() << endl;
cout << ConfigInfo::GetInstance() << endl;
ConfigInfo::GetInstance()->SetIp("196.13.1");
cout << ConfigInfo::GetInstance()->GetIp() << endl;
return 0;
}