Design&Art

C++博客 首页 新随笔 联系 聚合 管理
  26 Posts :: 0 Stories :: 38 Comments :: 0 Trackbacks

2009年4月11日 #

一、对于基本声明 

1.const int r=100; //标准const变量声明加初始化,因为默认内部连接所以必须被初始化,其作用域为此文件,编译器经过类型检查后直接用100在编译时替换。 

2.extend const int r=100; //将const改为外部连接,作用于扩大至全局,编译时会分配内存,并且可以不进行初始化,仅仅作为声明,编译器认为在程序其他地方进行了定义。 

3.const int r[ ]={1,2,3,4}; 

struct S {int a,b;}; 
const S s[ ]={(1,2),(3.4)}; //以上两种都是常量集合,编译器会为其分配内存,所以不能在编译期间使用其中的值,例如:int temp[r[2]];这样的编译器会报告不能找到常量表达式 

二、对于指针 
1.const int *r=&x; //声明r为一个指向常量的x的指针,r指向的对象不能被修改,但他可以指向任何地址的常量。 

2.int const *r=&x; //与用法1完全等价,没有任何区别。 

3.int * const r=&x; //声明r为一个常量指针,他指向x,r这个指针的指向不能被修改,但他指向的地址的内容可以修改。 

4.const int * const r=&x; //综合1、3用法,r是一个指向常量的常量型指针。 

三、对于类型检查 
可以把一个非const对象赋给一个指向const的指针,因为有时候我们不想从这个指针来修改其对象的值;但是不可以把一个const对象赋值给一个非const指针,因为这样可能会通过这个指针改变指向对象的值,但也存在使这种操作通过的合法化写法,使用类型强制转换可以通过指针改变const对象: 

const int r=100; 
int * ptr = const_cast<int *>(&r); //C++标准,C语言使用:int * ptr =(int*)&r; 

四、对于字符数组 
如char * name = “china”; 这样的语句,在编译时是能够通过的,但是”china”是常量字符数组,任何想修改他的操作也能通过编译但会引起运行时错误,如果我们想修改字符数组的话就要使用char name[ ] = “china”; 这种形式。 

五、对于函数 
1.void Fuction1 ( const int r ); //此处为参数传递const值,意义是变量初值不能被函数改变 

2.const int Fuction1 (int); //此处返回const值,意思指返回的原函数里的变量的初值不能被修改,但是函数按值返回的这个变量被制成副本,能不能被修改就没有了意义,它可以被赋给任何的const或非const类型变量,完全不需要加上这个const关键字。但这只对于内部类型而言(因为内部类型返回的肯定是一个值,而不会返回一个变量,不会作为左值使用),对于用户自定义类型,返回值是常量是非常重要的,见下面条款3。 

3.Class CX; //内部有构造函数,声明如CX(int r =0) 

CX Fuction1 () { return CX(); } 

const CX Fuction2 () { return CX(); } 

如有上面的自定义类CX,和函数Fuction1()和Fuction2(),我们进行如下操作时: 

Fuction1() = CX(1); //没有问题,可以作为左值调用 

Fuction2() = CX(1); //编译错误,const返回值禁止作为左值调用。因为左值把返回值作为变量会修改其返回值,const声明禁止这种修改。 


4.函数中指针的const传递和返回: 

int F1 (const char * pstr); //作为传递的时候使用const修饰可以保证不会通过这个指针来修改传递参数的初值,这里在函数内部任何修改*pstr的企图都会引起编译错误。 

const char * F2 (); //意义是函数返回的指针指向的对象是一个const对象,它必须赋给一个同样是指向const对象的指针。 

const char * const F3(); //比上面多了一个const,这个const的意义只是在他被用作左值时有效,它表明了这个指针除了指向const对象外,它本身也不能被修改,所以就不能当作左值来处理。 


5.函数中引用的const传递: 

void F1 ( const X& px); //这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的,他禁止对引用的对象的一切修改,唯一不同的是按值传递会先建立一个类对象的副本,然后传递过去,而它直接传递地址,所以这种传递比按值传递更有效。 

