目录
一,特殊类设计
1. 只在堆上创建的类
2. 只允许在栈上创建的类
3. 不能被继承的类
4. 不能被拷贝的类
5. 设计一个类,只能创建一个对象(单例对象)
饿汉模式
懒汉模式
C++11静态成员初始化多线程安全问题
二, C++类型转换
C语言类型转换存在的问题
1. static_cast(近似类型)
2. reinterpret_cast(不相关类型)
3. const_cast
4. dynamic_cast(一般用于多态)
5. RTTI(了解)
结语
嗨!收到一张超美的风景图,愿你每天都能顺心!
一,特殊类设计
1. 只在堆上创建的类
实现方式:1. 将类的构造函数私有,拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。2. 提供一个静态的成员函数,在该静态成员函数中完成堆对象的创建
// 方法一
class HeapOnly
{
public:
void Destory()
{
cout << "~HeapOnly" << endl;
delete this;
}
private:
~HeapOnly()
{}
int i;
};
// 将析构函数设为私有,这样在栈中的对象(不需要手动析构),在栈结束时
// 无法自动调用析构函数。
// 方法二:构造函数私有,仅提供一个creat函数来进行构造
class HeapOnly_2
{
public:
// static 添加原因:我们知道调用成员函数需要对象,但我们这时并没有对象,
// 因此需要用static修饰。(先有鸡先有蛋问题)
static HeapOnly_2* creat_(int b)
{
return new HeapOnly_2(b);
}
// 防止默认拷贝,赋值构造生成,在栈上创建
HeapOnly_2(const HeapOnly_2& b) = delete;
HeapOnly_2& operator=(const HeapOnly_2& b) = delete;
private:
HeapOnly_2(int b)
:i(b)
{}
int i;
};
int main()
{
// HeapOnly it;
HeapOnly* it = new HeapOnly;
it->Destory();
//HeapOnly_2 it2;
HeapOnly_2* it2 = HeapOnly_2::creat_(1);
//HeapOnly_2 it3 = *it2;
//HeapOnly_2 it3(*it2);
delete it2
return 0;
}
2. 只允许在栈上创建的类
class StackOnly
{
public:
static StackOnly creat_(int b)
{
return StackOnly(b); //意味着拷贝构造不能禁止
}
// creat_返回值是右值,我们拷贝构造参数只设置右值,同时默认构造也不会产生
StackOnly(StackOnly&& st)
:_i(st._i)
{}
//但这也没有禁止,move一下就又可以了
private:
// 构造一私有,堆栈上都无法构造创建
StackOnly(int i)
:_i(i){}
StackOnly& operator=(const StackOnly& st) = delete;
int _i;
};
方法同1类似,但这无法限制住,static对象的拷贝。
3. 不能被继承的类
C++98方式,将构造函数私有,派生类无法调用到构造函数导致无法被继承。
// C++98中构造函数私有化,派生类中调不到基类的构造函数。则无法继承
class NonInherit
{
public:
static NonInherit GetInstance()
{
return NonInherit();
}
private:
NonInherit()
{}
};
C++11新方式,在目标类添加关键词——final,表示该类无法被继承。
class A final
{
// ....
};
4. 不能被拷贝的类
class CopyBan
{
// ...
private:
CopyBan(const CopyBan&);
CopyBan& operator=(const CopyBan&);
//...
};
原因:1. 设置成私有:如果只声明没有设置成private,用户自己如果在类外定义了,就可以不能禁止拷贝了2. 只声明不定义:不定义是因为该函数根本不会调用,定义了其实也没有什么意义,不写反而还简单,而且如果定义了就不会防止成员函数内部拷贝了。
class CopyBan
{
// ...
CopyBan(const CopyBan&)=delete;
CopyBan& operator=(const CopyBan&)=delete;
//...
};
5. 设计一个类,只能创建一个对象(单例对象)
设计模式(Design Pattern)是一套 被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。我们学过的有适配器,迭代器,包装器,以及扩展:工厂,观察者模式。下面的单例模式也是 。
饿汉模式
就是说不管你将来用不用,程序启动时就先创建一个唯一的实例对象 。
class SingleCase
{
public:
static SingleCase* GetInstace()
{
return _sc;
}
size_t GetSize()
{
return _vc.size();
}
void Add(const string& str)
{
_mtx.lock();
_vc.push_back(str);
_mtx.unlock();
}
void print()
{
for (auto& it : _vc)
{
cout << it << endl;
}
}
SingleCase(const SingleCase& it) = delete;
SingleCase& operator=(const SingleCase& it) = delete;
private:
SingleCase()
{}
mutex _mtx;
vector<string> _vc;
static SingleCase* _sc;
};
// 声明定义分离,_sc是也在类成员中(能调用类成员),虽然看起来像在类外。
SingleCase* SingleCase::_sc = new SingleCase;
饿汉的缺点:
1. 单例对象的构造可能会占用大量资源,对应用程序的打开比较慢。
2. 不适合多个单例对象之间存在依赖关系生成。如单例A,单例B,必须得先有A再有B,如果在同一个源文件中跟声明顺序相同;但如果不在同一个源文件中,产生顺序就不确定。
优点:代码简单,支持一些高并发场景
懒汉模式
如果单例对象构造十分耗时或者占用很多资源,比如加载插件啊, 初始化网络连接啊,读取文件啊等等,而有可能该对象程序运行时不会用到,那么也要在程序一开始就进行初始化,就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式( 延迟加载)更好。
class SingleCase_lazy
{
public:
static SingleCase_lazy* GetInstace()
{
// 处理大多数情况,提高效率
if (_sc == nullptr)
{
SingleCase_lazy::new_mtx.lock();
// 两程序几乎同一时间进入该
// 作用域时,再进行判断
if (_sc == nullptr)
{
// 加锁防止_sc发生覆盖
_sc = new SingleCase_lazy;
}
SingleCase_lazy::new_mtx.unlock();
}
return _sc;
}
size_t GetSize()
{
return _vc.size();
}
void Add(const string& str)
{
_vmtx.lock();
_vc.push_back(str);
_vmtx.unlock();
}
void print()
{
for (auto& it : _vc)
{
cout << it << endl;
}
}
static void Destory()
{
new_mtx.lock();
if (_sc)
{
cout << "delete _sc" << endl;
}
new_mtx.unlock();
delete _sc;
}
class De
{
public:
~De()
{
Destory();
}
};
SingleCase_lazy(const SingleCase_lazy& it) = delete;
SingleCase_lazy& operator=(const SingleCase& it) = delete;
private:
SingleCase_lazy()
{}
static mutex new_mtx;
mutex _vmtx;
vector<string> _vc;
static SingleCase_lazy* _sc; //无法显示调用析构,我们通过内部类,调用
};
SingleCase_lazy* SingleCase_lazy::_sc = nullptr;
mutex SingleCase_lazy::new_mtx; // 静态函数的锁
SingleCase_lazy::De _de;
总结:设计一个单例对象,我们需要满足:1. 把数据放一个类中,把这个类设计成单例类。 2. 保持进程唯一,需要将拷贝,赋值都禁止。 3. 设计模式是饿汉,还是懒汉
C++11静态成员初始化多线程安全问题
class SingleInstance {
public:
static SingleInstance* GetInstance() {
// 局部静态变量实现的懒汉式单例,C++11后线程安全
// 在C++11之前,不能保证多线程安全
static SingleInstance instance;
return &instance;
}
private:
SingleInstance();
~SingleInstance();
SingleInstance(const SingleInstance& signal) = delete;
SingleInstance& operator=(const SingleInstance& signal) = delete;
};
二, C++类型转换
static_cast、reinterpret_cast、const_cast、dynamic_cast
C语言类型转换存在的问题
1. 隐式类型转化有些情况下可能会出问题:比如数据精度丢失2. 显式类型转换将所有情况混合在一起,代码不够清晰
1. static_cast(近似类型)
int main()
{
double d = 12.34;
int a = static_cast<int>(d);
cout<<a<<endl;
return 0;
}
2. reinterpret_cast(不相关类型)
int main()
{
double d = 12.34;
int a = static_cast<int>(d);
cout << a << endl;
// 这里使用static_cast会报错,应该使用reinterpret_cast
//int *p = static_cast<int*>(a);
int *p = reinterpret_cast<int*>(a);
return 0;
}
3. const_cast
int main()
{
volatile const int i = 10;
int* it = const_cast<int*>(&i);
*it = 2;
cout << i << endl; // 10
cout << *it << endl; // 2
// i为什么没有被修改?答:i被编译器优化,i被10替换到寄存中,并没有在内存中获取
//在C++中,volatile关键字用于告诉编译器该变量的值可能会在程序的控制之外被改变,
//因此编译器不应该对该变量进行优化。这意味着每次访问该变量时,编译器都会从内存
// 中读取最新的值,而不是使用之前缓存的值 (来自gpt的回答)
return 0;
}
4. dynamic_cast(一般用于多态)
2. dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回0。
class A
{
public :
virtual void f(){}
};
class B : public A
{};
void fun (A* pa)
{
// dynamic_cast会先检查是否能转换成功,能成功则转换,不能则返回
B* pb1 = static_cast<B*>(pa);
B* pb2 = dynamic_cast<B*>(pa);
cout<<"pb1:" <<pb1<< endl;
cout<<"pb2:" <<pb2<< endl;
}
int main ()
{
A a;
B b;
fun(&a);
fun(&b);
return 0;
}
5. RTTI(了解)
1. typeid 运算符(获取类型)2. dynamic_cast 运算符3. decltype
结语
本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力