春暖花开
雪化了,花开了,春天来了
posts - 149,comments - 125,trackbacks - 0

摘自:http://www.diybl.com/course/3_program/c++/cppjs/2008426/111619.html
  
      去年11月的MSDN杂志曾刊登过一篇文章 Break Free of Code Deadlocks in Critical Sections Under Windows ,Matt Pietrek 和 Russ Osterlund 两位对临界区(Critical Section)的内部实现做了一次简短的介绍,但点到为止,没有继续深入下去,当时给我的感觉就是痒痒的,呵呵,于是用IDA和SoftIce大致分析了一下临界区的实现,大致弄明白了原理后也就没有深究。现在乘着Win2k源码的东风,重新分析一下这块的内容,做个小小的总结吧 :P
     临界区(Critical Section)是Win32中提供的一种轻量级的同步机制,与互斥(Mutex)和事件(Event)等内核同步对象相比,临界区是完全在用户态维护的,所以仅能在同一进程内供线程同步使用,但也因此无需在使用时进行用户态和核心态之间的切换,工作效率大大高于其它同步机制。
     临界区的使用方法非常简单,使用 InitializeCriticalSection 或 InitializeCriticalSectionAndSpinCount 函数初始化一个 CRITICAL_SECTION 结构;使用 SetCriticalSectionSpinCount 函数设置临界区的Spin计数器;然后使用 EnterCriticalSection 或 TryEnterCriticalSection 获取临界区的所有权;完成需要同步的操作后,使用 LeaveCriticalSection 函数释放临界区;最后使用 DeleteCriticalSection 函数析构临界区结构。
     以下是MSDN中提供的一个简单的例子:

    以下为引用:

 // Global variable
 CRITICAL_SECTION CriticalSection;

 void main()
 {
     ...

     // Initialize the critical section one time only.
     if (!InitializeCriticalSectionAndSpinCount(&CriticalSection, 0x80000400) )
         return;
     ...

     // Release resources used by the critical section object.
     DeleteCriticalSection(&CriticalSection)
 }

 DWORD WINAPI ThreadProc( LPVOID lpParameter )
 {
     ...

     // Request ownership of the critical section.
     EnterCriticalSection(&CriticalSection);

     // Access the shared resource.

     // Release ownership of the critical section.
     LeaveCriticalSection(&CriticalSection);

     ...
 }

     首先看看构造和析构临界区结构的函数。
     InitializeCriticalSection 函数(ntosdll esource.c:1210)实际上是调用 InitializeCriticalSectionAndSpinCount 函数(resource.c:1266)完成功能的,只不过传入一个值为0的初始Spin计数器;InitializeCriticalSectionAndSpinCount 函数主要完成两部分工作:初始化 RTL_CRITICAL_SECTION 结构和 RTL_CRITICAL_SECTION_DEBUG 结构。前者是临界区的核心结构,下面将着重讨论;后者是调试用结构,Matt 那篇文章里面分析的很清楚了,我这儿就不罗嗦了 :P
     RTL_CRITICAL_SECTION结构在winnt.h中定义如下:

以下为引用:

 typedef struct _RTL_CRITICAL_SECTION {
     PRTL_CRITICAL_SECTION_DEBUG DebugInfo;

     //
     //  The following three fields control entering and exiting the critical
     //  section for the resource
     //

     LONG LockCount;
     LONG RecursionCount;
     HANDLE OwningThread;        // from the thread''s

ClientId->UniqueThread
     HANDLE LockSemaphore;
     ULONG_PTR SpinCount;        // force size on 64-bit systems when packed
 } RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;

     InitializeCriticalSectionAndSpinCount 函数中首先对临界区结构进行了初始化

     DebugInfo 字段指向初始化临界区时分配的RTL_CRITICAL_SECTION_DEBUG结构;
     LockCount 字段是临界区中最重要的字段,初始值为-1,当临界区被获取(Hold)时此字段大于等于0;
     RecursionCount 字段保存当前临界区所有者线程的获取缓冲区嵌套层数,初始值为0;
     OwningThread 字段保存当前临界区所有者线程的句柄,初始值为0;
     LockSemaphore 字段实际上是一个auto-reset的事件句柄,用于唤醒等待获取临界区的阻塞线程,初始值为0;
     SpinCount 字段用于在多处理器环境下完成轻量级的CPU见同步,单处理器时没有使用(初始值为0),多处理器时设置为SpinCount参数值(最大为MAX_SPIN_COUNT=0x00ffffff)。此外 RtlSetCriticalSectionSpinCount 函数(resource.c:1374)的代码与这儿设置SpinCount的代码完全一样。

     初始化临界区结构后,函数会根据SpinCount参数的一个标志位判断是否需要预先初始化 LockSemaphore 字段,如果需要则使用NtCreateEvent创建一个具有访问权限的同步用事件核心对象,初始状态为没有激发。这一初始化本来是在 EnterCriticalSection 函数中完成的,将之显式提前可以进一步优化 EnterCriticalSection 函数的性能。
     值得注意的是,这一特性仅对Win2k有效。MSDN里面说明如下:

