内江规划建设教育培训中心网站,丽水企业网站建设,网站建设合同 附件,网络推广培训推荐特殊类的设计和类型转换 特殊类的设计1.设计一个类#xff0c;不能被拷贝2.设计一个类#xff0c;只能在堆上创建对象3.设计一个类#xff0c;只能在栈上创建对象4.设计一个类#xff0c;不能被继承5.单例模式 C的类型转换1. C语言中的类型转换2.C语言类型转换的缺点3.C的强… 特殊类的设计和类型转换 特殊类的设计1.设计一个类不能被拷贝2.设计一个类只能在堆上创建对象3.设计一个类只能在栈上创建对象4.设计一个类不能被继承5.单例模式 C的类型转换1. C语言中的类型转换2.C语言类型转换的缺点3.C的强制类型转换 C中const引用做参数的特殊机制RTTI扩展 特殊类的设计
1.设计一个类不能被拷贝
拷贝只会放生在两个场景中拷贝构造函数以及赋值运算符重载因此想要让一个类禁止拷贝只需让该类不能调用拷贝构造函数以及赋值运算符重载即可。
//1C98将拷贝构造函数与赋值运算符重载只声明不定义并且将其访问权限设置为私有即可。
class A
{
public:A(){}
private:A(A); A operator(const A);int x;
};//2C11扩展delete的用法delete除了释放new申请的资源外如果在默认成员函数后跟上
// delete表示让编译器删除掉该默认成员函数。
class B
{
public:B() {}B(B) delete;B operator(const B) delete;int x;
};2.设计一个类只能在堆上创建对象
两种实现方式
将类的构造函数私有拷贝构造声明成私有。防止别人调用拷贝在栈上生成对象。提供一个静态的成员函数在该静态成员函数中完成堆对象的创建。析构函数私有化提供destory接口释放空间。
//只能在堆上开空间
// 第一种方案构造、拷贝构造私有化提高static返回创建对象指针
class A {
public:static A* get(){return new A;}
private:A(A){}A(){}
};//第二种方案析构函数私有化提供destory接口释放空间
class B {
public:void Destory(){delete this;}
private:~B() //栈上变量函数调用结束前调不动析构{cout ~B endl;}
};3.设计一个类只能在栈上创建对象
将构造函数私有化然后设计静态方法创建对象返回即可
//设计一个类只能在栈上面开空间
//禁用new设计static方法返回局部对象
class C {
public:static C get(){return C();}
private:C(){}void* operator new(size_t s) delete;
}; 4.设计一个类不能被继承
//不能被继承
// 1C98中构造函数私有化派生类中调不到基类的构造函数。则无法继承
class A
{
public:static A GetInstance(){return A();}
private:A(){}
};//2C11方法可用final关键字final修饰类表示该类不能被继承。
class B final
{///
};5.单例模式
单例模式一个类只能创建一个对象即单例模式该模式可以保证系统中该类只有一个实例并提供一个访问它的全局访问点该实例被所有程序模块共享。比如在某个服务器程序中该服务器的配置信息存放在一个文件中这些配置数据由一个单例对象统一读取然后服务进程中的其他对象再通过这个单例对象获取这些配置信息这种方式简化了在复杂环境下的配置管理。 两种设计
饿汉模式在main函数执行前就创建好。
//单例化模式的设计
//饿汉模式在main函数前创建好
//要点1只能右一个实例把构造和拷贝构造私有
//2要在main函数前就创建好我们可以设计成静态成员类似与全局变量
//3提供全局函数返回对象引用或指针
//优点简单
//缺点可能会导致进程启动慢且如果有多个单例类对象实例启动顺序不确定。
class A {
public:static A get(){return a;}//需要什么变量和方法自己添加
private:A() {};A(A) {};A operator(A) delete;//赋值最好禁止掉但自己给自己赋值也影响不大static A a;
};
A A::a; //类外定义懒汉模式需要使用的使用才创建。
//懒汉模式需要的时候创建
//要点1只能有一个实例把构造和拷贝构造私有
//2设计一个静态变量指针初始化为空
//3第一次调用get方法的时候才创建对象
class B {
public:static B* get(){if (b nullptr){b new B();}return b;}
private:B() {};B(B) {};B operator(B) delete;static B* b;
};
B* B::b nullptr;//如果需要在退出时进行数据持久化可以利用析构函数和内部类
//可以手动调用但不调也会在main结束前自动调用
class B {
public:static B* get(){if (b nullptr){b new B();}return b;}static void destory(){if (b) {//数据持久化操作delete b; b nullptr; //懒汉释放空间其实不重要重要的是可以在这个过程进行数据持久化cout destory endl;}}
private:B() {};B(B) {};B operator(B) delete;static B* b;class C {public:~C(){B::destory();}};static C c; //在main函数结束前会调用相应的析构函数
};
B* B::b nullptr;
B::C B::c;C的类型转换
1. C语言中的类型转换
在C语言中如果赋值运算符左右两侧类型不同或者形参与实参类型不匹配或者返回值类型与接收返回值类型不一致时就需要发生类型转化C语言中总共有两种形式的类型转换隐式类型转换和显式类型转换。
隐式类型编译器在编译阶段自动进行能转就转不能转就编译失败显式类型转化需要用户自己处理
void Test()
{int i 1;// 隐式类型转换有关联意义相似double d i;printf(%d, %.2f\n, i, d);int* p i;// 显示的强制类型转换int address (int)p;printf(%x, %d\n, p, address);
}2.C语言类型转换的缺点
隐式类型转化有些情况下可能会出问题比如数据精度丢失显式类型转换将所有情况混合在一起代码不够清晰
因此C提出了自己的类型转化风格注意因为C要兼容C语言所以C中还可以使用C语言的转化风格。 3.C的强制类型转换
标准C为了加强类型转换的可视性引入了四种命名的强制类型转换操作符
static_cast
int main()
{//相近类型意义也相近的转化对应C的int、double、char之间转换double d 12.34;int a static_castint(d);coutaendl;return 0;
}reinterpret_cast
int main()
{//reinterpret_cast用于有一定关联但意义不相似的类型间转换对应C的int与int*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;
}const_cast
void Test()
{//const_cast最常用的用途就是删除变量的const属性方便赋值//注意const_cast属于比较危险的转换volatile const int a 2;int* p const_castint*(a);*p 3;//这里加volatile是因为编译器对const变量有优化可能会放到寄存器也有可能是把a直接替换为2//所以有时内存中数据修改了但是打印发现没有变化加volatile可以强制每次都去内存取cout a endl;
}dynamic_cast
dynamic_cast用于将一个父类对象的指针/引用转换为子类对象的指针或引用(向下转型动态转换)
向上转型子类对象指针 / 引用-父类指针 / 引用(不需要转换赋值兼容规则)向下转型父类对象指针 / 引用-子类指针 / 引用(用dynamic_cast转型是安全的)
使用注意 dynamic_cast只能用于父类含有虚函数的类虚函数我在多态那一文讲过 为什么dynamic_cast 的工作原理是基于运行时的类型信息RTTI。当一个类包含至少一个虚函数时编译器会自动为该类生成一个虚函数表其中包含了所有虚函数的地址。每个该类的对象都会存储一个指向虚函数表的指针。因此通过检查这个指针我们可以确定对象的实际类型。但是如果一个类没有虚函数那么它就不会有虚函数表也就无法在运行时确定其实际类型。在这种情况下使用 dynamic_cast 进行类型转换会导致未定义的行为。 ---- 简而言之就是不知道指针指向的类型不能确保安全性。 dynamic_cast会先检查是否能转换成功能成功则转换不能则返回空。
class A
{
public :virtual void f(){}
};
class B : public A
{};
void fun (A* pa)
{// dynamic_cast会先检查是否能转换成功能成功则转换不能则返回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;
}C中const引用做参数的特殊机制
先看一种常见情况
void fun(vectorint v)
{//不做操作
}int main()
{initializer_listint li { 1, 2, 3 };//这里很明显也很常见是隐式类型转换//只要vectorint支持了initializer_listint做参数的构造即可fun(li);
}再看引用做参数
void fun(vectorint v)
{//不做操作
}int main()
{initializer_listint li { 1, 2, 3 };//这里会报错也很好理解引用底层是指针initializer_listint* 和 vectorint* 不支持隐式转换fun(li);
}最后看const引用做参数
void fun(const vectorint v)
{//不做操作
}int main()
{initializer_listint li { 1, 2, 3 };//这里不报错原因是触发了隐式转换存在对应构造函数为什么//1const修饰后是不支持修改的这个时候隐式转换是安全的//2如果普通引用也支持隐式类型转换的话可能修改关键数据造成错误fun(li);
}RTTI扩展
RTTIRun-time Type identification的简称即运行时类型识别。 C通过以下方式来支持RTTI
typeid运算符dynamic_cast运算符本质是检查虚函数表decltype