C++ 类的设计与实现规范
文章目录
- 1.规范一:将类的定义放在头文件中实现
- 2.规范二:尽量将数据成员申明为私有
- 3.规范三:将成员函数放到类外定义
- 参考文献
规范是一种规定,遵守这种规定能够带来长远的利益,而违反这种规定却不会立即收到惩罚。程序设计的规范是人们在长期的编程实践中总结出来的,深入理解这些规范需要认真的思考和大量的实践 。不符合程序设计规范的代码也能通过编译并运行,但是从长远来看,代码存在可读性差、安全性低、不易扩展、不易维护等问题。类是面向对象程序设计最主要的元素,遵循必要的规范,设计出性能优良的类,并以适当的方式实现,是编写出高质量程序的关键。
1.规范一:将类的定义放在头文件中实现
这样可以保证通过引入头文件时,使用的是同一个类,也有利于代码维护。比如我们有如下 Student 类:
// a.cpp
class Student {uint64_t id;string name;
public:uint64_t getID(){return id;};string getName(){return name;}
};// b.cpp
//有相同的类Student定义
class Student {uint64_t id;string name;
public:uint64_t getID(){return id;};string getName(){return name;}
};
假如根据项目的新需求,类 Student 需要添加年龄(age)私有数据成员,此时,如果更改了 a.cpp 中的Student 定义而忘记更改 b.cpp 中的定义,则会出现类定义不一致的情况,容易导致编译错误。即使记得每个源文件都需要修改,如果几十甚至上百个源文件都定义了类 Student,那么我们需要重复更改很多次,这种费力不讨好的做法应该尽量避免。有没有一劳永逸的做法,其实是有的,我们将类的定义放在头文件中,在需要类的源文件包含类定义所在头文件即可,保证了类定义的一致性,并且修改效率高,代码易于维护。
2.规范二:尽量将数据成员申明为私有
数据成员表示了类对象的状态,这些状态对外界应该是不可见的。在设计一个类的时候,如果把它的数据成员访问权限设为 public 和 protected,会带来如下影响。
(1)会使类的封装性遭到破坏;
(2)public 数据成员,类的用户直接访问数据成员,一旦数据成员的定义频繁改变,类的所有客户端代码都要修改,增加了代码模块间的耦合度。
考察如下示例程序:
#include <iostream>
#include <string>
using namespace std;class Student {public:uint64_t id;string name;public:Student(){id = 0;name = "";};void print(){cout<<"id:"<< id<<" name:"<<name<<endl;}uint64_t getID() { return id; };string getName() { return name; }
};int main(int argc, char* argv[]) {Student s;s.id = 1;s.name = "C罗";s.print();
}
程序输出结果:
id:1 name:C罗
Student 是一个学生类,我们希望用户能够正确的使用Student来创建学生对象,但是在上面的代码中,我们发现用户给学生设置的名称为“C罗”,然而中国目前姓名是不能以字母开头的,所以这个名字是不合法的。产生这个错误的原因是 Student 类的设计存在缺陷,将数据成员 id 和 name 的访问权限设置为 public,意味着有无数的函数可以不加限制地访问学生对象的数据成员,这样就无法保证每次对数据成员的设置都是正确的。如果我们增加一个设置接口,例如成员函数int set(uint64_t id,const string& name){...}
,那么能够修改数据成员的接口只有一个,只要在修改接口中排除各种错误的输入,就可以保证对 Student 对象的正确设置。这种对数据成员的直接访问,是对类封装性的一种破坏。
另外,从代码模块间的耦合度来看,将数据成员设置为公有,意味着所有用户对类数据成员直接依赖,一旦数据成员的定义发生变化,类的所有客户端代码均需要修改,降低了代码的可维护性。
同样地,将数据成员声明为 protected,也破坏了类的封装性,因为该类的所有子类均可以直接访问 protected数据成员,如果该类的子类数量庞大,一旦数据成员定义发生变化,所有的派生类都需要重写。所以,应该尽量将所有的数据成员申明为私有(private)。
3.规范三:将成员函数放到类外定义
类成员函数既可以放在类体内定义,也可以放在类体外定义。如果将类成员函数定义在类体内,会有如下影响。
(1)类的成员函数定义在类的内部影响可读性。一般来说,类的定义放在头文件中,使用时被不同的源文件包含,如果类成员函数定义在类体内,将会是代码体积增大,影响阅读,不利于类的修改与维护;
(2)泄露类的实现细节,不利于保护设计者的合法权益。因为接口开放给外部使用时,需要给出原型,比如类的定义,如果将类成员函数定义放在类体内,则函数实现将被暴露;
(3)会存在潜在的风险。如果类的成员函数存在多重定义,编译器无法检查出类定义的二义性。假设有一个类 Student 的定义放在两个头文件中,并且同名成员函数 print() 出现了二义性。考察如下程序:
/*test1.h*/
class Student {string name;public:Student(){name = "lvlv";};void print(){cout<<"name:"<<name<<endl;}
};
/*end test1.h*//*test1.cpp*/
#include "test1.h"void useClass();int main() {Student s;s.print();useClass();
}
/*end test1.cpp*//*test2.h*/
class Student {string name;public:Student() {name = "jf";};void print() {cout<<"another name:"<<name<<endl;}
};
/*end test2.h*//*test2.cpp*/
#include "test2.h"void useClass() {Student s;s.print();
}
/*end test2.cpp*/
编译运行上面的程序,输出结果如下:
name:lvlv
name:lvlv
上面错误地将类 Student 成员函数 print() 放在类体内定义并且出现重定义,本希望编译器在编译时能够帮助开发人员发现这种错误,但是由于编译器采用分离编译模式,各个源文件中的函数在编译时互不干涉,在链接时由于类体内定义的函数为 inline 函数,链接器会对重复的成员函数实体进行冗余优化,只保留一份函数实体,也就不会出现函数重定义的错误了。如果将类成员函数放在类外定义,则编译器可以发现这种重定义错误,所以在类的实现中,应该将类成员函数尽可能地放在类外定义。如果要定义内联函数,只需要在成员函数定义时显示地使用 inline 关键字即可。
参考文献
[1] 陈刚.C++高级进阶教程[M].武汉:武汉大学出版社,2008.C4.10类的设计与实现规范.P164-167
C++ 类的设计与实现规范相关推荐
- android 系统的切图方式_UI设计切图规范
移动UI设计切图是UI设计师最重要的设计输出物,切图资源输出是否规范直接影响到工程师对设计效果的还原度.设计师的切图输出物是是体现一个设计师专业水准的重要标准,同时也是设计师表达自己对设计态度的最有力 ...
- 一文讲透非标品的商品类目设计
www.pmcaff.com 本文为作者 百转 于社区发布 只要做电商,商品类目设计就是绕不开的一环.好的类目设计能让供需双方更快.更好定义或找到所关心的商品,是整个电商体系的地基,是构建并链接各个模 ...
- c语言grade d10,《电子技术10级C语言课程设计报告书写规范》.doc
课程设计报告书写规范 1.课程设计报告装订顺序:封面.任务书.目录.正文.附件(所有程序的源代码,要求对程序写出必要的注释).评分表(A4大小的图纸及程序清单). 2.正文的格式:一级标题用3号黑体, ...
- 数据库设计、查询规范及常用SQL语句
1.数据库设计规范 1.1 表设计 (1)表名前应加上前缀,表的前缀用系统或模块的英文名称缩写: (2)数据库表名应该有意义,表名太长需要用前缀表示,并且易于理解,最好使用可以表达功能的英文单词或缩写 ...
- MySQL 设计与开发规范
阅读目录 规范背景与目的 设计规范 数据库设计 库名 表结构 列数据类型优化 索引设计 分库分表.分区表 字符集 一个规范的建表语句示例 SQL 编写 DML 语句 多表连接 事务 排序和分组 线上禁 ...
- 【实验报告】实验一 简单类的设计与应用
实验一 简单类的设计与应用 一.实验目的 (1) 用来编写简单的C++程序掌握类的声明定义和使用方法理解和掌握类的数据抽象和数据封装的基本原理和方法. (2) 使用类编写较完整的C++程序,掌握单文件 ...
- 组件化设计思维 – 从规范到工具的构建与探索
作者 | 斓青 原文 | http://www.aliued.cn/2017/08/31/组件化设计思维-从规范到工具的构建与探索.html 阿里巴巴在中台战略的背景下,设计提效又再次推动着设计思维的 ...
- oracle报错数据复数,Oracle数据库设计策略及规范
Oracle数据库设计策略及规范 设计策略及规范 1. 目的 定义Oracle数据库设计规范,作为数据库规划.设计.开发以及维护人员的技术参考资料.用以规范和指导相关人员的设计行为. 2. 概述 本文 ...
- mysql左对齐原则_MySQL 设计与开发规范
MySQL 设计与开发规范 1 目的 本规范的主要目的是希望规范数据库设计与开发,尽量避免由于数据库设计与开发不当而产生的麻烦:同时好的规范,在执行的时候可以培养出好的习惯,好的习惯是软件质量的很好保 ...
最新文章
- 新日光Q3收入亏损8400万美元缩水33.85%
- [SpringBoot之Druid]
- 建立和操作 JDOM 文档
- hadoop配置文件加载机制
- Redis整合springboot实现集群模式
- JVM可生成的最大Thread数量探索
- 二叉搜索树c++_LeetCode98验证二叉搜索树
- 回文字符串,回文链表
- ROST_CM6软件之词频分析、社会网络和语义网络分析、情感分析
- 华为手机解锁码计算工具_华为最新解bl解锁码计算工具 V2.0.2 免费版
- 全志F1C100s入坑与填坑 uboot Linux Kernel 与buildroot
- html5 模拟scrollview,horizontalScrollView
- 《Dreamweaver CS6 完全自学教程》笔记 第十一章:模板和库
- 企业实战之部署Solarwinds Network八部众
- 程序复杂性度量方法-McCabe
- 【TCP的拥塞控制】基于窗口的拥塞控制
- vimPlus插件安装失败解决
- 【论文笔记】Civil Rephrases Of Toxic Texts With Self-Supervised Transformers
- IBM 员工大地震:3万职位面临调整 1万人或被裁
- YOLO模型 训练及预测
热门文章
- 数据库连接池种类及性能
- linux下的shell脚本(基本)
- AIX安装JDK1.7教程
- JavaAgent学习笔记
- JavaEE Tutorials (25) - 使用Java EE拦截器
- windows多线程详解
- 如何走技术路线的研究生论文?
- 如何消除选定TextBox后的光标但又不失去焦点。
- C++编译过程中没有找到MFC80UD.DLL,因此这个程序未能启动.重新安装应用程序可能会修复此问题? 的彻底解决...
- 【翻译】MSIL 教程(一)