以下为引用:

 Windows 2000:  If the high-order bit is set, the function preallocates the event used by the EnterCriticalSection function. Do not set this bit if you are creating a large number of critical section objects, because it will consume a significant amount of nonpaged pool. This flag is not necessary on Windows XP and later, and it is ignored.

     与之对应的 DeleteCriticalSection 函数完成关闭事件句柄和是否调试结构的功能。

     临界区真正的核心代码在win2kprivate tosdlli386critsect.asm里面,包括_RtlEnterCriticalSection、_RtlTryEnterCriticalSection和_RtlLeaveCriticalSection三个函数。

     _RtlEnterCriticalSection 函数 (critsect.asm:85) 首先检查SpinCount是否为0,如果不为0则处理多处理器架构下的问题[分支1];如为0则使用原子操作给LockCount加一,并判断是否其值为0。如果加一后LockCount大于0,则此临界区已经被获取[分支2];如为0则获取当前线程TEB中的线程句柄,保存在临界区的OwningThread字段中,并将RecursionCount设置为1。调试版本则调用RtlpCriticalSectionIsOwned函数在调试模式下验证此缓冲区是被当前线程获取的,否则在调试模式下激活调试器。最后还会更新TEB的CountOfOwnedCriticalSections计数器,以及临界区调试结构中的EntryCount字段。
     如果此临界区已经被获取[分支2],则判断获取临界区的线程句柄是否与当前线程相符。如果是同一线程则直接将RecursionCount和调试结构的EntryCount字段加一;如果不是当前线程,则调用RtlpWaitForCriticalSection函数等待此临界区,并从头开始执行获取临界区的程序。
     多CPU情况的分支处理方式类似,只是多了对SpinCount的双重检查处理。

     接着的_RtlTryEnterCriticalSection(critsect.asm:343)函数是一个轻量级的尝试获取临界区的函数。伪代码如下:

以下为引用:

 if(CriticalSection->LockCount == -1)
 {
   // 临界区可用
   CriticalSection->LockCount = 0;
   CriticalSection->OwningThread = TEB->ClientID;
   CriticalSection->RecursionCount = 1;

   return TRUE;
 }
 else
 {
   if(CriticalSection->OwningThread == TEB->ClientID)
   {
     // 临界区是当前线程获取
     CriticalSection->LockCount++;
     CriticalSection->RecursionCount++;

     return TRUE;
   }
   else
   {
     // 临界区已被其它线程获取
     return FALSE;
   }
 }
 
 

 

 

     最后的_RtlLeaveCriticalSection(critsect.asm:271)函数释放已获取的临界区,实现就比较简单了,实际上就是对嵌套计数器和锁定计数器进行操作。伪代码如下:

 

以下为引用:

 if(--CriticalSection->RecursionCount == 0)
 {
bsp;  // 临界区已不再被使用
   CriticalSection->OwningThread = 0;
 

   if(--CriticalSection->LockCount)
   {
     // 仍有线程锁定在临界区上
     _RtlpUnWaitCriticalSection(CriticalSection)
   }
 }
 else
 {
   --CriticalSection->LockCount
 }

posted @ 2008-12-01 14:05 Sandy 阅读(1796) | 评论 (0)编辑 收藏
一、宏中“#”和“##”的用法:

        一般用法:使用“#”把宏参数变为一个字符串,用”##”把两个宏参数结合在一起

       例子:

#include <iostream>
using namespace std;

#define TEST1(x) (cout<<id##x<<endl);
#define TEST2(p) (cout<<#p<<endl);
int main()
{
    
int id1 = 1001;
    
int id2 = 1002;
    TEST1(
1);    // == cout<< id1 << endl;
    TEST2(2);    // == cout<< "2" << endl;
    TEST1(2);    // == cout<< id2 << endl;

    system(
"pause");
    
return 0;
}

