响应式网站开发技术,wordpress修改首页,网站建设从哪几个情况去判,对于网站链接优化有哪些建议我们在C语言中经常会使用到强制类型转换#xff0c;例如指针和整形之间的转换是最为常见的#xff0c;但是
在C中#xff0c;C设计师认为这种强制类型转换是不安全的#xff0c;所以在C标准中加入了四种强制
类型转换风格#xff0c;这就是我将要介绍的强制类型转换。
在某…我们在C语言中经常会使用到强制类型转换例如指针和整形之间的转换是最为常见的但是
在C中C设计师认为这种强制类型转换是不安全的所以在C标准中加入了四种强制
类型转换风格这就是我将要介绍的强制类型转换。
在某些场景中我们可能需要一些特殊的类来让我们的代码能够更加符合场景比如只能在栈
上创建对象或者只能在堆上常见对象等等场景而其中尤为出名的一种特殊类也被纳入设计
模式中那就是单例模式。1. 特殊类设计
a. 不能能被拷贝的类
拷贝只会放生在两个场景中拷贝构造函数以及赋值运算符重载因此想要让一个类禁止拷贝只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。 由于这两个函数是默认成员函数你不写它会自动生成所以我们需要显式地写出来并且是具有私有属性
class A
{
public:A(int a 0):_a(a){}
private:A(const A a);A operator(const A a);int _a;
};而在C11中delete作用于函数有着新的作用那就是不生成该函数所以
class A
{
public:A(int a 0):_a(a){}A(const A a) delete;A operator(const A a) delete;
private:int _a;
};这个的话私有共有无所谓了。
b. 只能在堆上创建的对象
我们想要创建一个对象无非就这两种方式
int main()
{A aa;//栈上A* paa new(A);//堆上return 0;
}现在要让我们不能在栈上创建对象一定是从构造函数找出发点首先构造函数不能直接地被调用说明他应该是私有成员但是他是私有成员后我们new一个对象的时候也需要构造函数这样也不能在堆上创建对象了这个时候我们就需要在类中再写一个函数由于类内是没有私有公有一说的所以类内成员函数可以随意的调用类内的任意成员函数所以
class A
{
public:static A* getObj(int a 0){A* pa new A(a);return pa;}
private:A(int a 0) :_a(a){};int _a;
};这样写完之后我们发现我们要使用这个函数先得需要一个对象但是我们又创建不了对象自相矛盾了所以我们需要将这个函数设置为静态的
class A
{
public:static A* getObj(int a 0){A* pa new A(a);return pa;}
private:A(int a 0) :_a(a){};int _a;
};还有问题如果是这样呢 所以还需要禁用拷贝构造和赋值重载
class A
{
public:static A* getObj(int a 0){A* pa new A(a);return pa;}
private:A(int a 0) :_a(a){};A(const A a) delete;A operator(const A a) delete;int _a;
};c. 只能在栈上创建对象
我们肯定还是在构造函数上找突破口
class A
{
public:static A getObj(){A a;return a;}private:A(int a 0) :_a(a) {};int _a;
};而对于这样的方式有人会说会不会效率变慢了如果对象需要在堆上开辟空间的话不要忘了我们可是有右值引用的 这个现象需要在VS2019或者更低版本的编译器上进行VS2022优化有点高看不到这种现象。
#include iostream
using namespace std;class A
{
public:static A getObj(){A a;return a;}A(A a){cout 右值拷贝构造 endl;swap(_a, a._a);}private:A(int a 0) :_a(new int[a]) {};int* _a;
};int main()
{A a A::getObj();//A* pa new A;return 0;
}d. 只能创建一个对象单例模式
关于这一个设计这是一种设计模式因为它经常出现在各种编程场景中被人们广泛使用。 设计模式
设计模式Design Pattern是一套被反复使用、多数人知晓的、经过分类的、代码设计
经验的总结。为什么会产生设计模式这样的东西呢就像人类历史发展会产生兵法。最开
始部落之间打仗时都是人拼人的对砍。后来春秋战国时期七国之间经常打仗就发现打
仗也是有套路的后来孙子就总结出了《孙子兵法》。孙子兵法也是类似。使用设计模式的目的
为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。 设计模式使代码编写真
正工程化设计模式是软件工程的基石脉络如同大厦的结构一样。单例模式
一个类只能创建一个对象即单例模式该模式可以保证系统中该类只有一个实例并提
供一个访问它的全局访问点该实例被所有程序模块共享。比如在某个服务器程序中该
服务器的配置信息存放在一个文件中这些配置数据由一个单例对象统一读取然后服务
进程中的其他对象再通过这个单例对象获取这些配置信息这种方式简化了在复杂环境下
的配置管理。而关于单例模式的设计有两种方法应对于不同的场景
1). 饿汉模式
饿汉模式他的意思就是在进入main函数之前这个对象就已经被创建好了。 我们首先要想想什么变量是在进入main函数之前就已经被创建好了呢那肯定是全局变量啊但是全局变量你要知道它使用起来是有风险的在多文件的时候尤为明显可能会产生重定义的风险。所以我们是这样设计的
class Singleton
{
public:static Singleton getExa(){return sl;}void Add(const pairstring, int kv){sl._hash.insert(kv);}void Print(){for (auto e : sl._hash){cout e.first : e.second endl;}cout endl;}
private:mapstring, int _hash;Singleton() {};Singleton(const Singleton sl) delete;Singleton operator(const Singleton sl) delete;static Singleton sl;
};Singleton Singleton::sl;int main()
{Singleton::getExa().Add({ hello, 2 });Singleton::getExa().Add({ world, 1 });Singleton::getExa().Add({ xxx, 1 });Singleton::getExa().Add({ yyy, 10 });Singleton::getExa().Print();return 0;
}2). 懒汉模式
这种设计模式就是在对象被调用的时候再创建
class Singleton
{
public:static Singleton* getExa(){if (sl nullptr)sl new Singleton;return sl;}void Add(const pairstring, int kv){sl-_hash.insert(kv);}void Print(){for (auto e : sl-_hash){cout e.first : e.second endl;}cout endl;}
private:mapstring, int _hash;Singleton() {};Singleton(const Singleton sl) delete;Singleton operator(const Singleton sl) delete;static Singleton* sl;
};Singleton* Singleton::sl nullptr;int main()
{Singleton::getExa()-Add({ hello, 2 });Singleton::getExa()-Add({ world, 1 });Singleton::getExa()-Add({ xxx, 1 });Singleton::getExa()-Add({ yyy, 10 });Singleton::getExa()-Print();return 0;
}在饿汉模式中对象的销毁在进程结束之后会自动销毁并且由于是在栈上所以无法手动销毁对象但是在懒汉模式中该对象是从堆上开辟而来有人就会想到那这个对象究竟什么时候释放呢程序也不会自动调用析构函数释放啊其实我们不需要担心它内存泄露的问题因为进程如果是正常退出的话操作系统会帮我们做这一件事的如果进程是异常退出的话那需要担心的地方也不是这里了而是为什么进程异常退出了。但是我们非要设计进程结束释放资源的功能也是可以的
class Singleton
{
public:static Singleton* getExa(){if (sl nullptr)sl new Singleton;return sl;}void Add(const pairstring, int kv){sl-_hash.insert(kv);}void Print(){for (auto e : sl-_hash){cout e.first : e.second endl;}cout endl;}//创建一个内嵌的自定义类并且创建该自定义类的栈上的对象那么在进程结束的//时候该对象会调用它的析构函数我们在它的析构函数中释放sl即可。class GC{public:~GC(){if (sl){delete sl;sl nullptr;}}};private:mapstring, int _hash;Singleton() {};Singleton(const Singleton sl) delete;Singleton operator(const Singleton sl) delete;static Singleton* sl;static GC gc;
};Singleton* Singleton::sl nullptr;
Singleton::GC Singleton::gc;要注意自定义类型的静态成员变量在类内声明类外定义
3). 两者的场景
饿汉模式
如果这个单例对象在多线程高并发环境下频繁使用性能要求较高那么显然使用饿汉
模式来避免资源竞争提高响应速度更好。懒汉模式
如果单例对象构造十分耗时或者占用很多资源比如加载插件啊 初始化网络连接啊
读取文件啊等等而有可能该对象程序运行时不会用到那么也要在程序一开始就进行
初始化就会导致程序启动时非常的缓慢。 所以这种情况使用懒汉模式延迟加载更
好。在这里要说明一点饿汉模式不需要担心线程安全问题而懒汉模式需要需要加锁关于这一方面的问题日后再谈因为我也不会…。
1. C语言类型转换
我们在编写C/C代码的过程中肯定会不可避免地使用到强制类型转换例如自己计算结构体某一成员偏移量指针之间指针与整形之间转换的场景我们都是这么写
// 偏移量
#define offsetof( type,member) (char *)((type *)0-member)
它的类型转换分为两种一种是隐式的
double j 1.1;
int a j;但是很明显这种转换会丢失精度还有就是显式的
int main()
{int a 0;int* pa a;int b (int)pa;return 0;
}对于以上类型转换会存在一些很明显的缺陷
1. 转换的可视性比较差所有的转换形式都是以一种相同形式书写难以跟踪错误的转换
2. 隐式类型转化有些情况下可能会出问题比如数据精度丢失
3. 显式类型转换将所有情况混合在一起代码不够清晰
4. 隐式类型的转换在出现错误后一般在编译时才出现所以基于这种情况C标准提出了四种类型转换格式。
2. C类型转换
a. static_cast
static_cast用于非多态类型的转换静态转换编译器隐式执行的任何类型转换都可用static_cast但它不能用于两个不相关的类型进行转换
int main()
{double d 12.34;int a static_castint(d);coutaendl;return 0;
}b. reinterpret_cast
reinterpret_cast操作符通常为操作数的位模式提供较低层次的重新解释用于将一种类型转换为另一种不同的类型
int main()
{double d 12.34;int a static_castint(d);cout a endl;// 这里使用static_cast会报错应该使用reinterpret_cast//int *p static_castint*(a);int *p reinterpret_castint*(a);return 0;
}c. const_cast
const_cast最常用的用途就是删除变量的const属性方便赋值
void Test ()
{const int a 2;int* p const_cast int*(a );*p 3;couta endl;
}d. dynamic_cast
dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(动态转换) 向上转型子类对象指针/引用-父类指针/引用(不需要转换赋值兼容规则) 向下转型父类对象指针/引用-子类指针/引用(用dynamic_cast转型是安全的) 注意
dynamic_cast只能用于父类含有虚函数的类 dynamic_cast会先检查是否能转换成功能成功则转换不能则返回0
class A
{
public :
virtual void f(){}
};
class B : public A
{};
void fun (A* pa)
{
// dynamic_cast会先检查是否能转换成功能成功则转换不能则返回0
B* pb1 static_castB*(pa);
B* pb2 dynamic_castB*(pa);
coutpb1: pb1 endl;
coutpb2: pb2 endl;
}
int main ()
{A a;B b;fun(a);fun(b);return 0;
}