- 博客(29)
- 收藏
- 关注
原创 做个广告-新书优惠
本人新书《码农修行》目前当当正在五折优惠。《码农修行:编写优雅代码的32条法则》(林文 著)【简介_书评_在线阅读】 - 当当图书
2021-10-23 20:16:40 153
原创 这条编程规范如何?——条件分支超过6个时必须改为接口
前面举过一个面向接口编程的例子。在系统中写日志有3种不同的实现:写到日志文件。 写到数据库。 写到本地的一个日志服务的UDP端口。按照常规的思维逻辑,可以用一种最直接的写法。如下方案一。方案一public class LogController { private LogService logservice; void writeLog(SysLogInfo logInfo){ switch(logType){ case LT_FILE: logservice.writ
2021-07-21 00:39:00 157
原创 别把Java写成C++的样子。你真的在正确运用依赖注入吗?
最近发现很多使用Spring框架的Java代码存在依赖注入方式的误用,甚至是滥用。因此整理了这篇文章,欢迎大家一起探讨。先举个例子,从C++说起……C++的诟病C++最遭人诟病的地方就是定义一个类需要写两个文件,一个.h文件和一个.cpp文件。例如定义一个CMainFrame类,mainframe.h内容如下:class CMainFrame : public CFrameWndEx{protected: CMainFrame();public: virtual ~CMainFram
2021-05-09 20:53:39 1323 20
原创 《码农修行》法则07:避免过度防御
《码农修行》法则07:避免过度防御非法指针是C/C++程序中最令人头痛的问题之一。你也许会有类似的习惯,在函数入口即对指针的合法性进行检查。代码如下。即使在调用该函数前已经做过一次检查。代码如下。这两处检查都属于过度防御,完全没有必要。首先,参数的合法性应该由调用者保证。可以参考一些参数为指针的API,比如strncpy,当对其强制传入一个空指针时,程序并不会悄无声息,而是会抛出异常。因为这样的参数已经违反了接口的规约,函数内部不知道该如何处理,只能抛出异常让程序崩溃。此外,
2021-01-22 22:46:53 250 1
原创 留意编译告警
Java编程不涉及本节内容,因为它在编译时语法检查更加严格,要么出错要么成功。C/C++也许是太过于灵活,或者由于历史原因,编译时除了产生错误还会产生告警。同时程序中编译告警容易被忽视,但其中往往隐含着一些潜在的问题。我曾经参与过的一个项目中增加了PcLint检查,但由于规则设置的缘故,有一个bug没有产生PcLint告警。在最终定位问题后,才留意到它其实已经导致了一个编译告警但被忽视了,因为...
2019-10-26 20:17:44 534 1
原创 善始善终
互斥锁的加锁解锁,动态内存的申请释放,都较难驾驭且容易出错。所幸我在Z和H公司所写的代码都有意或无意的避免了这两个问题。当时所涉及的是基于VxWorks的嵌入式编程,在进程调度方面系统控制层做了限制,每个进程处理完自己的消息任务后才会调度另一个进程运行,因此避免了进程并发的情况,也就无需对临界资源进行加锁。另一方面,嵌入式系统对实时性要求很高,系统中所需要的内存都在初始化时全部申请完成,并且一直不...
2019-09-12 17:49:58 202
原创 命名
如果你准备花一小时写代码,那么其中半小时得用于起名字。这句话虽然有些夸张,但体现出命名的重要性。的确,类名、函数名、变量名、文件名等等如果含混晦涩,会给维护工作造成麻烦。命名时把握一个原则:对于函数说明其在“做什么”,对于类或变量说明其“是什么”。言简意赅是一种理想状态。首先要用词准确,特别是对英语没把握的人,请随时查一下字典。然后把这些词拼成一个短语或短句。最后,如果名字太长,再做一些简化。...
2019-08-29 11:50:13 555 1
原创 可读性
如何提高代码的可读性是一个永恒的话题。通常认为程序员需要具备较高的“数学”功底。毕竟要掌握各类模式和算法都依赖很强的逻辑思维能力。然而在经历了一段职业生涯后,我发现程序员的“语文”能力也同样重要。《重构》一书中就提出:唯有优秀的程序员能够写出人类能理解的代码。软件的规模越来越大,一个系统通常需要几代程序员来开发维护。然而这些程序员们往往都素未谋面,只能“神交”于代码的字里行间。提到可读性,...
2019-08-29 11:48:21 1024
原创 性能
做人不要斤斤计较,但写代码一定要斤斤计较!来看一个Linux-2.4内核中的例子,学习世界顶尖高手的做法。内核中经常要访问进程控制块(PCB),其在内核中定义为task_struct结构。为此在include\asm-i386\current.h中定义了一个宏current,提供指向当前进程的task_strcut结构的指针。static inline struct task_struct *...
2019-08-28 12:02:58 188
原创 用表驱动消除冗余代码
表驱动法最常用的一种场景是用其替代if和case语句,使用查表的方式实现代码逻辑,其可以以使代码更为简单明了,同时可以获得更优的执行效率。但其还有一种运用场景:可以消除冗余代码,使得代码更灵活,方便应对将来需求的变化。假设下面一段代码需要初始化一些ICON图标: m_ImageList.Add(LoadIcon(hResource, MAKEINTRESOURCE(ICON1))); m
2017-08-21 18:23:38 386
原创 写日志
为什么记日志日志记录是否完备,直接影响到软件产品维护和问题定位的难易程度。这主要源自以下几个因素:第一、开发人员自己的环境无法覆盖100%的应用场景,某项功能在开发者的机器上一切正常,但是到了测试人员或用户的机器上就不能满足预期。第二、当在用户现场出现问题时,往往无法再单步调试。如果要到用户现场架设一套开发环境成本太高。第三、有些问题存在一定概率,并不能100%复现。而日志可以记录
2017-07-25 15:13:15 872
原创 关于软件工程的类比
我不喜欢“码农”“码工”“程序猿”这类自贬身价的词语。我的爷爷是一名裁缝,在我的记忆里他靠在家里给别人定做衣服谋生,算是一个手艺人。我的职业生涯一直和代码打交道,靠写代码谋生,也算是一个手艺人。当然手艺好的人应该能称得上是代码工匠吧!我喜欢把这群代码工匠称为软件设计师。为什么是软件设计师而不是软件工程师,这源自于我对软件工程的不同理解。建筑工程大致可以分为两个阶段,设计阶段和施
2017-07-19 18:21:22 1495
原创 避免堆栈溢出
案例最近在做一个Windows程序,其中有个消息处理函数,大概是这样的:BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx) ON_MESSAGE(WM_MY_MESSAGE, &CMainFrame::OnMyMessage)END_MESSAGE_MAP()LRESULT CMainFrame::OnMyMessage(WPARAM wp, LPA
2017-05-11 16:11:14 2525
原创 透过现象看本质:共享单车之摩拜和ofo的工作原理分析
开锁方式摩拜用app扫码成功后,锁会自动打开,无需人工对单车进行干预。这说明摩拜单车内部有联网装置,app扫码成功后,服务器后台发送指令,单车自动开锁。ofo需要输入单车的ID号码,随后程序给出一个开锁密码,需人工旋转开锁密码后,方能开锁。因此ofo单车没有联网装置。其内部的开锁原理类似银行的U盾密码卡,其密码每分钟变换一次,输入单车ID后,程序后台会计算其密码。在实际使用中也会碰到旋转密
2017-05-03 17:51:37 17145
原创 写代码的目的
写了那么多年代码,突然想自问一下:写代码的目的到底是什么?在回答这个问题之前,我想先归纳一下软件开发者的职业生涯,总体说来可以分为这么几个阶段:初级阶段:仅仅是简单的完成上级分配的任务。上级让做什么就只做什么,甚至对代码的性能,可靠性,可扩展性,可维护性都不加考虑。此时开发者最需要的是一份详细设计文档,并按照文档进行开发。这个阶段的人对需求变更相当排斥,甚至会因此频繁跳槽。绝大多数开发者的
2017-04-07 15:57:20 2239
原创 Linux 3.x 内核学习笔记——页面的周转
页面周转本文以Linux-3.11.0为例。内核中所有物理内存都有其对应的page结构,这些page在整个系统的运行中,会处于下图的5个状态中。Freeze Page:并非所有内存都可以用于分配,有些内存被内核所保留使用,例如:内核映像本身所处的内存空间、一些硬件要求需要保留的地址区间、内核中一些固有的数据结构(如page结构本身)等。系统在初始化阶段将这些内存所对应的地址标识为r
2013-10-21 13:31:14 2732 1
原创 Linux 3.x 内核学习笔记——x86 64位内存管理
地址映射64位地址采用4层地址映射,如下图:pgd、pud、pmd、pte各占了9位,加上12位的页内index,共用了48位。即可管理的地址空间为2^48=256T。而在32位地址模式时,该值仅为2^32=4G。另外64位地址时支持的物理内存最大为64T,见e820.c中MAX_ARCH_PFN的定义:# define MAX_ARCH_PFN MAXMEM>>PAGE_
2013-10-09 18:17:30 10819
原创 一种大文件的排序方法
要求:一个文件中存有若干单词,每行一个,要求将文件中的单词按字典序排序。分析:由于一个文件的大小可能超过内存大小,因此想要一次将整个文件全部读入内存后再进行排序是不现实的。当然,处理此问题可以采用归并法:把大文件拆分成多个可以一次读入内存的小文件,再对小文件进行排序后再归并。不过在此介绍另一种方法:以“磁盘空间”换内存空间,在文件内部进行冒泡排序。算法思路:读取文件中的第1、2两个单词,若
2012-12-07 22:44:54 6666 1
原创 C++基本概念总结——构造函数的public/protected/private语义
类的构造函数为 public 时,表明该类的对象可以被任意的创建。例如:class PublicClass{public: PublicClass(){}}; PublicClass *p = new PublicClass();类的构造函数为 protected 时,无法通过上面的方法创建对象。protected 构造函数说明:该类是用于派生的。例如:class Pro
2012-11-22 22:15:00 1284 1
原创 C++基本概念总结——虚函数和非虚函数
虚函数享有面向对象的“多态”性,考虑下面的例子:class Base{public: virtual void VirtualFunc(){ cout << "Base::VirtualFunc" << endl; } void Func(){ cout << "Base::Func" << endl; }};class Derived : public Base{ virtua
2012-11-22 21:55:30 1085 1
原创 C++基本概念总结——静态成员
类的静态成员函数和静态成员变量是类的属性,在该类的所有对象间共享。在使用时,可以直接通过类名来引用,也可以通过对象进行引用。考虑以下代码:class A{public: static void update() {s_cnt++; } static int cnt() {return s_cnt; }private: static int s_cnt;};int A::s_cn
2012-11-21 21:08:29 479
原创 C++基本概念总结——引用和指针
相对于C语言,引用是C++新增的概念。引用和指针有点相似,很容易混淆。引用只是被引用对象的一个别名,不管被引用对象的大小是多少,其只占4字节的空间。考虑下面代码,A1包含一个int类型的变量和一个B类型的引用,A2包含一个int类型的变量和一个B类型的变量。class B{ int a[4];};class A1{ int i; B& m_b;};class A2{
2012-11-21 20:45:42 497
原创 C++基本概念总结——公有继承和私有继承
公有继承也叫“接口继承”,表示的是“是一个”(IsA)的语义。例如“大雁”类派生于“鸟”类,可以描述为“大雁是一种鸟”。class 大雁 : public 鸟{};一般在基类中提供一个纯虚函数作为接口,在派生类中重写该虚函数,就可以实现基于该接口的动态绑定(即多态)。需要注意,这里的“是”并非“等于”,“是”的含义应该为“属于”,即一种包含关系。因为说“大雁是一种鸟”是成立的,但反
2012-11-20 22:06:43 790
原创 C++基本概念总结——const 和 非const
const成员函数类的成员函数后边增加一个 const 关键字,表示该函数为 const 成员函数。考虑如下类A的定义,f1为 const 成员函数,f2为 非const 成员函数:class A{public: A(int i) : m_id(i) {} void f1() const; // const 成员函数 void f2(); // 非const 成
2012-11-19 21:17:06 742
原创 C++基本概念总结——重载(overload)和重写(override)
overload翻译为重载。指的是函数名相同,但参数列表不同。代码举例:// 自由函数重载void f1(int a, int b){}int f1(int a){ return 0;}// 类的成员函数重载class A{ void f2(int, int); int f2(int);};上面代码中,自由函数f1有两个函数体,第一个
2012-11-18 13:05:17 586
原创 C++基本概念总结——声明(declaration)和定义(definition)
术语“声明”和“定义”的区别,可以简单的归纳为:涉及内存分配的就是“定义”,否则就是“声明”。下面代码是声明和定义的举例:class B; // 声明。仅声明 B 是一个类,不涉及 B 的对象内存分配class A{ // 从这里直到“}”结束,是对类 A 的定义。涉及到 A 的对象的内存大小public: void f1(); // 声明。不涉及函数 f1 的内存分配
2012-11-17 23:39:07 1598
原创 写注释
注释是代码的补充,是为了帮助读者更全面的理解代码。注释的多少与理解代码的难易程度并没有直接的关系,因此规定代码的注释率,是完全没有必要的。用注释来说明“为什么”一段时间以后,即使是代码作者也许都忘了当初代码为什么会写把代码成这样,因此用注释来说明“为什么”尤为重要。以下代码摘自Linux 2.4.0内核:void ll_rw_block(int rw, int nr, struct
2012-05-27 22:37:07 1367
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人