二、防止一个头文件被重复包含
        #ifndef COMDEF_H
        #define COMDEF_H
        //头文件内容
        #endif
    当你所建的工程有多个源文件组成时,很可能会在多个文件里头包含了同一个头文件,如果借用上面的宏定义就能够避免同一个头文件被重复包含时进行多次编译。因为当它编译第一个头文件时总是没有定义#define COMDEF_H,那么它将编译一遍头文件中所有的内容,包括定义#define COMDEF_H。这样编译再往下进行时如果遇到同样要编译的头文件,那么由于语句#ifndef COMDEF_H的存在它将不再重复的编译这个头文件。

三、常用的宏定义
  __DATE__
  进行预处理的日期(“Mmm dd yyyy”形式的字符串文字)

  __FILE__
  代表当前源代码文件名的字符串文字

  __LINE__
  代表当前源代码中的行号的整数常量

  __TIME__
  源文件编译时间,格式微“hh:mm:ss”

参考文章:
   C中的预编译宏定义  http://blog.readnovel.com/article/htm/tid_900939.html
   C标准中一些预定义的宏 http://www.programfan.com/article/2883.html
   C语言常用宏定义技巧  http://blog.21ic.com/user1/3074/archives/2008/51567.html
   C语言宏定义技巧(常用宏定义) http://blog.21ic.com/user1/69/archives/2006/13695.html
   宏定义:http://blog.csdn.net/believefym/archive/2007/10/21/1836162.aspx

好好学习!
 
posted @ 2008-12-01 13:54 Sandy 阅读(1869) | 评论 (0)编辑 收藏
好文章,大家一起分享。

一直对extern "c"不是很明白。今天,看了一篇文章《C++中extern “C”含义深层探索》,对这个问题终于有所认识。转过来与大家分享。

链接:http://developer.51cto.com/art/200510/9066.htm
作者:宋宝华

1.引言
C++语言的创建初衷是“a better C”,但是这并不意味着C++中类似C语言的全局变量和函数所采用的编译和连接方式与C语言完全相同。作为一种欲与C兼容的语言,C++保留了一部分过程式语言的特点(被世人称为“不彻底地面向对象”),因而它可以定义不属于任何类的全局变量和函数。但是,C++毕竟是一种面向对象的程序设计语言,为了支持函数的重载,C++对全局函数的处理方式与C有明显的不同。
2.从标准头文件说起

某企业曾经给出如下的一道面试题:
面试题:为什么标准头文件都有类似以下的结构?
#ifndef __INCvxWorksh
#define __INCvxWorksh
#ifdef __cplusplus
extern "C" {
#endif
/*...*/
#ifdef __cplusplus
}
#endif
#endif /* __INCvxWorksh */
分析
显然,头文件中的编译宏“#ifndef __INCvxWorksh、#define __INCvxWorksh、#endif” 的作用是防止该头文件被重复引用。
那么
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __cplusplus
}
#endif
的作用又是什么呢?我们将在下文一一道来。

3.深层揭密extern "C"

extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。让我们来详细解读这两重含义。
被extern "C"限定的函数或变量是extern类型的;
extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。记住,下列语句:
extern int a;
仅仅是一个变量的声明,其并不是在定义变量a,并未为a分配内存空间。变量a在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。
通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在连接阶段中从模块A编译生成的目标代码中找到此函数。
与extern对应的关键字是static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被extern “C”修饰。
被extern "C"修饰的变量和函数是按照C语言方式编译和连接的;
未加extern “C”声明时的编译方式
首先看看C++中对类似C的函数是怎样编译的。
作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:
void foo( int x, int y );
该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为“mangled name”)。
foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void foo( int x, int y )与void foo( int x, float y )编译生成的符号是不相同的,后者为_foo_int_float。

同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编写程序的类成员变量可能与全局变量同名,我们以"."来区分。而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。
未加extern "C"声明时的连接方式
假设在C++中,模块A的头文件如下:
// 模块A头文件 moduleA.h
#ifndef MODULE_A_H
#define MODULE_A_H
int foo( int x, int y );
#endif
在模块B中引用该函数:
// 模块B实现文件 moduleB.cpp
#include "moduleA.h"
foo(2,3);

实际上,在连接阶段,连接器会从模块A生成的目标文件moduleA.obj中寻找_foo_int_int这样的符号!
加extern "C"声明后的编译和连接方式
加extern "C"声明后,模块A的头文件变为:
// 模块A头文件 moduleA.h
#ifndef MODULE_A_H
#define MODULE_A_H
extern "C" int foo( int x, int y );
#endif
在模块B的实现文件中仍然调用foo( 2,3 ),其结果是:
(1)模块A编译生成foo的目标代码时,没有对其名字进行特殊处理,采用了C语言的方式;
(2)连接器在为模块B的目标代码寻找foo(2,3)调用时,寻找的是未经修改的符号名_foo。
如果在模块A中函数声明了foo为extern "C"类型,而模块B中包含的是extern int foo( int x, int y ) ,则模块B找不到模块A中的函数;反之亦然。
所以,可以用一句话概括extern “C”这个声明的真实目的(任何语言中的任何语法特性的诞生都不是随意而为的,来源于真实世界的需求驱动。我们在思考问题时,不能只停留在这个语言是怎么做的,还要问一问它为什么要这么做,动机是什么,这样我们可以更深入地理解许多问题):
实现C++与C及其它语言的混合编程。

明白了C++中extern "C"的设立动机,我们下面来具体分析extern "C"通常的使用技巧。

4.extern "C"的惯用法
(1)在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:
extern "C"
{
#include "cExample.h"
}
而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。
笔者编写的C++引用C函数例子工程中包含的三个文件的源代码如下:
/* c语言头文件:cExample.h */
#ifndef C_EXAMPLE_H
#define C_EXAMPLE_H
extern int add(int x,int y);
#endif
/* c语言实现文件:cExample.c */
#include "cExample.h"
int add( int x, int y )
{
return x + y;
}
// c++实现文件,调用add:cppFile.cpp
extern "C"
{
#include "cExample.h"
}
int main(int argc, char* argv[])
{
add(2,3);
return 0;
}
如果C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern "C" { }。
(2)在C中引用C++语言中的函数和变量时,C++的头文件需添加extern "C",但是在C语言中不能直接引用声明了extern "C"的该头文件,应该仅将C文件中将C++中定义的extern "C"函数声明为extern类型。

笔者编写的C引用C++函数例子工程中包含的三个文件的源代码如下:
//C++头文件 cppExample.h
#ifndef CPP_EXAMPLE_H
#define CPP_EXAMPLE_H
extern "C" int add( int x, int y );
#endif
//C++实现文件 cppExample.cpp
#include "cppExample.h"
int add( int x, int y )
{
return x + y;
}
/* C实现文件 cFile.c
/* 这样会编译出错:#include "cExample.h" */
extern int add( int x, int y );
int main( int argc, char* argv[] )
{
add( 2, 3 );
return 0;
}
如果深入理解了第3节中所阐述的extern "C"在编译和连接阶段发挥的作用,就能真正理解本节所阐述的从C++引用C函数和C引用C++函数的惯用法。

posted @ 2008-12-01 11:43 Sandy 阅读(321) | 评论 (0)编辑 收藏
  MUI,其英文全拼为:Multilingual User Interface。这几天思考如何运用多语言的问题,查到了一些相关资料,贴出来与大家分享。
   
   Multilingual User Interface (MUI) 
   http://msdn.microsoft.com/en-us/library/aa913592.aspx
   The Multilingual User Interface (MUI) allows users to change the language of the user interface (UI). To make this possible, the MUI uses a single core binary that includes the system default language, together with one resource dynamic-link library (DLL) for each additional target language. The target device boots with the system default language and then a new user-selected language goes into effect after a soft reset. This switch requires recreating windows, menus, and dialog boxes with the newly loaded resources. In addition, to be considered successful, the language switch must display these elements with the correct fonts and with the correct locale-specific information. 
   
   In  This Section
   For All Platforms: 
   Multilingual User Interface (MUI) Application Development
   Provides an overview of the MUI architecture. Provides instructions on how to work with fonts in a multilingual user-interface environment and shows how to use MUI with applications. 
   Multilingual User Interface (MUI) Reference
   Provides reference pages for the MUI application programming interface.
    For Windows Embedded CE: 
    Multilingual User Interface (MUI) OS Design Development
    Provides information about the MUI support that is helpful when designing and developing a Windows Embedded CE OS. This includes dependency information, the modules and components that implement MUI, and MUI implementation considerations. 
    How to Create a Multilingual Run-time Image Using MUI
    Demonstrates how to create a run-time image in both French and English by using MUI.
    Multilingual User Interface (MUI) Registry Settings
    Provides registry-related information for MUI.
    Multilingual User Interface (MUI) Migration
    Provides information about factors to consider when migrating to a newer version of Windows Embedded CE

posted @ 2008-11-23 11:07 Sandy 阅读(2036) | 评论 (0)编辑 收藏
        
      总在快乐的时候,感到微微的惶恐
     在开怀大笑时,留下感动的泪水
     我无法相信单纯的幸福
     对人生的起伏悲苦,既坦然又不安
                               ————摘自几米漫画

        摘了这么几句与大家分享。心情烦恼得时候,会看看漫画,缓解一些心情。如果你也有心烦意乱的时候,看看漫画,或者看看喜剧,开怀的一笑,让你从你烦恼中脱离。
        最近心情也相当压抑,甚至万分郁闷。周末了,看到这么几句,心有所感,与大家分享。
posted @ 2008-11-22 00:24 Sandy 阅读(567) | 评论 (0)编辑 收藏
如何添加代码注释?
这个我一直没有搞懂。自己随便涂鸦,也懒得加注释。但是真正参加项目,有时也会找理由不添注释。看了一些书,参加一些培训,有的人说,加注释好,有人说加注释不好。好有好的地方,坏也有坏的味道。对于我这个初来乍到之人来说,反而倒是非常迷惑。
最近看到一篇文章“添加代码注释13个技巧”,也解除了心中的部分疑团。贴在这里与大家分享,也备以后参考。
链接:
http://sunxinhe.yo2.cn/articles/%E3%80%90%E8%BD%AC%E3%80%91%E6%B7%BB%E5%8A%A0%E4%BB%A3%E7%A0%81%E6%B3%A8%E9%87%8A13%E4%B8%AA%E6%8A%80%E5%B7%A7.html

添加代码注释13个技巧

作者:José M. Aguilar(西班牙语)
英文译者:Timm Martin
中文译者:numenzq

下面的13个技巧向你展示如何添加代码注释,这些技巧都很容易理解和记忆。

