- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28

实现方式2:
析构函数只声明不定义并私有化,析构函数是私有的,那么在对象离开其作用域时,编译器试图调用析构函数时会遇到问题,因为它不能从外部访问私有成员。
class OnlyHeap
{
public:
OnlyHeap()
{}
static OnlyHeap* CreateObject()
{
return new OnlyHeap;
}
private:
OnlyHeap(const OnlyHeap& oh)
{}
~OnlyHeap()
{}
};
int main()
{
OnlyHeap* oh2 = OnlyHeap::CreateObject();
OnlyHeap oh3(*oh2);
return 0;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31

三、请设计一个类,只能在栈上创建对象
class OnlyStack
{
public:
static OnlyStack CreateStack()
{
OnlyStack os;
return os;
}
private:
OnlyStack()
{}
void* operator new(size_t) = delete;
};
int main()
{
OnlyStack os1 = OnlyStack::CreateStack();
OnlyStack* os3;
os3 = new OnlyStack(os1);
return 0;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45

四、请设计一个类,不能被继承
- C++98
由于派生类的构造需要调用基类的构造函数,而这里将构造函数私有后,派生类则不能调用基类的构造函数,那么该类不能被继承。
class CannotInherit
{
public:
static CannotInherit CreateObject()
{
return CannotInherit();
}
private:
CannotInherit()
{}
};
class DerivedClass : public CannotInherit
{
DerivedClass()
{}
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18

- C++11
final修饰类,表示该类不能被继承。

五、请设计一个类,只能创建一个对象(单例模式)
设计模式:
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
单例模式:
一个类只能创建一个对象,即单例模式,该模式可以保证系统中该类只有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息,这种方式简化了在复杂环境下的配置管理。
单例模式有两种实现模式:
5.1 饿汉模式
就是说不管你将来用不用,程序启动时就创建一个唯一的实例对象
饿汉模式的优缺点
- 优点:简单
- 缺点:可能会导致进程启动慢,且如果有多个单例类对象实例启动顺序不确定。
class SingLeton
{
static SingLeton* GetInstance()
{
return &_sl;
}
private:
SingLeton()
{}
SingLeton(const SingLeton& sl) = delete;
SingLeton& operator=(const SingLeton& sl) = delete;
private:
static SingLeton _sl;
};
SingLeton SingLeton::_sl;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
如果这个单例对象在多线程高并发环境下频繁使用,性能要求较高,那么显然使用饿汉模式来避免资源竞争,提高响应速度更好。
那么这里单例模式中的饿汉模式就完成了,需要某个资源只创建一个对象,那么就在单例模式中添加这个资源的成员变量即可。
5.2 懒汉模式
如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式(延迟加载)更好。
懒汉模式的优缺点
- 优点:第一次使用实例对象时,创建对象。进程启动无负载。多个单例实例启动顺序自由控制。
- 缺点:复杂
假设下面懒汉模式下存储的对象是map
class SingLeton
{
public:
static SingLeton* GetInstance()
{
if (_psl == nullptr)
{
_psl = new SingLeton;
}
return _psl;
}
void Insert(const string& key, const string& value)
{
_dict[key] = value;
}
void Print()
{
for (auto dict : _dict)
{
cout << dict.first << ':' << dict.second << endl;
}
cout << endl;
}
private:
SingLeton()
{}
private:
static SingLeton* _psl;
map<string, string> _dict;
};
SingLeton* SingLeton::_psl = nullptr;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
那么为什么需要删除拷贝构造和拷贝赋值呢?
int main()
{
SingLeton::GetInstance()->Insert("want","想");
SingLeton::GetInstance()->Insert("miss", "错过");
SingLeton::GetInstance()->Insert("left", "左边");
SingLeton p(*(SingLeton::GetInstance()));
p.Insert("miss", "想念");
SingLeton::GetInstance()->Print();
p.Print();
return 0;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

因为不删除拷贝构造和拷贝赋值会导致单例不具有唯一性。
删除拷贝构造和拷贝赋值后的懒汉模式。
class SingLeton
{
public:
static SingLeton* GetInstance()
{
if (_psl == nullptr)
{
_psl = new SingLeton;
}
return _psl;
}
void Insert(const string& key, const string& value)
{
_dict.insert(make_pair(key, value));
}
void Print()
{
for (auto dict : _dict)
{
cout << dict.first << ':' << dict.second << endl;
}
}
private:
SingLeton()
{}
SingLeton(const SingLeton& sl) = delete;
SingLeton& operator=(const SingLeton& sl) = delete;
private:
static SingLeton* _psl;
map<string, string> _dict;
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
依照上面的代码还可以看出一个问题,那就是_psl是new出来的,需要delete吗?如何delete呢?
一般来说单例模式是伴随着一整个程序的,程序结束后会自动释放,不排除单例模式用到一半后不需要它了的情况,也有可能是程序结束后需要对数据进行持久化,所以可能需要delete,那么如何delete呢?
首先可以想到这个delete SingLeton::GetInstance()
,但是这个写法太挫了,别人可能看漏或是不理解,在后面继续使用但是模式,那么下面这段代码教大家如何delete这段数据。
首先在SingLeton中定义一个函数static void DelInstance()
用来释放空间,再定义一个内部类InstanceCleaner,而这个类是空类,在SingLeton中声明一个静态的InstanceCleaner类对象,在类外面定义,由于是空类,并不用担心会影响程序启动的速度,在InstanceCleaner的析构函数中调用DelInstance,那么在程序结束后会释放InstanceCleaner对象,调用析构函数,再调用DelInstance释放数据。值得注意的是SingLeton对象中的数据可以在程序结束后,依靠InstanceCleaner来释放数据,也可以自己手动调用DelInstance提前释放数据。
class SingLeton
{
public:
static SingLeton* GetInstance()
{
if (_psl == nullptr)
{
_psl = new SingLeton;
}
return _psl;
}
static void DelInstance()
{
if (_psl)
{
delete _psl;
_psl = nullptr;
cout << "static void DelInstance()" << endl;
}
}
void Insert(const string& key, const string& value)
{
_dict.insert(make_pair(key, value));
}
void Print()
{
for (auto dict : _dict)
{
cout << dict.first << ':' << dict.second << endl;
}
}
class InstanceCleaner
{
public:
InstanceCleaner()
{}
~InstanceCleaner()
{
DelInstance();
}
};
private:
SingLeton()
{}
~SingLeton()
{}
SingLeton(const SingLeton& sl) = delete;
SingLeton& operator=(const SingLeton& sl) = delete;
private:
static SingLeton* _psl;
map<string, string> _dict;
static InstanceCleaner _InstanceCleaner;
};
SingLeton* SingLeton::_psl = nullptr;
SingLeton::InstanceCleaner _InstanceCleaner;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
int main()
{
SingLeton::GetInstance()->Insert("want","想");
SingLeton::GetInstance()->Insert("miss", "错过");
SingLeton::GetInstance()->Insert("left", "左边");
cout << "Hello C++" << endl;
SingLeton::GetInstance()->DelInstance();
return 0;
}
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

实际上上面的懒汉模式还存在线程安全问题,在创建和删除单例的时候,多个线程可能会同时进入,这里我们保证只有第一个调用的线程才可以创建或是删除的单例。
#include
class SingLeton
{
public:
static SingLeton* GetInstance()
{
if (_psl == nullptr)
{
unique_lock<mutex> lock(mtx);
if (_psl == nullptr)
{
_psl = new SingLeton;
}
}
return _psl;
}
static void DelInstance()
{
if (_psl)
{
unique_lock<mutex> lock(mtx);
if (_psl)
{
delete _psl;
_psl = nullptr;
cout << "static void DelInstance()" << endl;
}
}
}
void Insert(const string& key, const string& value)
{
_dict.insert(make_pair(key, value));
}
void Print()
{
for (auto dict : _dict)
{
cout << dict.first << ':' << dict.second << endl;
}
}
class InstanceCleaner
{
public:
InstanceCleaner()
{}
~InstanceCleaner()
{
DelInstance();
}
};
private:
SingLeton()
{}
~SingLeton()
{}
SingLeton(const SingLeton& sl) = delete;
SingLeton& operator=(const SingLeton& sl) = delete;
private:
static SingLeton* _psl;
static mutex mtx;
map<string, string> _dict;
static InstanceCleaner _InstanceCleaner;
};
SingLeton* SingLeton::_psl = nullptr;
SingLeton::InstanceCleaner _InstanceCleaner;
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
这里我们设计一个最简单的单例模式,下面的并且下面的代码是懒汉模式,只有第一次调用函数时,才会创建单例。sl是一个静态对象,只有在第一次调用的时候才会初始化,下面的代码在C++11之前是存在线程安全问题的,这也是C++11之前的缺陷,而C++11之后就不存在线程安全问题了,保证了sl只初始化一次。
class SingLeton
{
public:
static SingLeton& GetInstance()
{
static SingLeton sl;
return sl;
}
private:
SingLeton()
{}
~SingLeton()
{}
SingLeton(const SingLeton& sl) = delete;
SingLeton& operator=(const SingLeton& sl) = delete;
};
class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">
class="hide-preCode-box">
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
结尾
如果有什么建议和疑问,或是有什么错误,大家可以在评论区中提出。
希望大家以后也能和我一起进步!!🌹🌹
如果这篇文章对你有用的话,希望大家给一个三连支持一下!!🌹🌹

data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/qq_55401402/article/details/139021158","extend1":"pc","ab":"new"}">>
评论记录:
回复评论: