2007年01月15日 星期一 12:44
编写C++/C代码规则
参考书目《高质量C++/C编程》(林锐著)
能长期稳定地编写出高质量程序的程序员称为编程老手。
能长期稳定地编写高难度高质量程序的程序员称为编程高手。
※知错就改;温故知新;坚持学习,天天向上
规则
★为了防止头文件重复引用,应当用ifndef/define/endif结构产生预处理块。
★用include<filename.h>表示引用标准库的头文件;用include "filename.h"引用非标准库的头文件。
?★头文件只存放“声明”而不存放“定义”;不提倡使用全局变量,不要在头文件声明,分目录放置;如果头文件是私有的,为加强其隐蔽性,存放在子目录中。
★类声明之后,每个函数定义之后都要加空行。
★逻辑密切相关的不加空行,其他的地方应加空行。
★一行代码只做一件事。
★if,for,while,do等语句各自占一行,执行语句不得紧跟其后。无论执行语句有多少都要加{}。
★变量定义时初始化。
★关键字之后要留空格,函数名之后不留空格。
★“{”和“}”独占一行与引用它们的语句左对齐。
★“{}”内语句在右边数格处左对齐。
★代码行(hang)为一屏幕能显示下的长度。
★长代码表达式在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当缩进,使排版整齐语句可读。
×将修饰符*和&紧靠变量(不同意!既然都不允许int* x,y的声明方式,为何不靠近数据类型!)
★注释是代码提示,不是文档(象我,在没有条件写文档的情况下只能拿注释当文档!)注释与代码一直,没用的注释及时删除。注释放在代码上方!
★★尽量使结构清晰,避免繁琐的注释。
★★多重嵌套结束处加注释,便于阅读。
★★标识符应当直观可拼读,长度应符合“min-length&&max-informatrion”原则。
★★与所采用的OS或开发工具的风格保持一致。
★不要靠大小写区分相似的标识符。
★★不要出现完全相同的局部变量和全局变量。
★变量名使用“形容词+名词”。
★★全局函数名使用“动词”+“名词”;类成员函数名只使用“动词”。
★★用正确的反义词表互斥的变量或相反的动作函数。
★windows程序开发“匈牙利”命名规则
类名和函数名首字母大写
变量和参数用小写字母开头的单词组合而成
常量全部大小用下划线分割单词
静态变量s_,全部变量g_,类的类数据成员m_。
★★对于unsigned的变量加入un前缀,对于常用的,并且通过变量名称如:(size len 等)容易推断出的变量不加前缀
★代码行中符号较多,用括号确定其优先级。
★复合表达式不要太复杂,不要多用途,不要与数学表达式混淆。
★不可将布尔变量与TRUE,FALSE或者1,0进行比较。
★将整形变量用“==”或“!=”直接与0比较。
★不可将浮点变量用“==”或“!=”与任何数字比较。
★指针变量用“==”或者“!=”与NULL比较。
★if的条件可写成if(NULL == p),注意。
★★只有==使用变量在右的方式,!=、 >、 >=、 <、 <=条件不使用变量在右的方式
★注意使用其简单模式:condition?x:y。
★★长循环在最内,会提高循环效率。
★逻辑判断套循环效率高,但不易理解,不易修改。
★★不可在循环体内改变循环变量。
★for循环采用半开半闭区间写法for(i=0;i<N;i++)。
★每个case语句后都要加break。
★不要忘记default分支。
★多重嵌套可使用goto跳出。
★★函数很多返回,且返回之前要释放堆上申请的内存或关闭文件等操作,可使用goto返回位置。
★★使用含义直观的常量表示程序中多次出现的数字或字符串。
×在C++中只使用const不使用#define。
★需要对外公开的常量放在头文件中,不需要公开的放在定义文件的头部;用户看得见的放在一个公共的头文件中,便于管理。
★★定义中应表现出关系密切的常量间的关系。
★类中常量应用enum{SIZE1=100, SIZE2=200}。
★参数的书写要完整(类型 名字);无参数应写void(声明中用void)。
★★参数要命名恰当,顺序合理。
★如果参数是指针,且反作输入用,应在类型前加const,以防在函数内被修改。
★值传递,传递对象时应用const& 方式省去临时对象的构造和析构过程,提高效率。
★★参数最好控制在五个以内,尽量不使用类型和数目不确定的参数。
★不要省略返回值类型。
★★函数名和返回值在语义上不可冲突。
★返回对象时使用“引用传递”可提高效率,但有些场合会出错。
★★在函数体的“入口处”对参数的有效性进行检查—正确使用断言(assert)。在函数体的“出口处”对return语句的正确性和效率进行检查。不能返回指针“栈内存”的“指针”或引用 返回的是“值”,“指针”还是“引用”。返回值是个对象,要考虑return的效率。
★★函数应功能单一,50行代码以内,避免“记忆”(static)功能,检查全局变量,文件句柄等,出错处理的返回值一定要清楚。
★★assert不仅说明非法错误还要说明非法错误的原因!
★★对参数合法性的判断:assert()只适用自己和团队内部调用的函数,是一种“约定”的检查,对外提供的函数接口不应使用assert(),应用if替代。
内存分配方式:静态存储区域,在栈上创建,从堆上分配。
内存错误以及对策:内存未分配成功却使用它,使用之前检查指针是否为NULL
分配成功却未初始化却使用了;赋初值
忘记释放内存,new/delete malloc/free成对出现
释放了却还使用:数据结构不合理,重新设计;return用错;野指针
修改指向静态存储区的指针中的内容将出错
数组用库函数strcpy(),strcmp()进行复制和比较
指针内容比较,先申请len+1个空间内存在用strcpy比较 sizeof(数组)计算数组的容量sizeof(指针)计算指针变量的容量 数组作为函数的参数进行传递时,数组自动退化为同类型指针,不要用指针参数申请动态内存,如果必须应用指向指针的指针 指针与其所指向的内存的消亡是没有联系的
new/delete对对象比malloc/free多调用构造和析构函数
内存耗尽指针将返回NULL;用return返回;用exit(1)终止程序
异常处理如 vc++中_set_new_hander函数?
malloc的使用:int* p = (int *)malloc(sizeof(int) * length);
free的使用free(NULL)没有问题,free(p)且p!=NULL二次将出错
new数组对象时,delete[]数组释放
重载 小心隐式类型转换
重载,隐藏,覆盖
参数缺省值只出现在函数声明中,多个缺省值从后向前挨个缺省
重载运算符
×内联取代宏代码,内联放定义处;代码长,有循环不易inline。inline会对栈溢出的排错造成困难
一定要写无参数构造函数,拷贝构造函数,析构函数,赋值函数,偷懒时候可将其声明为private
若逻辑上B是A的“一种”,并且A的所有功能和属性对B而言都有意义,则允许B继承A的功能和属性
use const whenever you need!
日期: 2016-09-09 17:30:06, 8 years and 128 days ago
原文
红黑树与哈希表比较:
- 哈希表操作不够清楚。
- 红黑树插入,删除,查找速度可均摊。
- 在最坏情况下,树有更好的性能。
这大半是因为一个历史意外。在标准规则被确定之前,标准容器(包括迭代器和算法)很晚才被加入。所以,在标准确定之前,没有充分考虑到哈希表的定义,并且也没有足够时间加入。所以标准只包括了一个基于树的map。
C++11 加入了基于hash的std::unorderedmap(以及std:unorderedset)。
日期: 2016-07-21 17:30:06, 8 years and 178 days ago
2007年01月15日 星期一 12:33
快速学习C++
其中*为需要重点注意
《Essential C++》侯捷译 Stanley B. Lippman著
一、C++基础
1、撰写C++程序
////////////////////////////////////////
#include <iostream>
using namespace std; //让命名空间暴光
int main
{
cout<<"hello,C++"<<endl;
}
///////////////////////////////////////
如果main()函数的最后没有return语句,C99规定编译器要自动在生成的目标文件中(如 exe 文件)加入return 0;表示程序正常退出。
2、对象的定义与初始化 ??※const的应用
3、撰写表达式
4、条件语句和循环(Loop)语句
if (条件)
{
...
}
else
{
...
}
while (条件)
{
...
}
5、数组 Arrays off-by-one 越界(数组array[n]从array[0]开始,最后一个为array[n-1])
6、指针带来的弹性 *取值 &取地址
7、文件的读写
#include <fstream>
ofstream outfile ("seq_data.txt"); //写文件
ofstream outfile("seq_data.txt", ios_base::app); //追加形式开启
ifstream infile("seq_data.txt"); //读文件
fstream iofile("seq_data.txt", ios_base::in|ios_base::app); //读写
二、面向过程的编程风格
1、撰写一个函数
int function(int x, int *y)
{
...
return 0;
}
2、调用(invoking)一个函数
3、提供默认参数值
4、使用局部静态对象 const
5、声明一个inline函数
6、提供重载函数
7、定义并使用模版函数
template<typename elemType>
int function(const int x, const elemtype y)
{
...
}
8、函数指针 vector<int>* (*seq_pstr)(int);
9、枚举类型 enum ns_type{ns_fibon, ns_lucas, ns_pell};
10、设定头文件
三、泛型编程风格
1、指针的算术运算 int* pi;pi+2的意义
2、???Iterators泛型指针
vector<string> svec;
vector<string>::iterator iter=svec.begin();
3、所有容器通用的操作
equality() ==
inequality() !=
assignment() =
empty()
size()
clear()
begin()
end()
insert()
erase()
4、使用序列式容器
5、使用泛型算法
6、如何设计一个泛型算法
(1)开始我们写了一个函数,它可以找出vector内小于10的所有元素,然而函数过于死板没有弹性。
(2)接下来我为函数加上一个数值参数,让用户得以指定某个数值,以此和vector中的元素做比较。
(3)后来我又加上一个新函数:一个函数指针,让用户得以指定比较方式。
(4)然后我引入function object的概念,使我们得以将某组行为传给函数,此法比函数指针的做法效率更高,我们也带领各位简短地检阅了标准函数库提供的function object(所谓function object,是某种class的实体对象,这类classes对function call运算符进行了重载操作,如此一来,可使function object被当成一般函数来使用)[函数指针,指向函数]
(5)最后我们以template function的形式重新实现。为了支持多种容器,我们传入一对iterators,标出一组元素的范围;为了支持多种元素型别,我将元素型别参数化也将施用于元素身上的“比较操作”参数化,以便得以同时支持函数指针和function object两种方式
3、TMap使用
map<string, int> words;
words["verneer"] = 1;
map<string, int>::iterator it;
it->first;
it->second;
words.find(); //如果key已置于其中,find()会返回一个iterator,指向key/value形成一个pair;反之则返回end()
words.count(); //返回某特定项目在map内的个数
set 由一群keys组合,对于任何keys值,set只能存储一份
set<int> iset
iset.insert(ival);
iset.insert(vec.begin(), vec.end());
如何使用Iterator Inserters
#include <iterator>
back_inserter();
inserter(vector, iterator);
front_inserter();
四、基于对象的编程风格
1、如何实现一个class
class Jack
{
public:
//...
private:
//...
}
2、constructor和destructor
3、const
4、this指针
5、static class member静态的类成员 类名::静态成员函数
6、iterator class
7、friend class友元类
8、copy assignment operator
9、function object
10、运算符重载
11、指向类成员函数的指针
五、面向对象编程风格
(1)概念:继承、多态、动态绑定
(2)面向对象思维
(3)不带继承的多态*
(4)定义一个抽象基类:找出所有子类共通的操作行为->哪些操作与型相依(是否为virtual)->每个操作行为的存取层次(是public还是private还是protected)
(5)定义一个派生类*
(6)使用继承体系:将所有派生类共有的实现内容抽离出来,移至基类内
(7)基类抽象:从子类中抽象出基类
(8)初始化、析构、复制 子类数据成员=基类data member+本身data member
(9)在派生类中定义一个虚函数 继承 虚拟函数的静态决议->派生时完全吻合
(10)执行期型别鉴定机制*
六、以template进行编程
1、被参数化的型别
2、class template的定义
3、template型别参数(type parameters)的处理
4、实现一个class template
5、一个以function template完成的output运算符
6、常量表达式(constant expressions)默认参数(Default Parameters)
7、Template参数
8、member template funtions
七、异常处理*
日期: 2016-06-10 17:30:06, 8 years and 219 days ago
function declaration with throw
日期: 2016-05-13 17:30:06, 8 years and 247 days ago
从visual C++.NET 2002(VC 7.0)开始,一些存在于MFC的基础类被重写和修订,削减了与
其他MFC类之间的关系。这些类库可以独立被用于任何的native C++工程。
类
- CFileTime
- CFileTimeSpan
- CFixedStringT
- CImage
- COleDateTime
- COleDateTimeSpan
- CPoint
- CRect
- CSimpleStringT
- CSize
- CStrBufT
- CStringData
- CStringT
- CTime
- CTimeSpan
- IAtlStringMgr
CSimpleStringT
|
+--CStringT
|
+--CFixedStringT
测试代码
例子
https://github.com/codepongo/utocode/tree/master/windows/independentmfc
int
main(int argc, char* argv[])
{
/* string */
CStringT< char, StrTraitATL< char, ChTraitsCRT< char > > > str;
str = "hi";
str += ",independent MFC!\n";
printf(str);
/* point size rect */
CPoint pt;
CRect rc;
CSize sz;
rc.left = rc.top = 0;
rc.right = rc.bottom = 100;
sz = rc.Size();
pt.x = 10;
pt.y = 10;
printf("rect(%d, %d, %d, %d):(%d,%d) move (%d, %d)\n",
rc.left, rc.top, rc.right, rc.bottom, sz.cx, sz.cy, pt.x, pt.y);
/* time */
CTime tms;
CTime tm;
str.Format("%04d-%02d-%02d %02d:%02d:%02d\n",
tms.GetYear(), tms.GetMonth(), tms.GetDay(), tm.GetHour(), tm.GetMinute(), tm.GetSecond());
printf(str);
CFileTime ft;
CFileTimeSpan fts;
COleDateTime dt;
COleDateTimeSpan dts;
str.Format("%lld\n", ft.GetTime());
printf(str);
/* image */
CImage image;
image.Load(L"image.png");
str.Format("width:%d height%d\n", image.GetWidth(), image.GetHeight());
printf(str);
system("pause");
return 0;
}

参考
ATL/MFC Shared Classes
Classes Shared by MFC and ATL
MFC Hierarchy Chart
CString模板结构解析
日期: 2014-01-10 17:30:06, 11 years and 6 days ago