大规模C++程序设计 -- 基本规则

news2024/12/27 1:47:38

文章目录

  • 基本规则
    • 概述
    • 成员数据访问
    • 全局命名空间
      • 全局数据
      • 自由函数
      • 枚举类型、typedef和常量数据
      • 预处理宏
      • 头文件中的名称
    • 包含卫哨
    • 包含冗余卫哨
    • 文档
    • 标识符命名规则

基本规则

概述

任何精美的艺术不仅来源于创造,而且来自于规范。编程也是如此。C++是易总大型语言,有充足的空间进行创造。但是,由于设计空间太大,以至于没有约束–也就是说没有设计结构上的一些适当的约束–大型项目很容易变的难以管理和维护。

  • 设计规则:经验告诉我们,某些编程习惯虽然在C++中完全合法,但是决不能简单地用于大型项目中。检验是否遵守了这些规则的过程不能是一种主观过程。设计规则必须足够准确、详尽和良好的定义,以便可以客观的检验是否遵守了这些规则。为了效果更好,设计规则必须适合于进行非人为的、借助自动工具的机械验证。
  • 指导方针:经验告诉我们,有一些习惯应尽可能的避免,这种具有更抽象特征性的建议规程称为指导方针,这样的规程有时候允许一些合法的例外。指导方针就像必须遵守的经验法则,除非是别的更紧迫的工程原因要求遵守其他方针
  • 原则:有一些观察和事实在设计过程中经常被证明是有用的,但必须在特定设计的上下文中评估,这些观察和事实称为原则

成员数据访问

封装是一个术语,用于描述在过程接口后面隐藏实现细节的概念。类似的术语有信息隐藏和数据隐藏。直接访问一个类的数据成员违反封装原则。

主要设计规则:保持数据成员的私有性

例如:我们有个未封装的接口类,可以直接访问内部成员

class Rectangle
{
public:
    Point lower_left_;
    Point upper_right_:
    ...
}

如果我们修改了类内的成员,会导致之前直接访问成员的代码都需要重新编码。组件重用使得这个问题变的更加严重。

如果不能通过某个类的逻辑接口编程访问或检测到其包含的实现细节,则称这些实现细节被该类封装了

封装是面向对象设计的一个重要的工具。封装意味着我们将低层次的信息集合在了一起,使他们以一种紧密耦合的密切方式潜在地交互。而信息隐藏则用于限制外部世界与某些细节交互,这些细节与类要帮助实现的抽象无密切关系

使所有数据成员保持私有,并且提供适当的访问函数和操纵函数,可以着呢宫颈癌可维护性,保证对象的完整性。

全局命名空间

对于中等规模的项目来说,如果参与开发的不止一个人,当各自独立开发的部件集成到一起的时候,就会有命名冲突的危险。问题的严重性会随着系统规模的扩大而成指数级增长。若冲突是由于第三方提供的集成软件引起的,则情况会更加恶化。

全局数据

避免在文件作用域内包含带外部连接的数据

文件作用域中带有外部连接的数据存在于其他编译单元中的全局变量冲突的危险(这些编译单元的作者是以自我为中心,他们认为自己拥有整个全局域)。但是名称污染只是全局变量破坏程序的许多方式之一。全局变量将对象和代码绑定在一起,这种方式使得在别的程序中实际上不可能有选择的重用编译单元。在大型项目中,调试、测试甚至理解大量的全局变量的系统的开销也会变得非常的惊人。

加入我们被迫使用一个已经要求在其接口上使用全局变量的系统,则有两种简单的变换方式能将这些变量非全局化:

  • 将全局变量引入一个结构中
  • 然后将他们私有化并添加静态访问函数

假如我们有以下变量:

int size;
double scale;
const char *syatem;

我们需要改成下面代码,就可以将其从全局名称空间中删除:

struct Global
{
	static int size_;
    static double scale_;
    static const char *system_;
}

需要再源文件中定义这些静态数据成员,这样就可以将名称冲突减小到会与一个类名称冲突,后续我们讨论解决办法。

我们需要使Global成一个类,并且提供方法访问对应的静态变量,这样可以消除直接访问静态成员所带来的难以维护的问题。

自由函数

自由函数也会对全局名称空间形成危险,尤其是参数基调中不包含任何用户定义类型时。如果一个自由函数在一个头文件中定义为有内部连接或者在源文件中定义为有外部连接时,那么在程序集成过程中,他们可能会和有相同名称的另一个函数定义相冲突。运算符函数例外。

在头文件作用域内避免使用自由函数(运算符函数除外),在源文件中避免使用带有外部连接的自由函数(包括运算符函数)

