c++11
unique_ptr
防拷贝
shared_ptr / weak_ptr:
引用计数,支持拷贝
面试
手写shared_ptr
各种ptr的特性对比, 不会问定制删除器和weak_ptr,但是问shared_ptr时,可以往这边延展.
单例
保证一写数据在一个进程中,只有一份,并且方便访问修改.
饿汉模式
在main函数之前就创建了对象
#include <iostream>
#include <vector>
#include <string>
#include <thread>
#include <mutex>
#include <time.h>
using namespace std;class Singleton
{
public:static Singleton* GetInstance(){return _ins;}void Add(const string& str){_mtx.lock();_v.push_back(str);_mtx.unlock();}void Print(){_mtx.lock();for (auto& e : _v){cout << e << endl;}cout << endl;_mtx.unlock();}
private://限制类外面随意创建对象Singleton() {}
private:mutex _mtx;vector<string> _v; //想让vector的数据,在全局只有唯一一份static Singleton* _ins;
};Singleton* Singleton::_ins = new Singleton;int main()
{//Singleton s1;//static Singleton s2;Singleton::GetInstance()->Add("张三" );Singleton::GetInstance()->Add("李四");Singleton::GetInstance()->Print();int n = 10;thread t1([n]() {for (size_t i = 0; i < n; i++){Singleton::GetInstance()->Add("t1线程" + to_string(rand())); //添加随机数}});thread t2([n]() {for (size_t i = 0; i < n; i++){Singleton::GetInstance()->Add("t2线程" + to_string(rand())); //添加随机数}});t1.join();t2.join();Singleton::GetInstance()->Print();return 0;
}
张三
李四张三
李四
t1线程41
t1线程18467
t1线程6334
t1线程26500
t2线程41
t2线程18467
t2线程6334
t1线程19169
t1线程15724
t1线程11478
t1线程29358
t2线程26500
t2线程19169
t2线程15724
t2线程11478
t1线程26962
t2线程29358
t1线程24464
t2线程26962
t2线程24464
懒汉模式
第一次访问实例对象时才创建
class Singleton
{
public:static Singleton* GetInstance(){if (_ins == nullptr) //提高效率{_imtx.lock();if (_ins == nullptr) //线程安全和只new一次{_ins = new Singleton;}_imtx.unlock();}return _ins;}void Add(const string& str){_vmtx.lock();_v.push_back(str);_vmtx.unlock();}void Print(){_vmtx.lock();for (auto& e : _v){cout << e << endl;}cout << endl;_vmtx.unlock();}
private://限制类外面随意创建对象Singleton() {}
private:mutex _vmtx;vector<string> _v; //想让vector的数据,在全局只有唯一一份static Singleton* _ins;static mutex _imtx;
};Singleton* Singleton::_ins = nullptr;
mutex Singleton::_imtx;int main()
{srand(time(0));int n = 10;thread t1([n]() {for (size_t i = 0; i < n; i++){Singleton::GetInstance()->Add("t1线程" + to_string(rand())); //添加随机数}});thread t2([n]() {for (size_t i = 0; i < n; i++){Singleton::GetInstance()->Add("t2线程" + to_string(rand())); //添加随机数}});t1.join();t2.join();Singleton::GetInstance()->Print();return 0;
}
static Singleton* GetInstance() //懒汉模式 另一种写法{//C++11之前,不能保证初始化静态局部对象的线程安全问题(不安全)//C++11之后,可以保证初始化静态局部对象的线程安全问题(安全)static Singleton inst;return &inst;}
懒汉和饿汉的优缺点
饿汉:
缺点:
1. 如果单例对象很大,main函数之前就要申请,第一暂时不需要使用却占用资源, 第二程序启动会变慢.
2. 如果两个单例都是饿汉,并相互有依赖关系, 要求单例1先创建, 单例2再创建(例如数据库先启动,缓存后启动), 饿汉无法控制谁先创建的顺序
优点:
比懒汉简单
懒汉优点:解决了饿汉的缺点
懒汉缺点:比饿汉复杂
懒汉有线程安全问题, 饿汉没有线程安全问题
保证单例对象回收
#include <iostream>
#include <vector>
#include <string>
#include <thread>
#include <mutex>
#include <time.h>
using namespace std;class Singleton
{
public:static Singleton* GetInstance(){if (_ins == nullptr) //提高效率{_imtx.lock();if (_ins == nullptr) //线程安全和只new一次{_ins = new Singleton;}_imtx.unlock();}return _ins;}//不需要显式地释放单例对象//因为一般全局都要使用单例对象. 所以不需要显式地释放. 程序结束时最后会系统释放//有些特殊场景,想显式的释放一下static void DelInstance(){_imtx.lock();if (_ins != nullptr){delete _ins;_ins = nullptr;}_imtx.unlock();}//单例对象回收class GC //内部类.写成外部类也可以{public:~GC(){DelInstance();}};static GC _gc; //定义静态全局gc对象~Singleton(){//持久化-永久存下来// // 要求程序在结束前, 将数据写到文件中.//单例对象析构时做持久化.} void Add(const string& str){_vmtx.lock();_v.push_back(str);_vmtx.unlock();}void Print(){_vmtx.lock();for (auto& e : _v){cout << e << endl;}cout << endl;_vmtx.unlock();}
private://限制类外面随意创建对象Singleton() {}
private:mutex _vmtx;vector<string> _v; //想让vector的数据,在全局只有唯一一份static Singleton* _ins;static mutex _imtx;
};Singleton* Singleton::_ins = nullptr;
mutex Singleton::_imtx;Singleton::GC Singleton::_gc;
int main()
{srand(time(0));int n = 10;thread t1([n]() {for (size_t i = 0; i < n; i++){Singleton::GetInstance()->Add("t1线程" + to_string(rand())); //添加随机数}});thread t2([n]() {for (size_t i = 0; i < n; i++){Singleton::GetInstance()->Add("t2线程" + to_string(rand())); //添加随机数}});t1.join();t2.join();Singleton::GetInstance()->Print();return 0;
}
t1线程41
t2线程41
t1线程18467
t2线程18467
t2线程6334
t2线程26500
t2线程19169
t1线程6334
t1线程26500
t1线程19169
t1线程15724
t1线程11478
t1线程29358
t1线程26962
t1线程24464
t2线程15724
t2线程11478
t2线程29358
t2线程26962
t2线程24464
补充:防拷贝
//无论饿汉,懒汉,都要防拷贝Singleton(const Singleton& s) = delete; //不封拷贝构造无法保证单例//Singleton s(*Singleton::GetInstance());Singleton& operator=(const Singleton& s) = delete;
类型转换
static_cast
reinterpret_cast
const_cast
dynamic_cast
前三种为了解决c语言使用不规范 ~,第四种是c++相较于c语言新增的。
class A
{
public:virtual void f() {}
};class B : public A
{};void fun(A* pa, const string& s)
{std::cout << "pa" << s << std::endl;B* pb1 = (B*)pa;B* pb2 = dynamic_cast<B*>(pa); //安全的 std::cout << "[强制转换]pb1:" << pb1 << endl;std::cout << "[dynamic_cast转换]pb2:" << pb2 << endl << endl;//检查父类指针,如果原本指向父类对象,就转失败;//如果原本指向子类对象,就转成功
}int main()
{//double d = 12.34;//int a = static_cast<int>(d); //相似类型可以转, 整形不可转成指针 隐式类型转换//cout << a << endl;//int *p = reinterpret_cast<int*>(a); //重新解释的转换 ,不可用static_cast 强制类型转换//const int a = 2;//int* p = const_cast<int*>(&a); //对应c语言的下一行//int* p2 = (int*)&a;//*p = 3;//cout << a << endl; //2//cout << *p << endl; //3//cout << *p2 << endl;//加了const后,编译器优化,把a存到寄存器, 从寄存器取a. ////4 .dynamic_cast 动态转换 只有c++有//指针 / 引用可转 , 对象不可转A a;B b;//bb = (B)aa; //对象不可转//dynamic转换是安全的, 强制类型转换是不安全的fun(&a, "指向父类对象");fun(&b, "指向子类对象");return 0;
}
pa指向父类对象
[强制转换]pb1:0000009495EFF498
[dynamic_cast转换]pb2:0000000000000000pa指向子类对象
[强制转换]pb1:0000009495EFF4B8
[dynamic_cast转换]pb2:0000009495EFF4B8
RAII ---- 资源 请求 is 初始化
RTTI ---- :Run-time Type identifification的简称,即:运行时类型识别。
cout << typeid(a).name() << endl; //class A
识别指针是指向父类还是子类?
常见面试题