**另外只有引用的const传递可以传递一个临时对象,因为临时对象都是const属性,且是不可见的,他短时间存在一个局部域中,所以不能使用指针,只有引用的const传递能够捕捉到这个家伙。 

六、对于类 
1.首先,对于const的成员变量,只能在构造函数里使用初始化成员列表来初始化,试图在构造函数体内进行初始化const成员变量会引起编译错误。初始化成员列表形如: 
2.X:: X ( int ir ): r(ir) {} //假设r是类X的const成员变量 

2.const成员函数。提到这个概念首先要谈到const对象,正象内置类型能够定义const对象一样(const int r=10;),用户自定义类型也可以定义const对象(const X px(10);),编译器要保证这个对象在其生命周期内不能够被改变。如果你定义了这样的一个const对象,那么对于这个对象的一切非const成员函数的调用,编译器为了保证对象的const特性,都会禁止并在编译期间报错。所以如果你想让你的成员函数能够在const对象上进行操作的话,就要把这个函数声明为const成员函数。假如f( )是类中的成员函数的话,它的声明形如: 
int f( ) const; //const放在函数的最后,编译器会对这个函数进行检查,在这个函数中的任何试图改变成员变量和调用非const成员函数的操作都被视为非法 
注意:类的构造和析构函数都不能是const函数。 

3.建立了一个const成员函数,但仍然想用这个函数改变对象内部的数据。这样的一个要求也会经常遇到,尤其是在一个苛刻的面试考官那里。首先我们要弄清楚考官的要求,因为有两种方法可以实现,如果这位考官要求不改变原来类的任何东西,只让你从当前这个const成员函数入手,那么你只有使用前面提到的类型强制转换方法。实例如下: 

//假如有一个叫做X的类,它有一个int成员变量r,我们需要通过一个const成员函数f( )来对这个r进行++r操作,代码如下 

void X::f( ) const 

{ (const_cast(this)) -> ++r; } //通过this指针进行类型强制转换实现 

另外一种方法就是使用关键字:mutable。如果你的成员变量在定义时是这个样子的: 

mutable int r ; 

那么它就告诉编译器这个成员变量可以通过const成员函数改变。编译器就不会再理会对他的检查了。 
posted @ 2009-04-11 00:51 安帛伟 阅读(311) | 评论 (0)编辑 收藏

2009年3月4日 #

在VC6中,如何查看以下代码中vec里的内容?

    vector<int> vec;
    vec.push_back(
1);
    vec.push_back(
14);
如果在Watch窗口中直接输入vec,则会出现如下内容:

众所周知,vector使用的是线性连续存储空间,上图中的_First和_Last分别指向配置得来的连续空间中目前已被使用的范围,而_End指向整块连续空间的尾端。
因此,我们可以用如下方式来查看vector里的内容:
vec._First[0]
vec._First[1]


同理,对于嵌套的vector(如下代码中的vv)
    vector<int> vec;
    vec.push_back(
1);
    vec.push_back(
14);
    vector
< vector<int> > vv;
    vv.push_back(vec);
    vec.push_back(
15);
    vv.push_back(vec);
我们可以这样查看:
vv._First[1]._First[2]
posted @ 2009-03-04 14:42 安帛伟 阅读(4999) | 评论 (1)编辑 收藏

2009年1月22日 #

STL的map表里有一个erase方法用来从一个map中删除掉指令的节点
eg:

map<string,string> mapTest;
typedef map
<string,string>::iterator ITER;

ITER iter
=mapTest.find(key);
mapTest.erase(iter);

 像上面这样只是删除单个节点,map的形为不会出现任务问题,
但是当在一个循环里用的时候,往往会被误用,那是因为使用者没有正确理解iterator的概念.
像下面这样的一个例子就是错误的写法,
eg:

for(ITER iter=mapTest.begin();iter!=mapTest.end();++iter)
{
cout
<<iter->first<<":"<<iter->second<<endl;
mapTest.erase(iter);
}

这是一种错误的写法,会导致程序行为不可知.究其原因是map 是关联容器,对于关联容器来说,如果某一个元素已经被删除,那么其对应的迭代器就失效了,不应该再被使用;否则会导致程序无定义的行为。
可以用以下方法解决这问题:
正确的写法
1.使用删除之前的迭代器定位下一个元素。STL建议的使用方式

for(ITER iter=mapTest.begin();iter!=mapTest.end();)
{
cout
<<iter->first<<":"<<iter->second<<endl;
mapTest.erase(iter
++);
}

2. erase() 成员函数返回下一个元素的迭代器

for(ITER iter=mapTest.begin();iter!=mapTest.end();)
{
cout
<<iter->first<<":"<<iter->second<<endl;
iter
=mapTest.erase(iter);
}
posted @ 2009-01-22 13:33 安帛伟 阅读(26323) | 评论 (7)编辑 收藏

2008年5月28日 #

开闭原则(OCP):对扩展开放,对修改封闭
里氏代换原则(LSP):子类可替换父类,反之不行
依赖倒置原则(DIP):依赖于抽象而不是依赖与具体
接口隔离原则(ISP):多个专门接口比一个总接口好
合成/聚合复用原则(CARP):尽量使用合成/聚合,而不是继承
迪米特法则(LoD):不要和陌生人说话
posted @ 2008-05-28 03:11 安帛伟 阅读(513) | 评论 (1)编辑 收藏

2007年12月18日 #

GMOS击键模型

GMOSgoals/objects/methods/slecetion rules

基本操作时间

名称和助记

典型值

含义

击键(Keying),K

0.2

敲击键盘上的一个键所需要的时间

指向(Pointing),P

1.1

用户指向显示屏上某一位置所需要的时间

归位(Homing),H

0.4

用户将手从键盘移动到鼠标或者从鼠标移动到键盘需要的时间

心理准备(Mentally preparing),M

1.35

用户进入下一步所需要的心理准备时间

响应(Responding),R

 

 

 

插入删除心理准备活动的规则

规则0 候选M的初始插入

在所有的K之前插入M。在所有用于选择命令的P之前插入M。但是对于选择命令参数的P,不要插入M

规则1 预期M的删除

如果M前面的操作符号能完全预期M后边的一个操作符,则将该M删除。例如移动鼠标并点击目标,就需要删除按规则0插入的M,变PMKPK

规则2 认知但愿内M的删除

如果一串MK属于同一个认知单元,则删除除了第一个以外的所有M。例如连续输入一个单词或者多个数字。

规则3 连续终结符之前M的删除

如果K是一个认知单元后面的多余分隔符,如命令的分隔符后面紧跟着参数的分隔符,则将之前的M删除。

规则4 作为命令终结符的M的删除

如果K是一个分隔符,且后面紧跟着一个常量字符串,则将之前的M删除。

规则5 重叠M的删除

不要记入任何与R重叠的M

 

举例:

设计一个软件可以完成如下功能,可以把摄氏温度转换成华氏温度,也可以把华氏温度转换成摄氏温度。

界面一如下:


缺省选项没有选中

HPKHKKKKK

HMPMKMHMKMKMKMKMK

HMPKHMKKKKMK

2H3M1P6K = 2*0.4 + 3*1.35 + 1.1 + 6*0.2 = 7.15

 

缺省选项选中

MKKKKMK

2M5K = 2*1.35 + 5*0.2 = 3.7

 

取两者的平均值(7.15 + 3.7/2 =5.4

posted @ 2007-12-18 20:40 安帛伟 阅读(1042) | 评论 (0)编辑 收藏

2007年11月26日 #

选自 Martin Fowler的《Refactoring,Improving the design of the existing code》一书

1.Duplicated Code
  代码重复几乎是最常见的异味了。他也是Refactoring 的主要目标之一。代码重复往往来自于copy-and-paste 的编程风格。与他相对应OAOO 是一个好系统的重要标志。

2.Long method
  它是传统结构化的“遗毒“。一个方法应当具有自我独立的意图,不要把几个意图放在一起。

3.Large Class
  大类就是你把太多的责任交给了一个类。这里的规则是One Class One Responsibility。

4.Divergent Change
  一个类里面的内容变化率不同。某些状态一个小时变一次,某些则几个月一年才变一次;某些状态因为这方面的原因发生变化,而另一些则因为其他方面的原因变一次。面向对象的抽象就是把相对不变的和相对变化相隔离。把问题变化的一方面和另一方面相隔离。这使得这些相对不变的可以重用。问题变化的每个方面都可以单独重用。这种相异变化的共存使得重用非常困难。

5.Shotgun Surgery
  这正好和上面相反。对系统一个地方的改变涉及到其他许多地方的相关改变。这些变化率和变化内容相似的状态和行为通常应当放在同一个类中。

6.Feature Envy
  对象的目的就是封装状态以及与这些状态紧密相关的行为。如果一个类的方法频繁用get 方法存取其他类的状态进行计算,那么你要考虑把行为移到涉及状态数目最多的那个类。

7.Data Clumps
  某些数据通常像孩子一样成群玩耍:一起出现在很多类的成员变量中,一起出现在许多方法的参数中…..,这些数据或许应该自己独立形成对象。

8.Primitive Obsession
  面向对象的新手通常习惯使用几个原始类型的数据来表示一个概念。譬如对于范围,他们会使用两个数字。对于Money,他们会用一个浮点数来表示。因为你没有使用对象来表达问题中存在的概念,这使得代码变的难以理解,解决问题的难度大大增加。
  好的习惯是扩充语言所能提供原始类型,用小对象来表示范围、金额、转化率、邮政编码等等。

9.Switch Statement
  基于常量的开关语句是 OO 的大敌,你应当把他变为子类、state 或strategy。

10. Parallel Inheritance Hierarchies
  并行的继承层次是shotgun surgery 的特殊情况。因为当你改变一个层次中的某一个类时,你必须同时改变另外一个层次的并行子类。

11. Lazy Class
  一个干活不多的类。类的维护需要额外的开销,如果一个类承担了太少的责任,应当消除它。

12. Speculative Generality
  一个类实现了从未用到的功能和通用性。通常这样的类或方法唯一的用户是testcase。不要犹豫,删除它。

13. Temporary Field
  一个对象的属性可能只在某些情况下才有意义。这样的代码将难以理解。专门建立一个对象来持有这样的孤儿属性,把只和他相关的行为移到该类。最常见的是一个特定的算法需要某些只有该算法才有用的变量。

14. Message Chain
  消息链发生于当一个客户向一个对象要求另一个对象,然后客户又向这另一对象要求另一个对象,再向这另一个对象要求另一个对象,如此如此。这时,你需要隐藏分派。

15. Middle Man
  对象的基本特性之一就是封装,而你经常会通过分派去实现封装。但是这一步不能走得太远,如果你发现一个类接口的一大半方法都在做分派,你可能需要移去这个中间人。

16. Inappropriate Intimacy
  某些类相互之间太亲密,它们花费了太多的时间去砖研别人的私有部分。对人类而言,我们也许不应该太假正经,但我们应当让自己的类严格遵守禁欲主义。

17. Alternative Classes with Different Interfaces
  做相同事情的方法有不同的函数signature,一致把它们往类层次上移,直至协议一致。

18. Incomplete Library Class
  要建立一个好的类库非常困难。我们大量的程序工作都基于类库实现。然而,如此广泛而又相异的目标对库构建者提出了苛刻的要求。库构建者也不是万能的。有时候我们会发现库类无法实现我们需要的功能。而直接对库类的修改有非常困难。这时候就需要用各种手段进行Refactoring。

19. Data Class
  对象包括状态和行为。如果一个类只有状态没有行为,那么肯定有什么地方出问题了。

20. Refused Bequest
  超类传下来很多行为和状态,而子类只是用了其中的很小一部分。这通常意味着你的类层次有问题。

21. Comments
  经常觉得要写很多注释表示你的代码难以理解。如果这种感觉太多,表示你需要Refactoring。

 

posted @ 2007-11-26 17:09 安帛伟 阅读(396) | 评论 (0)编辑 收藏

2007年11月21日 #

     摘要: C++的static有两种用法:面向过程程序设计中的static和面向对象程序设计中的static。前者应用于普通变量和函数,不涉及类;后者主要说明static在类中的作用。
  阅读全文
posted @ 2007-11-21 01:10 安帛伟 阅读(312) | 评论 (0)编辑 收藏

2007年11月9日 #

在网上看到这样一小段有意思的代码:
int main()
{
    
int i;
    
int a[10];
    
for(i=0; i<=10; i++)
    {
        a[i]
=0;
        printf(
"%d ",a[i]);
    }
    
return 0;
}
这段代码里的错误很明显,数组a在循环时越界了。不过在VC6下编译运行后的结果很有意思,是个无限循环,知道为什么吗?
posted @ 2007-11-09 20:00 安帛伟 阅读(298) | 评论 (0)编辑 收藏

2007年10月18日 #

     摘要: 处理在程序的运行时刻发生的错误,对于任何一个程序设计者来讲都是不陌生的。对于错误的处理,我们有很多方法,本篇着重介绍的是C++中的错误异常处理。  阅读全文
posted @ 2007-10-18 15:38 安帛伟 阅读(472) | 评论 (0)编辑 收藏

2007年10月15日 #

在网上搜索“什么是哲学”,最后发现哲学没有明确的定义(至少中文的没找到)。
不过以我的理解,哲学就是思辩。思考会让人认识到以前未认识到的东西,辩论则会减少思考过程中的错误认识。通过这样的手段,人类得以发展出现代科学体系。
一切建立在思辩基础之上的学科都是哲学的衍生学科,这包括所有的自然科学。计算机科学是自然科学的一个分支,所以也是哲学,这也就是为什么英文里的Ph.D(Doctor of Philosophy Degree 博士学位)是指所有自然科学的博士而非特指哲学专业的博士的原因。
有一个老游戏“文明”,游戏中最先研究出“哲学”的国家将直接进入“黄金时代”,生产力大大提高。这一点设计得非常切合实际,也说明了哲学在人类发展史上的重要性。
就中国来说,显然哲学这个词被“马克思主义哲学”所误用,一提哲学好像都与马克思主义有关,其实不然。“坚持XXXX不动摇”这一类的言论其本身就不具有思辩的特性。我并不是说XXXX不正确,只是如果不去辩论,怎么能知道XXXX是否正确呢;如果XXXX本身是正确的,那么辩论不是可以更好的体现出它的正确性吗?
中国经历了二千年的封建制度,在这二千年里中国显然没有发展出哲学,所以自然科学体系没有在中国历史中产生也就很正常了。“勾股定理”、四大发明等,这些可能是中国历史上为数不多的与自然科学相关的成果了,但是没有哲学,一切成果也没有办法进一步发展成科学体系了。中国的封建制度显然发展到了一个很高的高度,该体系应该说是很完善的:“君贵民轻”的教育制度;儒家思想被统治阶段所利用;中央集权与军队的使用。所有这些都延长了封建制度的存在时间,不得不说,中国人还是很聪明的,只可惜聪明反被聪明误。那些封建制度不那么完善的地方,会更早地发现封建制度的缺点,也就更早的产生新的政治制度。而哲学,则是新制度产生的基础,通过思辨,我们可以发现“‘君’其实并不那么贵,‘民’也并不是那么轻”;当统治阶段想要灌输某种有利于他们统治的思想时,思辩会帮助我们明辨是非。
事实上中国并非没有思辩,只不过思辨的思想没有占据统治地位,这一点一直到现在也是这样,不然为什么会有人删除我们的帖子呢?:P
posted @ 2007-10-15 15:31 安帛伟 阅读(218) | 评论 (0)编辑 收藏

仅列出标题  下一页