网站如何做延迟加载,免费自己建立网站,wordpress前后几篇,做钉子出口哪个网站好一、对象封装
在面向对象编程中#xff0c;首要的事情就是如何进行对象的封装。说的直白一些#xff0c;就是如何设计类或者是结构体。许多开发者看过不少的书#xff0c;也学过很多的设计方法#xff0c;更看过很多别人的代码。那么如何指导自己进行对象的封装呢#xf…一、对象封装
在面向对象编程中首要的事情就是如何进行对象的封装。说的直白一些就是如何设计类或者是结构体。许多开发者看过不少的书也学过很多的设计方法更看过很多别人的代码。那么如何指导自己进行对象的封装呢 在书籍中学过设计原则如才分析过的六大原则也从书本上知道了继承是面向对象设计的一个重要方式。还有前面提到的内联以及学习过的内存对齐、依赖注入注意不是依赖倒置等等那么如何才能在对象封装的过程中灵活的运用这些知识呢这就需要一个封装设计从整体到细节从粗芜到精细的过程。
二、封装的过程
在对象的封装过程中可以划分成三个层次 1、宏观设计 需要考虑这个类的功能它如何与其它功能类或者管理类进行交互。这里就需要考虑一些设计原则如单一职责、开闭原则、迪米特法则等 。正如前面分析的首先从宏观要保证这个类或者结构体的功能要尽量自耦合尽量只提供对外的输出接口这个当然是理想的尽量减少对内的输入接口少继承多组合等等。 那么有人会问这个体现在哪儿只是一种大脑的风暴其实这就是在前文“架构设计杂谈”中提到的从思想到规则产出。这就需要一些工具如一些UML的设计工具一些流程图的工具等等。开发者可以在这些设计的具现化过程中把自己的思想逐步形成一个设计的实体形成规则。
2、局部设计 局部设计其实就是进一步的细化在类初步被设计出来已后如何设计对外交互接口和头文件暴露的程度就提到了日程来。一般来说一个封装的对象类或结构中函数分成两大类一种是自用的不对外暴露另外一种是对外的也就是常说的对外接口。对内的函数可以随时调整当然这种情况还是要尽量避免因为它只能影响自己但对外接口一般相对是比较稳定的。所以这就要求在设计时需要考虑对外接口的控制。 接口的控制一般考虑是尽量要齐全比如看一些库或框架时可以看到一些从0NN上了十的量级的参数。这在早期的C程序中是不可避免的即使到了C11以后仍然在模板编程中可以看到类似的一些机制。另外一个就是是否跨平台上需要考虑ABI的兼容性设计。最后还要考虑的是不要随便的引入其它的依赖这个非常重要也非常典型。举一个例子在开发之初可能需要某个三方的库但后来不用了却忘记删除相关的头文件注意是头文件因为别的文件很容易想到就剔除了这其实就引入了一些莫名的风险。包括可以使用依赖注入来实现一些反转控制等等。 另外一个是头文件的暴露 这里有一个原则就是尽量不要暴露变量只暴露接口函数。其实就是出于简单原则和安全原则了。它的实现方法也很多如简单的抽象出一个虚接口类但这可能就ABI无法兼容了、Pimpl、设计一个专门的接口类等等。 还有头文件和 cpp文件的是否分享的问题这一般就涉及到是否为模板、是否有大量的内联函数、内部类、是否普遍公用的头文件等等。模板一般是建议都写在头文件中毕竟大多数的编译器都不支持模板的分离编译。有大量的内联函数的类一般也建议写到头文件中。其它情况自己可以根据情况斟酌。
3、细节设计 其实上面的两层设计可能大多数开发者还是觉得没有什么实际意义毕竟开发的程序有几个是大规模的应用很多都是自己搞来搞去的。但下面的细节设计就非常重要了。 a) 变量设计 变量的设计从访问限制上分为三类public公有,protected保护,private私有。一般来说不涉及到继承的只考虑公有和私有两类。从主流的设计思想出发变量一般是不建议公有的即变量一律私有化通过接口和控制变量的访问只有某些静态成员变量可能需要公有。 从变量的类型来说有静态、静态局部和普通变量这个一般来说比较好区别使用静态一般都有一些特殊的要求比如直接暴露变量并且唯一一个常见的就是单例。 从CV限定上来看变量如果是一个恒定值或者只读访问那么使用const限制如果是和硬件通信或者多线程通信中使用一些易变的变量 需要增加volatile限定。不过这里需要注意的是有些平台对volatile有一些不同的设定。比如在WINDOWS和Linux平台上对其的使用限定就不完全相同。但是这一个一般在嵌入式中可能更有实用性在上层应用上就属于小众应用了。 变量的初始化C11以前少量两个以内 的可以使用构造初始化函数内赋值但一般是建议使用列表列表初始化在C11以后增加了默认赋值即在定义成员变量时就赋值。可以综合运用。另外还有下面提到的委托构造和继承构造。 在变量设计中还有一个比较少见但比较重要的应用就是mutable它可以和 const函数共同使用来达到一些特殊的修改动作要谨慎使用。 inline是C17后才提供的可以根据其实功能来和实际场景匹配。如果需要进行指针控制建议使用智能指针。
b) 构造和析构函数 构造函数和析构函数原则上来说尽量少干活特别是涉及到内存处理的工作。如果有较多的初始化的动作包括内存分配或者资源回收动作建议是做一个初始化的接口和一个回收接口来进行虽然这样看起来有些丑陋并且增加了一些工作量。之所以不建议在构造和析构函数中做较多的工作原因就在于二者对异常的处理比较难于控制。 另外就不得不提到委托构造和继承构造了这都可以大大减少代码的编写量并使整个工程代码显得整洁得体。 还有一个问题就是构造函数是不会有是否virtual的问题但一般析构函数都会有这个虚拟的问题换句话说析构函数是否需要增加virtual。这个判断的很简单如果没有继承或者继承没有虚函数就不要用。 如果不允许类的隐式转换可以增加explict关键字防止编译器进行隐式类型转化。 c) 异常的处理 这包括刚刚提到的构造函数和析构函数。大家有兴趣可以查找资料来看一看这两个函数是如何控制异常的还是比较麻烦的不如单独拉出来处理。 d)拷贝构造和移动构造等函数何时需要编写 C11以前是默认的四个函数C11后是六个增加了移动拷贝和移动赋值。一般来说如需要实现非构造和析构函数中的其中任何一个就建议全写如果可以认定不需要就把其设置为delete。 特别是在赋值构造函数需要处理一下自身的赋值没啥实际意义。
d)普通函数的限定 包括static,const,noexcept等如此这些限定一般来说都比较好理解。一般不允许修改的都加const需要回传值的加引用如果既不允许修改变量又比较大,为了减少内存复制可以引用加const,这里重点提一个在C11中新增的引用限定符即类似于
class A
{int get0() {return a;}
int get1(){return a;}
int get2()const {return a;}};它可以限定左右值的调用但需要注意的是它只能在成员函数中使用而不能应用在static函数上。 还有最常见的inline函数一般建议是小函数功能简单的写成inline函数。不过话说回来这个是向编译器建议的不是强迫的如果设计上对性能不是要求多高可以忽略它。 e) 封装模板类 模板类比较麻烦的在一般它只在头文件里这里需要注意的有两点一个是模板的延迟加载即没有显示的调用模板不会自动生成实例另外一个是模板的难于调试性。只要注意到这两点一般的开发都可以比较好的适用了。 其实重点还是在于模板的代码不容易理解如果实际的场景中大家都不太会用模板还是建议将其耦合到自身不要对外暴露或者干脆不使用模板。
最后如果确定类不再继承可以使用final关键字来控制类的继承。这些都是一些比较常见的封装设计的实践经验不一定普适但可以根据实际情况综合应用到一起重点在如何使用它们这才是本文的目的。 从局部开始其实就可以在IDE或者相关工具中进行类的设计了当然继续在UML等工具进行设计也没有问题毕竟很多UML工具可以直接导出为类的代码。这个就看每个开发者的习惯和想法了不必强求。
三、结构体封装的特别说明
在C中类和结构体基本的封装方式是一致的但由于细节的不同还是有些不同的 1、结构的成员均为公有 所以上面的设计中关于变量的限制接口的限定等就没有了实际的意义
2、内存对齐和定义顺序 写过网络和硬件开发的开发者都知道二进制流传输是有字节对齐一说的。所以在设计一些对数据量敏感或者说有要求的结构体POD类型的类时需要对字节进行对齐。它有两种方式一种是内部调整一种是使用编译命令处理。在C/C面试时经常遇到一些类似下面的面试题
struct A {int d;double d1;char c;int i;double d2;bool b;
};然后sizeof(A)的大小。然后再把一些变量的位置换一下顺序再求一下大小。就会发现可能大小不一样。这其实就是内存对齐的原因。所以在一些对空间要求严格或者访问严格限定的情况下就需要处理一下变量的顺序。或者在某些情况下可以干脆使用一些预编译命令强制其内存对齐到1,比如在串口传输中就可以使用“#pragma pack(1)”当然不同的平台和不同的语言都不同的对齐指令或者方法C新标准中也提供了std::align,根据实际情况使用即可。不过强制内存对齐一般会造成性能的损失这个需要考虑应用场景。同时编译器对小对象的优化也需要考虑一般来说对于某些情况下需要限制一下对128位长度的访问变量因为它的速度会更快一些。 另外字节对齐的原因和好处这里就不再赘述有兴趣可以自己查看一些资料。 做为另外两种封装枚举体和联合体一般用得比较多的前者但前者除了一些C11前后的类型限定外只增加了一些类型处理的接口没有什么可谈的。而联合体应用的就更少了用到后自己斟酌考虑就可以了。
四、总结
面向对象编程是一种非常广泛的编译方式很多开发者可能对它是既了解又不了解。对一些基础的知识会用但又不知道是否用得合适能不能有一个标准来判定。其实这恰恰表明对面向对象编程还是掌握的不够深入。一切设计没有标准只有原则这也意味着实际场景下在考虑原则的同时更要考虑实际的需求进行适当的取舍。 最好的设计方法是没有的只有最合适的设计方法。