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
识别指针是指向父类还是子类?
常见面试题