1.要求设计一个类,允许其只能在堆上创建
(1).将构造方法声明为私有
(2).将构造方法delete(C++11标准才支持),但是如果delete之后new也是不能正常创建对象的,所以此方法是错误的。#include <iostream>
using namespace std;
class Test
{
public:
static Test* CreateInstance() //要在堆上实例化对象,就得依靠这个静态方法
{
return new Test;
}
//Test(const Test &) = delete;
private:
Test(){ } //将构造函数声明为私有
private:
int a = 0;
int b = 0;
};
void main()
{
Test t = Test::CreateInstance();
}
</pre>
![](https://i-blog.csdnimg.cn/blog_migrate/c52f8682ce54989ba389b21a98a3b2a5.png)
2.要求设计一个类,不允许堆上创建
(1)私有化构造函数,静态方法按值返回栈上创建的对象(按值返回调用的是拷贝构造方法,所以拷贝构造必须公有)
#include <iostream>
using namespace std;
class Test
{
public:
static Test CreateInstance()
{
return Test();
}
Test(const Test& c) {}; //将拷贝构造函数声明为公有
private:
Test() { } //将构造函数声明为私有
private:
int a = 0;
int b = 0;
};
int main()
{
Test t = Test::CreateInstance();
return 0;
}
(2)重载操作符new,并给为私有属性( 考虑:要防止定位new吗? )
#include <iostream>
using namespace std;
class Test {
private:
void* operator new(size_t sz) {};
private:
int a = 0;
int b = 0;
};
int main() {
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/c52f8682ce54989ba389b21a98a3b2a5.png)
3.要求设计一个类,不允许对象拷贝
(1)将拷贝构造方法声明为私有
#include <iostream>
using namespace std;
class Test {
public:
Test()= default;
private:
Test(const Test& t) {};
private:
int a = 0;
int b = 0;
};
int main() {
Test t1;
//Test t2 = t1;
Test t3;
t3 = t1; //对象赋值
return 0;
}
(2).根据C++ 11标准,利用delete关键字删除函数
#include <iostream>
using namespace std;
class Test {
public:
Test() = default;
Test(const Test& t) = delete;
private:
int a = 0;
int b = 0;
};
int main() {
Test t1;
//Test t2 = t1;
Test t3;
t3 = t1; //对象赋值
return 0;
}
(3).通过继承构造方法是私有属性的类,从而达到不能拷贝的目的
#include <iostream>
#include <boost/utility.hpp>
using namespace std;
class Test : public boost::noncopyable {
};
int main() {
Test t1;
//Test t2 = t1; //不能拷贝构造,派生类要拷贝构造就得先拷贝构造基类
return 0;
}
原理如下:
boost中将noncopyable的拷贝构造函数声明为了私有
class noncopyable
{
protected:
noncopyable() {}
~noncopyable() {}
private: // emphasize the following members are private
noncopyable( const noncopyable& );
const noncopyable& operator=( const noncopyable& );
};
![](https://i-blog.csdnimg.cn/blog_migrate/c52f8682ce54989ba389b21a98a3b2a5.png)
4.请设计一个类,不能被继承
(1).构造函数私有化,派生类中调不到基类的构造函数,则无法继承
class NonInherit
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
private:
NonInherit()
{}
};
(2).借助C++ 11中的final关键字,final修饰类,表示该类不能被继承。
class A final
{
// ....
};
![](https://i-blog.csdnimg.cn/blog_migrate/c52f8682ce54989ba389b21a98a3b2a5.png)
5.设计一个类,只能创建一个对象(单例模式)
我们首先实现只允许创建一个对象,不考虑线程安全
//只允许创建一个对象
#include <iostream>
using namespace std;
class Test {
public:
static Test* creatInstance() {
if (soleInstance == nullptr)
soleInstance = new Test;
return soleInstance;
}
private:
Test() {};
private:
static Test* soleInstance;
};
Test* Test::soleInstance = nullptr; //静态成员类外声明
int main() {
Test* p = Test::creatInstance();
Test* p1 = Test::creatInstance();
Test* p2 = Test::creatInstance();
return 0;
}
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
单例模式可分为懒汉模式和饿汉模式
饿汉模式:程序启动时就创建一个唯一的实例对象
class Singleton
{
public:
static Singleton* GetInstance()
{
return &m_instance;
}
private:
// 构造函数私有
Singleton(){};
// C++98 防拷贝
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
// or
// C++11
//Singleton(Singleton const&) = delete;
//Singleton& operator=(Singleton const&) = delete;
private:
static Singleton m_instance;
};
Singleton Singleton::m_instance;
缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定
懒汉模式实际上相对于饿汉模式是一种延迟加载的方式
如果单例对象构造十分耗时或者占用很多资源,比如加载插件,初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。
优点:第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控制。
#include <iostream>
using namespace std;
class Singleton {
public:
static Singleton* getInstance() {
if (nullptr == _singleton)
_singleton = new Singleton;
return _singleton;
}
private:
Singleton() {};
private:
static Singleton* _singleton;
};
Singleton* Singleton::_singleton = nullptr;
以上两种单例模式的实现不是线程安全的,创建一个对象本身不是原子操作,主要包含两个操作:分配内存空间,引用变量指向内存。在创建实例的静态方法中必须保证创建对象操作的原子性,才能保证线程安全和逻辑的正确性。
以懒汉模式为例如下:
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
class Singleton {
public:
static Singleton* GetInstance() {
// 注意这里一定要使用Double-Check的方式加锁,才能保证效率和线程安全
if (nullptr == _singleton) {
_mt.lock();
if (nullptr == _singleton) {
_singleton = new Singleton();
}
_mt.unlock();
}
return _singleton;
}
private:
Singleton() {};
private:
static Singleton* _singleton;
static mutex _mt;
};
Singleton* Singleton::_singleton = nullptr;
mutex Singleton::_mt;
void thread_func(int n)
{
for (int i = 0; i < n; ++i)
cout << Singleton::GetInstance() << endl;
}
int main() {
thread t1(thread_func, 10);
thread t2(thread_func, 10);
t1.join();
t2.join();
cout << Singleton::GetInstance() << endl;
cout << Singleton::GetInstance() << endl;
return 0;
}
Double-Check的方式加锁,既能保证效率又能保证线程安全