幸好,自由函数总能分组到一个只包含静态函数的工具类中。这样产生的内聚性不一定是最佳的,但它减少了全局名冲突的可能性。例如:

int getA();
void setB(double b);
int isPasswordCorrect(const char *usr, const char *pass)

该自由函数可以使用下面的静态方法替换:

struct SysUtil{
    static int getA();
	static void setB(double b);
	static int isPasswordCorrect(const char *usr, const char *pass)
}

唯一有冲突的危险符号是类名SysUtil.

但是自由运算符函数不能嵌入类中。这不是一个严重的问题,因为自由运算符要求至少有一个参数是用户自定义类型,因此自由运算符冲突的可能性很小,而且这种冲突在实践中一般不成问题。

枚举类型、typedef和常量数据

枚举类型、typedef和文件作用域常量数据都有内部连接。人们经常在头文件的文件作用域内声明常量、枚举类型或用户自定义类型,这是一个错误。

在头文件作用域内应该避免使用枚举类型、typedef和常量数据。

因为C++支持嵌套类型,因此可以在一个类的作用域中定义枚举类型,或者是声明typedef而不会在全局名称空间中与别的名称冲突。选择一个更受限的作用域,在该作用域中定义一个枚举类型,我们可以确保所有的该枚举类型中的枚举元素的作用域相同,并且不会与该作用域之外定义的别名冲突。

例如:

enum Color { RED, ORANGE };
enum Fruit { APPLE, ORANGE };

这两个枚举类型可能会在某一天产生冲突,如果将这两个枚举类型改为在类中定义,可以解决二义性问题。当然C++11还提供了以下方式声明,也可以解决域冲突的问题。一般情况下,优先使用enum class去定义枚举

enum class Color {}

预处理宏

在C++中几乎不需要哄。他们对包含卫哨(guard)是有用的,并且只有在少数情况下,在一个源文件中他们的好处才会超过他们的问题(最特别的是,当用于为移植或者调试获得条件编译时)。但是,一般来说,预处理宏对软件产品是不合适的。

除非是作为包含卫哨,否则在头文件中应该避免使用预处理宏

预处理器不是C++语言的一部分;他的基本原则是完全不改变原文,这使宏非常难以调试。尽管宏可以使代码易于编写,但是他们的自由形式经常使得代码更难阅读和理解。例如下面代码:

#define glue(x, y) x/**/y
glue(pri, ntf)("hello world");

在源代码层次上,我们如何告诉调试这、浏览器或者其他自动工具去处理上述代码呢?

和在源文件中包含宏一样,甚至有更充足的软件工程理由要求头文件不能包含宏。这样情况是允许的:在一个头文件中使用#define来定义预处理常量。因为宏不是C++语言的一部分,他们不会被放在类的作用域中。任何包含#define的头文件将具有该预处理常量的定义。

在丢失或者没有充分实现C++语言特性的情况下,预处理宏也可以用于实现模板。如果宏用于此目的,那么宏函数将出现在头文件中。有一些解决这个问题的方法(不使用宏),可能更适合大型项目。无论如何,与模板相关的问题应在开发过程的早期解决。

头文件中的名称

在头文件的文件作用域中声明的名称,可能潜在地与整个系统中任何一个文件对的文件作用域名称冲突。即使在一个源文件的文件作用域中声明带有内部连接的名称也不能保证一定不与头文件的文件作用域名称冲突

只有类、结构、联合和自由运算符应该出现在头文件的文件作用域内声明;只有类、结构、联合和内联函数应该在头文件的文件作用域内定义

我们希望只能在一个头文件的文件作用域中找到类的声明、类定义、自由运算符声明和内联函数定义。在类作用域中嵌入所有其他的结构,可以消除大多数与名称冲突相关的麻烦。

包含卫哨

如果我们遵守了2.3,我们仍然会遇到问题

当编译组件c的源文件时候,预处理其首先会包含和c相关的头文件,依次地头文件所包含的其他头文件,则编译器会报多重定义

在每个头文件周围放置一个唯一和可预知的包含卫哨

在这里插入图片描述

解决这个问题的传统方法是,在每个头文件的内容周围加上一个内部的保护包装器。不管包含图是什么样的,包装器都能确保类和内联函数在一个给定的编译单元中只出现一次。注意,我们不是在企图组织循环包含;我们要阻止的是重复包含,这种重复包含源自一个非循环包含图中的再收敛

现代的处理方式是:

#ifndef PROJECTNAME_FILENAME_H
#define PROJECTNAME_FILENAME_H
#endif  // PROJECTNAME_FILENAME_H
// 或者
#program once

包含冗余卫哨

使用冗余哨位,可以有效的减小编译时间复杂度