1. 逐层注释
为每个代码块添加注释,并在每一层使用统一的注释方法和风格。例如:
针对每个类:包括摘要信息、作者信息、以及最近修改日期等
针对每个方法:包括用途、功能、参数和返回值等
在团队工作中,采用标准化的注释尤为重要。当然,使用注释规范和工具(例如C#里的XML,Java里的Javadoc)可以更好的推动注释工作完成得更好。
2. 使用分段注释
如果有多个代码块,而每个代码块完成一个单一任务,则在每个代码块前添加一个注释来向读者说明这段代码的功能。例子如下:

// Check that all data records
// are correct
foreach (Record record in records)
{
if (rec.checkStatus()==Status.OK)
{
. . .
}
}
// Now we begin to perform
// transactions
Context ctx = new ApplicationContext();
ctx.BeginTransaction();
. . .

3. 在代码行后添加注释
如果多行代码的每行都要添加注释,则在每行代码后添加该行的注释,这将很容易理解。例如:

const MAX_ITEMS = 10; // maximum number of packets
const MASK = 0x1F;    // mask bit TCP

在分隔代码和注释时,有的开发者使用tab键,而另一些则使用空格键。然而由于tab键在各编辑器和IDE工具之间的表现不一致,因此最好的方法还是使用空格键。

4. 不要侮辱读者的智慧

避免以下显而易见的注释:

if (a == 5)      // if a equals 5
counter = 0; // set the counter to zero

写这些无用的注释会浪费你的时间,并将转移读者对该代码细节的理解。

5. 礼貌点
避免粗鲁的注释,如:“注意,愚蠢的使用者才会输入一个负数”或“刚修复的这个问题出于最初的无能开发者之手”。这样的注释能够反映到它的作者是多么的拙劣,你也永远不知道谁将会阅读这些注释,可能是:你的老板,客户,或者是你刚才侮辱过的无能开发者。

6. 关注要点

不要写过多的需要转意且不易理解的注释。避免ASCII艺术,搞笑,诗情画意,hyperverbosity的注释。简而言之,保持注释简单直接。

7. 使用一致的注释风格
一些人坚信注释应该写到能被非编程者理解的程度。而其他的人则认为注释只要能 被开发人员理解就行了。无论如何,Successful Strategies for Commenting Code已经规定和阐述了注释的一致性和针对的读者。就个人而言,我怀疑大部分非编程人员将会去阅读代码,因此注释应该是针对其他的开发者而言。

8. 使用特有的标签
在一个团队工作中工作时,为了便于与其它程序员沟通,应该采用一致的标签集进行注释。例如,在很多团队中用TODO标签表示该代码段还需要额外的工作。

int Estimate(int x, int y)
{
// TODO: implement the calculations
return 0;
}

注释标签切忌不要用于解释代码,它只是引起注意或传递信息。如果你使用这个技巧,记得追踪并确认这些信息所表示的是什么。

9. 在代码时添加注释
在写代码时就添加注释,这时在你脑海里的是清晰完整的思路。如果在代码最后再添 加同样注释,它将多花费你一倍的时间。而“我没有时间写注释”,“我很忙”和“项目已经延期了”这都是不愿写注释而找的借口。一些开发者觉得应该 write comments before code,用于理清头绪。例如:
public void ProcessOrder()
{
// Make sure the products are available
// Check that the customer is valid
// Send the order to the store
// Generate bill
}

10. 为自己注释代码

当注释代码时,要考虑到不仅将来维护你代码的开发人员要看,而且你自己也可能要看。用Phil Haack大师的话来说就是:“一旦一行代码显示屏幕上,你也就成了这段代码的维护者”。因此,对于我们写得好(差)的注释而言,我们将是第一个受益者(受害者)。

11. 同时更新代码和注释

如果注释没有跟随代码的变化而变化,及时是正确的注释也没有用。代码和注释应该同步变化,否则这样的注释将对维护你代码的开发者带来更大的困难。使用重构工具时应特别注意,它只会自动更新代码而不会修改注释,因此应该立即停止使用重构工具。

12. 注释的黄金规则:易读的代码
对于开发者的一个基本原则就是:让你的代码为己解释。虽然有些人怀疑这会让那些不愿意写注释的开发者钻空子,不过这样的代码真的会使你容易理解,还不需要额外维护注释。例如在Fluid Interfaces文章里向你展示的代码一样:

Calculator calc = new Calculator();
calc.Set(0);
calc.Add(10);
calc.Multiply(2);
calc.Subtract(4);
Console.WriteLine( "Result: {0}", calc.Get() );

在这个例子中,注释是不需要的,否则可能就违反了技巧4。为了使代码更容易理解,你可以考虑使用适当的名字 (Ottinger's Rules里讲解得相当好),确保正确的缩进,并且采用coding style guides,违背这个技巧可能的结果就像是注释在为不好的代码apologize。

13. 与同事分享技巧
虽然技巧10已经向我们表明了我们是如何从好的注释中直接受益,这些技巧将让所有开发者受益,特别是团队中一起工作的同事。因此,为了编写出更容易理解和维护的代码,尝试自由的和你的同事分享这些注释技巧。


好东西拿出来一起分享
posted @ 2008-11-20 09:38 Sandy 阅读(1100) | 评论 (1)编辑 收藏

今天突然遇到Active Sync 4.5 setup.msi这个工具在windows vista系统上装不上这个问题,查了一下,原来在windows vista上,不再采用Active Sync ,而是采用了drvupdate-x86.exe,具体名字叫做Microsoft Windows Mobile 设备中心6.1。微软对其的描述是:
使用 Windows Mobile 设备中心,您可以建立新的合作关系,与 Windows Mobile 设备(Windows Mobile 2003 或更高版本)同步音乐、图片和视频,并对其进行管理。Windows Mobile 设备中心与高效的商务数据同步平台紧密结合,提供了令人耳目一新的用户体验。Windows Mobile 设备中心可以帮助您快速建立新的合作关系,同步重要的商务信息(例如电子邮件、联系人和日历约会),轻松管理同步设置,以及在设备与 PC 间传送商业文档。
这个主要是用于vista系统的。下载地址是
http://www.microsoft.com/downloads/details.aspx?familyid=46F72DF1-E46A-4A5F-A791-09F07AAA1914&displaylang=zh-cn

也可以建议大家去搜一下,我是在新浪上下载的。
链接:http://down1.tech.sina.com.cn/download/down_contents/1183219200/35965.shtml

posted @ 2008-11-19 19:37 Sandy 阅读(1059) | 评论 (0)编辑 收藏

 

        在csdn上闲逛,看到一个这样的问题:如何在PPC上实现多语言的程序列表中的名称在编译时自动切换

        这个曾经让我相当苦恼,看到这个自然不会放过。


         一位牛人很简单的回答:
    
      参考MUIHello 例子: 

       C:\Program Files\Windows CE Tools\wce500\Windows Mobile 5.0 Smartphone SDK\Samples\CPP\Win32\Muihello\app\app.sln 
      C:\Program Files\Windows CE Tools\wce500\Windows Mobile 5.0 Smartphone SDK\Samples\CPP\Win32\Muihello\German_resfile\german_resfile.sln 
      C:\Program Files\Windows CE Tools\wce500\Windows Mobile 5.0 Smartphone SDK\Samples\CPP\Win32\Muihello\Resfile\resfile.sln

      Samples竟然有多语言的例子,让我相当的惊讶!
     学习了一下,比我自己的方法简单很多,看来需要好好学习SDK中提供的例子。

     这个例子会生成muihello.exe.0409.mui,这其实是一个DLL资源文件,有人说这是一种命名规则。在不同的平台上就调用不同的资源,实现多语言。

      在resfile.cpp文件中

#include "windows.h"

/////////////////////////////////////////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HANDLE hInstance, DWORD dwReason, LPVOID lpReserved)
{
    
return TRUE; 
}

     还有一个文件resfile.def
     
LIBRARY      "muihello.exe.0409.mui"

EXPORTS

      而且resource.h是共用一个的。

    尝试了一下,蛮好。
posted @ 2008-11-18 22:22 Sandy 阅读(1432) | 评论 (0)编辑 收藏

Burning    by Maria Arredondo

Passion is sweet

Love makes weak

You said you cherished freedom so

You refused to let it go

Follow your fate

Love and hate

Never failed to seize the day

Don’t give yourself away

Oh when the night falls

And you’re all alone

In your deepest sleep

What are you dreaming of

My skin is still burning from your touch

Oh I just can’t get enough

I said I wouldn’t ask for much

But your eyes are dangerous

So the world keeps spinning in my head

Can we drop this masquerade

I can’t predict where it ends

If you’ re the rock I’ll crush against

Trapped in a crowd

Music is loud

I said I loved my freedom too

Now I’m not so sure I do

All eyes on you

Wings so true

Better quit while you're a head

Now I’m not so sure I am

Oh when the night falls

And you’re all alone

In your deepest sleep

What are you dreaming of

My skin is still burning from your touch

Oh I just can’t get enough

I said I wouldn’t ask for much

But your eyes are dangerous

So the world keeps spinning in my head

Can we drop this masquerade

I can’t predict where it ends

If you’ re the rock I’ll crush against

My soul, my heart

If you’re near or if you’re far

My life, my love

You can have it all

posted @ 2008-11-12 09:18 Sandy 阅读(143) | 评论 (0)编辑 收藏

下面摘自http://www.cppblog.com/woaidongmao/archive/2008/11/03/65897.html

看到他们的在争论很有意思,我不是很懂。

有利还是有弊呢?


EXT_ASSERT将ASSERT与if结合在一起

ASSERT在DEBUG程序时候帮了太多太多忙,不过在ASSERT判断传入参数后,还需要if再按相同条件判断一遍,不符合规则return,这样才是正确的逻辑。但这样代码难看,且工作重复无趣,又容易出现差漏。

刚弄了个简单EXT_ASSERT宏,按我的理解应该可以解决问题,但不确定是否有漏洞,发出来大家一起瞄瞄。

 

#define RET_VOID
#define EX_ASSERT(exp, ret) {ASSERT(exp);if(!(exp))return(ret);}

 

BOOL CXXX::FunXXX(const data* p_data)
{
   EXT_ASSERT(p_data, FALSE);//---- 返回BOOL型

}

int CXXX::FunXXX(const data* p_data)
{
   EXT_ASSERT(p_data, -1);//---- 返回int型

}

const retdata* CXXX::FunXXX(const data* p_data)
{
    EXT_ASSERT(p_data, NULL);//---- 返回NULL指针

}

retdata CXXX::FunXXX(const data* p_data)
{
    EXT_ASSERT(p_data, retdata());//---- 返回空对象

}

void CXXX::FunXXX(const data* p_data)
{
    EXT_ASSERT(p_data, RET_VOID);//---- 仅仅return

}

posted on 2008-11-03 23:34 肥仔 阅读(333) 评论(7)  编辑 收藏 引用 所属分类: C++存档

评论

# re: EXT_ASSERT将ASSERT与if结合在一起  回复  更多评论   

哥们儿,如果你是说MFC里的ASSERT的话(看你的类命名风格,估计是吧),在retail build里,ASSERT是完全不会被放到代码里的。你这样用ASSERT,把ASSERT和if条件绑在一起就等于把处理错误的断言和正常程序逻辑绑在了一起,不是一个好的设计。如果一定要这么干,也该是绑VERIFY,至少在retail build里VERIFY里的逻辑还会被执行。

ASSERT应该拿来断言程序正常执行完全不可能出现的错误(这些错误会在debug build里出现是因为当前程序还不完善),在正常逻辑中,他们是不应该用程序逻辑去handle的错误,所以一句ASSERT够了。
2008-11-04 05:27 | www.helpsoff.com.cn

# re: EXT_ASSERT将ASSERT与if结合在一起  回复  更多评论   

@www.helpsoff.com.cn
我在Imperfect C++中看到过相同的言论。
不过我的应用是,常常用ASSERT检测参数的合法性,ASSERT之后,当然还要if一把了,对于ASSERT和if不要放在一起这种观点,我不是很认同,我觉得放在一起很好用的。

另外,在Release下,ASSERT没了,但是if留下了,这是需要的效果。
2008-11-04 10:45 | 肥仔

# re: EXT_ASSERT将ASSERT与if结合在一起  回复  更多评论   

我不会这么用,断言的目的去那了。
2008-11-04 11:30 | Touchsoft

# re: EXT_ASSERT将ASSERT与if结合在一起  回复  更多评论   

@肥仔
哥们儿,你还是没理解,ASSERT的不是拿来干这个的。你爱怎样玩就怎样玩吧,反正自己的代码自己维护,其他人的意见听不听在你。
2008-11-04 12:29 | www.helpsoff.com.cn

# re: EXT_ASSERT将ASSERT与if结合在一起  回复  更多评论   

@www.helpsoff.com.cn
谢谢你的意见,但是不采纳。原因有3点经历:

1、ASSERT判断函数参数合法性,调试时会帮了很大的忙;
2、if判段函数参数合法性,是健壮性的一部分;
3、ASSERT和if 合在一起,不觉得有任何不妥,且ASSERT不出现在Release中,这正是需要的。

可能涉及到的一个争论是,检测参数合法形是调用者,还是被调用者的责任?
C/C++的主流是调用者保证参数的合法性,被调用者不检测参数合法性,这就是为什么认为,只要ASSERT,不需要if了。
strcpy(szBuf, NULL)之所以让一个程序崩溃也是这个原因,但是为什么要让它崩溃?能够不崩溃,继续执行岂不是更好吗?
2008-11-04 13:44 | 肥仔

# re: EXT_ASSERT将ASSERT与if结合在一起  回复  更多评论   

1) 没人否认ASSERT的用处;
2) 需要if判断处理的参数和用ASSERT断言的不合法参数,不应属于一个范畴,不应该混合在一起处理;
3) 代码不管怎么写在没遇到问题前都不会有什么不妥,自己觉得好就好吧。

你当然可以去写一个万能的strcpy,但是如何能保证你的strcpy是真正的“万能”的呢?不崩溃继续执行倒是没问题,但是出问题的真正根源在哪里呢,你这样做不就掩盖了问题吗?应该做的是出现这样的问题时,能有用且有效的指出错误,而不是做garbage in, garbage out。

设计代码,不去扯那些玩得出花花的设计模式,有些很基本很直白的原则,比如说“garbage in, garbage out”,比如高内聚/低耦合...说多了也没意思,楼主爱怎么玩怎么玩,大家都是这么过来的,其中的东西自己去体会了。
2008-11-04 15:35 | www.helpsoff.com.cn

# re: EXT_ASSERT将ASSERT与if结合在一起  回复  更多评论   

@www.helpsoff.com.cn
程序以外,人生很多地方都需要与别人探讨,对于不合己见者,请不必太在怀,更没必要带着情绪和语气,摆出姿态。这样才能赢得更多的合作,我想我的这几句话还算中肯。
2008-11-04 16:32 | 肥仔
posted @ 2008-11-04 18:28 Sandy 阅读(387) | 评论 (0)编辑 收藏
仅列出标题
共15页: First 7 8 9 10 11 12 13 14 15