#ifndef PROJECTNAME_FILENAME_H
#define PROJECTNAME_FILENAME_H

#ifndef INCLUDE_H1
#include "h1.h"
#endif

#endif  // PROJECTNAME_FILENAME_H

文档

为接口建立文档以便其他人可以使用,至少请另一个开发者检查每个接口

明确地声明条件(在该条件下行为没有定义)

使用assert语句有助于为程序员实现编码时的假设建立文档

文档和assert语句的有效使用可以使我们得到更简练但是仍然十分有用的代码。如果有人误用了某个函数,这是他们自己的错–而且他们很快就能发现这个错误!

标识符命名规则

使用一种一致的方法(例如在变量后加_表示成员变量,首字母大写来表示类,使用全部大写来标识不变值、const和预处理数据等, 标识符名称要一致)当函数实现方式相同时候,名称必须一致

在一个大型系统的整个接口获得一致性,可以增强可用性,但是要达到此目的也可能会遇到出人预料的困难。在大型项目中,授权一个顶级开发小组担当接口工程师已经被证明是有效的,可以跨越开发小组获得一致性。容器类以及他们的迭代器也有助于模板的实现,实现模板可有效地加强跨越其他无关对象的一致性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1520801.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【New Release】PostgreSQL小版本(16.2, 15.6, 14.11, 13.14,12.18) 发布了

前言 PostgreSQL遵循小版本的发布规律,这一个季度的小版本又发布了。可以算作是2024年第一个季度的版本发布。如果总结其规律:大概就是2月、5月、8月、11月的样子。通常因为11月配合大版本的发布,它是起点,也有可能就是终点。起点…

【Docker篇】自定义Dockerfile的操作

文章目录 🍔镜像结构🛸什么是Dockerfile⭐基于Ubuntu镜像构建一个新镜像,运行一个java项目🔎使用 java:8-alpine 🍔镜像结构 镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。 我们以MySQL为例&am…

Vintage账龄分析表计算底层逻辑(Python实操)

大家好,我是东哥。 信贷风控领域中,经常用到账龄Vintage报表,这是入门初学者的难点之一,因为它涉及到用户还款、逾期等多种行为以及业务上的多种统计口径,因此很多朋友一直无法将逻辑梳理清楚。本次来给大家详细介绍V…

Java:多态

目录 1.向上转型2.动态绑定3.方法重写4.理解多态5.多态的优缺点 1.向上转型 把子类对象给到父类,代码如下 class Animal{public String name;public int age;public void eat(){System.out.println(this.name"正在吃饭!");} } class Dog ext…

力扣映射思辨题:赎金信

思路很简单&#xff1a;查到就改 bool canConstruct(char* ransomNote, char* magazine) {for(long x0;x<strlen(ransomNote);x){for(long y0;y<strlen(magazine);y){if(magazine[y]ransomNote[x]){ransomNote[x]1;magazine[y]1;break;}}}for(long x0;x<strlen(ranso…

【ArcGIS 脚本工具】批量导出布局为图片

文章开始前要介绍一下ArcMap与ArcPro在布局上的区别。 ArcMap10.x版本的mxd文件默认只有一个布局&#xff0c;所以如果一个项目需要出几张图&#xff0c;做好的办法就是建几个mxd文件。 但是ArcPro在这方面更加整合了&#xff0c;一个aprx文件内可以新建任意多的布局&#xff…

无人机助力智慧农田除草新模式,基于YOLOv8全系列【n/s/m/l/x】参数模型开发构建无人机航拍场景下的农田杂草检测识别系统

科技发展到今天&#xff0c;无人机喷洒药物已经不是一件新鲜事情了&#xff0c;在很多高危的工作领域中&#xff0c;比如高空电力设备除冰&#xff0c;电力设备部件传送更换等等&#xff0c;无人机都可以扮演非常出色的作用&#xff0c;前面回到老家一段时间&#xff0c;最近正…

内网渗透之路:常用命令助力信息深度探索

1、查询网络配置信息 ipconfig /all 2、查询操作系统及软件信息 &#xff08;1&#xff09;查询操作系统和版本信息 英文操作系统 systeminfo | findstr /B /C:"OS Name" /C:"OS Version" 中文操作系统 systeminfo | findstr /B /C:"OS 名称&q…

【C#】int+null=null

C#语法&#xff0c;这玩意不报错 intnullnull&#xff0c;有点不合逻辑 (Int32)(bizRepair0rder.CreateTime. Value - regues.Mlodifylime.Value).TotalMinutes (Int32)(bizRepair0rder.CreateTime. Value - reques.llodifylime.Value).TotalMinutes nullstring是引用类型&…

【C#】【SAP2000】读取SAP2000中所有Frame对象在指定工况的温度荷载值到Grasshopper中

if (build true) {// 连接到正在运行的 SAP2000// 使用 COM 接口获取 SAP2000 的 API 对象cOAPI mySapObject (cOAPI)System.Runtime.InteropServices.Marshal.GetActiveObject("CSI.SAP2000.API.SapObject");// 获取 SAP2000 模型对象cSapModel mySapModel mySap…

PlantUML + VS Code

PlantUML 使用实例 文章目录 PlantUML 使用实例1. PlantUML简介1.1 什么是PlantUML1.2 PlantUML优势在哪 2. 怎么用2.1 环境依赖2.2 VS Code组件安装 3. 常用语法3.1 标记开始结束3.2 声明参与者3.3 声明关系3.4 对消息序列编号3.5 组合消息 4. 实例 1. PlantUML简介 1.1 什么…

NCDA大赛中哪些HTML5设计作品展现出色?

与传统的HTML网页设计相比&#xff0c;HTML5网页设计主要是对网页内容的加强。HTML5已成为目前最流行的标记语言&#xff0c;拥有成熟的社区和广泛的浏览器支持&#xff0c;HTML5完整的功能和强大的扩展性使设计师和开发者能够点铁成金。HTML5可以一手控制更多可控元素&#xf…

HDS-NAS分配资源并挂载win和linux

1、首先创建系统文件。 选择nas存储池 2、根据自己的需求创建相应的挂载方式 3、window配置 配置成功 最后即可在window系统网络位置映射网络即可&#xff0c; 格式为\\123.3.4.5\test 注&#xff1a;IP地址 4、liunx挂载方式 创建完成之后即可挂载&#xff0c;注意目的主…

Vue+OpenLayers7入门到实战:OpenLayers如何使用全屏控件,来实现地图容器的全屏和退出全屏功能

返回《Vue+OpenLayers7》专栏目录:Vue+OpenLayers7入门到实战 前言 本章介绍如何使用OpenLayers7在地图上使用地图全屏控件,来控制地图容器的全屏和退出全屏的功能。 注意:这里的全屏控件全屏指的是地图容器全屏,并非整个网页全屏。 网页整体全屏和指定网页节点全屏可以参…

FreeRTOS的学习路径介绍

FreeRTOS是一种广泛使用的开源实时操作系统&#xff08;RTOS&#xff09;&#xff0c;它被设计为一个小型、可扩展的操作系统&#xff0c;适用于微控制器和嵌入式系统。由于其轻量级和可扩展性&#xff0c;FreeRTOS被广泛应用于物联网&#xff08;IoT&#xff09;、工业自动化、…

简单了解 vim 编辑器最基础的操作

简单了解 vim 编辑器最基础的操作 vim 这个是 Linux 上自带的一个文本编辑器&#xff0c;使用 vim 就可以更灵活的对文件进行编辑了&#xff08;虽然和记事本的定位差不多,实际上vim的使用要复杂很多&#xff09; 1.打开文件 语法&#xff1a;vim 文件名 示例&#xff1a;…

mysql 排序底层原理解析

前言 本章详细讲下排序&#xff0c;排序在我们业务开发非常常见&#xff0c;有对时间进行排序&#xff0c;又对城市进行排序的。不合适的排序&#xff0c;将对系统是灾难性的&#xff0c;这个不是危言耸听。可能有些人会想&#xff0c;对于排序mysql 是怎么实现的&#xff0c;…

【计算机网络篇】计算机网络的性能指标

文章目录 &#x1f354;计算机网络的性能指标&#x1f5c3;️常见的计算机网络性能指标⭐速率⭐带宽⭐吞吐量⭐时延⭐时延带宽积⭐往返时间⭐利用率⭐丢包率 &#x1f50e;总结 &#x1f354;计算机网络的性能指标 计算机网络的性能指标被用来从不同方面度量计算机网络的性能 …

02-Java变量和运算符

1. 基本数据类型转换&#xff08;Conversion&#xff09; 在Java程序中&#xff0c;不同的基本数据类型的值经常需要进行相互转换。Java语言所提供的七种数值类型之间可以相互转换&#xff0c;基本数据类型转换有两种转换方式&#xff1a;自动类型转换和强制类型转换。boolean…

江科大stm32学习笔记【6-2】——定时器定时中断定时器外部时钟

一.定时器定时中断 1.原理 2.硬件 3.程序 此时CK_PSC72M&#xff0c;定时1s&#xff0c;也就是定时频率为1Hz&#xff0c;所以可以PSC7200-1,ARR10000-1。 Timer.c: #include "stm32f10x.h" // Device headerextern uint16_t Num;//声明跨文件的…