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

发现许久没有来园子里逛了,一来是工作了,的确很忙,二来是手的确有点懒。想写点东西,找来找去,还是对这个园子有感情。

最近发现懒惰真的是一个很容易养成的习惯,它会慢慢吞噬掉一切。就项目而言,压力的确比学生时要繁重许多,但条理性似乎更加重要。随手记下,随时总结总归是好的,一个回头,就不知道什么是时候是个头了。

所以半年多的经验来看,一定要及时的总结项目,要把最新鲜的,最热气腾腾的东西记录下来。
对自己回顾是一个帮助,对他人入门也是个捷径。

养成及时总结的习惯很重要。

posted @ 2010-11-16 23:34 Sandy 阅读(268) | 评论 (0)编辑 收藏
今天是一个非常开心的日子,我拿到了我梦寐以求的一家公司的offer,可以继续沿着梦想的道路继续前进,开心。

首先感谢默默支持我的人,在这一路寻找的路上,有过很多挫折,谢谢他们给我的力量,让我越挫越勇。

其次感谢一次次面试中给了我很多帮助的面试官,其实面试是一个互相学习的机会,而且每一次的肯定都是对能力的一种肯定,给了我很多信心。最后可能由于各种原因,与一些公司擦肩而过,也衷心祝愿这些公司的发展越来越好。

最后感谢给了我很多建议的人,让我一路坚持梦想的走下来。 软件开发,是我热爱的一个方向,我也希望能够沿着这条路继续走下去。






小小广告,如果你的网络很慢,不妨试试Opera的Turbo加速,不一般的体验啊!
posted @ 2009-12-21 22:29 Sandy 阅读(336) | 评论 (3)编辑 收藏
摘自:
http://liuaigui.blog.sohu.com/86494742.html

线程安全的(Thread-Safe):如果一个函数在同一时刻可以被多个线程安全地调用,就称该函数是线程安全的。线程安全函数解决多个线程调用函数时访问共享资源的冲突问题。

可重入(Reentrant):函数可以由多于一个线程并发使用,而不必担心数据错误。可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据。可重入性解决函数运行结果的确定性和可重复性。可重入函数编写规范为:
1、不在函数内部使用静态或全局数据
2、不返回静态或全局数据,所有数据都由函数的调用者提供。
3、使用本地数据,或者通过制作全局数据的本地拷贝来保护全局数据。
4、如果必须访问全局变量,利用互斥机制来保护全局变量。
5、不调用不可重入函数。

两者之间的关系:
1、一个函数对于多个线程是可重入的,则这个函数是线程安全的。
2、一个函数是线程安全的,但并不一定是可重入的。
3、可重入性要强于线程安全性。

比如:strtok函数是既不可重入的,也不是线程安全的。加锁的strtok不是可重入的,但线程安全。而strtok_r既是可重入的,也是线程安全的

之所以会去查这个问题,是在考试中遇到过考strtok这个问题是否可用于多线程。自己从使用的角度上感觉不安全,但是究其原因却不是很明白。查了一下,居然涉及到函数的线程安全。

在另一篇博文中,是这样解释的:
strtok不是一个线程安全的函数。因为根据其定义,它必须使用内部静态变量来记录字符串中下一个需要解析的标记的当前位置。但是,由于指示这个位置的变量只有一个,那么,在同一个程序中出现多个解析不同字符串的strtok调用时,各自的字符串的解析就会互相干扰。(摘自: http://hi.baidu.com/pigfanfan/blog/item/72816c958d63e743d1135ebf.html

怎么避免呢?
我们可以利用另一个函数:strtok_r,这个是一个线程安全的函数——strtok_r,以此来代替strtok。
posted @ 2009-10-22 17:33 Sandy 阅读(1168) | 评论 (0)编辑 收藏
 转:C++ pair用法
摘自:http://hi.baidu.com/lucene1983/blog/item/83bb68351d12ffbed1a2d3fe.html

1 pair的应用

pair是将2个数据组合成一个数据,当需要这样的需求时就可以使用pair,如stl中的map就是将key和value放在一起来保存。另一个应用是,当一个函数需要返回2个数据的时候,可以选择pair。 pair的实现是一个结构体,主要的两个成员变量是first second 因为是使用struct不是class,所以可以直接使用pair的成员变量。

2 make_pair函数

template pair make_pair(T1 a, T2 b) { return pair(a, b); }

很明显,我们可以使用pair的构造函数也可以使用make_pair来生成我们需要的pair。 一般make_pair都使用在需要pair做参数的位置,可以直接调用make_pair生成pair对象很方便,代码也很清晰。 另一个使用的方面就是pair可以接受隐式的类型转换,这样可以获得更高的灵活度。灵活度也带来了一些问题如:

std::pair<int, float>(1, 1.1);

std::make_pair(1, 1.1);

是不同的,第一个就是float,而第2个会自己匹配成double。

以上是从网上找来的资料,我又查了一下关于pair的定义,其定义是一个模板结构。
   

        // TEMPLATE STRUCT pair
template<class _Ty1,
    
class _Ty2> struct pair
    
{    // store a pair of values
    typedef pair<_Ty1, _Ty2> _Myt;
    typedef _Ty1 first_type;
    typedef _Ty2 second_type;

    pair()
        : first(_Ty1()), second(_Ty2())
        
{    // construct from defaults
        }


    pair(
const _Ty1& _Val1, const _Ty2& _Val2)
        : first(_Val1), second(_Val2)
        
{    // construct from specified values
        }


    template
<class _Other1,
        
class _Other2>
        pair(
const pair<_Other1, _Other2>& _Right)
        : first(_Right.first), second(_Right.second)
        
{    // construct from compatible pair
        }


    
void swap(_Myt& _Right)
        
{    // exchange contents with _Right
        std::swap(first, _Right.first);
        std::swap(second, _Right.second);
        }


    _Ty1 first;    
// the first stored value
    _Ty2 second;    // the second stored value
    }
;

make_pair同样也是一个模板函数。其定义如下:
template<class _Ty1,
    
class _Ty2> inline
    pair
<_Ty1, _Ty2> make_pair(_Ty1 _Val1, _Ty2 _Val2)
    
{    // return pair composed from arguments
    return (pair<_Ty1, _Ty2>(_Val1, _Val2));
    }

posted @ 2009-10-15 11:09 Sandy 阅读(11492) | 评论 (0)编辑 收藏

有这么一个关于虚函数和虚继承的问题,如下:
class A
{
    char k[3];
public:
    virtual void aa();
};

class B: public virtual A
{
    char j[3];
public:
    virtual void bb();
};

class C: public virtual B
{
    char i[3];
public:
   virtual void cc();
};
请问sizeof(A), sizeof(B), sizeof(C)分别为多少?

对于A, 我们很清楚的知道,其大小为8。
对于B,考虑到虚继承和自身的虚函数,我们也可以算出来起大小为8+8+4 = 20
对于C,其大小为20+8+4 = 32。
其中 4为虚继承所占用的指针。

这个看上去没有什么问题。但是当我把虚继承去掉以后,这里却有了一些变化?
首先,我猜想了一下,A是8,B是16,C是24。
可惜结果和我想的不一样,答案是8, 12, 16。很有规律的一个数字。
从A到B,只增加了4。什么原因呢?

http://www.diybl.com/course/3_program/c++/cppjs/2007927/74925.html这里介绍了一些

The existence of virtual function(s)

Existence of virtual function(s) will add 4 bytes of virtual table pointer in the class, which will be added to size of class. Again, in this case, if the base class of the class already has virtual function(s) either directly or through its base class, then this additional virtual function won't add anything to the size of the class. Virtual table pointer will be common across the class hierarchy. That is

class Base {

public:

 ...        

virtual void SomeFunction(...);

private:  

int iAMem

};

class Derived : public Base

{

 ...       

 virtual void SomeOtherFunction(...);

private:      

int iBMem

};

In the example above, sizeof(Base) will be 8 bytes--that is sizeof(int iAMem) + sizeof(vptr). sizeof(Derived) will be 12 bytes, that is sizeof(int iBMem) + sizeof(Derived). Notice that the existence of virtual functions in class Derived won't add anything more. Now Derived will set the vptr to its own virtual function table.


派生类和基类拥有相同的虚函数表。

但似乎虚继承的时候,又摈弃了这一做法。

所以两个是有所区别的。

posted @ 2009-10-07 22:43 Sandy 阅读(1661) | 评论 (2)编辑 收藏

今天在看面试宝典,注意到上面所说浮点数在内存里和整数的存储方式不同,但究竟有何不同呢?
在网上搜了一下:
http://blog.csdn.net/djsl6071/archive/2007/03/16/1531336.aspx中介绍了浮点数在内存中的存储方式,觉得不错,转过来。

浮点数在内存中的存储方式

浮点数保存的字节格式如下:

地址        +0          +1           +2           +3
内容    SEEE EEEE   EMMM MMMM    MMMM MMMM    MMMM MMMM

这里
S 代表符号位,1是负,0是正
E 偏移127的幂,二进制阶码=(EEEEEEEE)-127。
M 24位的尾数保存在23位中,只存储23位,最高位固定为1。此方法用最较少的位数实现了
较高的有效位数,提高了精度。

零是一个特定值,幂是0 尾数也是0。

浮点数-12.5作为一个十六进制数0xC1480000保存在存储区中,这个值如下:
地址 +0     +1     +2     +3
内容0xC1   0x48   0x00   0x00

浮点数和十六进制等效保存值之间的转换相当简单。下面的例子说明上面的值-12.5如何转
换。
浮点保存值不是一个直接的格式,要转换为一个浮点数,位必须按上面的浮点数保存格式表
所列的那样分开,例如:

地址       +0           +1            +2            +3
格式   SEEE EEEE    EMMM MMMM     MMMM MMMM     MMMM MMMM
二进制  11000001     01001000      00000000      00000000
十六进制   C1           48            00            00

从这个例子可以得到下面的信息:
  符号位是1 表示一个负数
  幂是二进制10000010或十进制130,130减去127是3,就是实际的幂。
  尾数是后面的二进制数10010000000000000000000
在尾数的左边有一个省略的小数点和1,这个1在浮点数的保存中经常省略,加上一个1和小数
点到尾数的开头,得到尾数值如下:
1.10010000000000000000000

接着,根据指数调整尾数.一个负的指数向左移动小数点.一个正的指数向右移动小数点.因为
指数是3,尾数调整如下:
1100.10000000000000000000

结果是一个二进制浮点数,小数点左边的二进制数代表所处位置的2的幂,例如:1100表示
(1*2^3)+(1*2^2)+(0*2^1)+(0*2^0)=12。
小数点的右边也代表所处位置的2的幂,只是幂是负的。例如:.100...表示(1*2^(-1))+
(0*2^(-2))+(0*2^(-2))...=0.5。
这些值的和是12.5。因为设置的符号位表示这数是负的,因此十六进制值0xC1480000表示-
12.5。

关于多字节数据类型在内存中的存储问题


int ,short 分别是4、2字节。他们在内存中的存储方式下面举个例子说明。

int data = 0xf4f3f2f1;
其中低位存放在编址小的内存单元,高位存放在编址高的内存单元
如下:
地址:0x8000      0x8001    0x8002   0x8003
数据:   f1              f2            f3          f4
根据IEEE在1985年制定的标准来处理浮点数
单精度浮点数用4字节,包括1位符号位s(整数为0,负数为1),8位指数位e,23位有效位f
浮点型使用的是科学计数法,比如十进制的12345可以表示为1.2345 * 10^4(表示10的4次幂)
用二进制表示为 1.1000000111001 * 2^13
所以计算机中用浮点数表示12345这个十进制应该是这样的,s位为0,因为是正数,指数位为13+127=140(127为单精度浮点数偏移值,为了表示只有小数部分的数),有效位为1000000111001
计算的时候用 (-1)^s * 1.f * 2^(e-127) ,结果就是 1* 1.1000000111001 * 2^(140-127=13) ,和我们刚才表示的一样
还比如,十进制小数0.125转换为二进制小数0.001可以表示为 1* 1.0 * 2^(124-127=-3)
double,双精度浮点数有1位符号位、11位指数位和52位有效数
谢谢,和我找的资料差不多:)
知道公式
n=(-1)^s*m*2^e
e=|E|-bias
bias = 2^(k-1)-1(k为E的位数)
m=|1.M|


知道12345在内存中的10进制表示以后
0x4640e400 = 0(100 0110 0)<100 0000 1110 0100 0000>
括号中的数字为|E| = 140 所以e=140-127=13
尖括号中的数字为m=|1.M|=|1.100000011100100|=1.506958008
ok,
代入公式n = (-1)^0*1.506958008*2^13=12345
完工!!

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/edivista/archive/2009/06/07/4248794.aspx

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/edivista/archive/2009/06/07/4248794.aspx

posted @ 2009-10-02 20:51 Sandy 阅读(885) | 评论 (0)编辑 收藏

        DLL, 动态链接库, Dynamic-link library.一直以来都是Windows操作系统的基石。Window应用程序编程接口提供的所有函数都包含在DLL中。其中三个最重要的DLL分别是Kernel32.dll,User32.dll,GDI32.dll。Kernel32.dll ,包含的函数用来管理内存、进程以及线程;User32.dll包含的函数用来执行与用户界面相关的任务,如创建窗口和发送消息。GDI32.dll,包含的函数用来绘制图像和显示文字。Windows Mobile中重要的DLL是Coredll.dll.

         Windows系统广泛使用DLL,那么我们使用DLL的理由是什么?下面是在《Windows 核心编程中》摘录的一部分:
         一是扩展了应用程序的特性;
          二是简化了项目的管理;
          三是有助于节省内存;
          四是促进了资源的共享;
          五是促进了本地化;
         六是有助于解决平台间的差异;
         七是可以用于特殊目的。 
         目前用到的地方在 节省内存、资源共享、本地化、特殊目的。这几个是我在项目中见过的。DLL如何使用呢?一般有两种方法,一是隐式载入时链接(implicit load-time linking)和显示运行时链接(explicit run-time linking)。
         隐式链接,常用的方法是将DLL的头文件和LIB文件包含到执行文件程序中,相应DLL放到执行文件对应的目录。基本就OK了。
          例如:
           DLL为MyLib.dll,其头文件为MyLib.h, LIB文件为MyLib.lib。
           我们在执行文件中使用是,需要做如下操作:
           #include "MyLib.h"
           #pragma comment(lib, "MyLib.lib")

           即可实现隐式调用,变量、函数、类就可实现调用。
           这里lib文件非常小,它并不包含任何函数或变量,它只是列出了所有被导出的函数和变量的变量名,具体的实现在dll文件中。编译阶段不包含Lib文件会出错的。
         显示链接,在应用程序运行的过程中,显示地载入所需的DLL。
         一般可以使用LoadLibrary和FreeLibrary来实现加载和卸载DLL。那么如何判断一个DLL是否已经被映射到了进程的地址空间中?我们可以使用GetModuleHandle.
          如:
           HMODULE hInstDll = GetModuleHandle(TEXT("MyLib"));
           if (hInstDll == NULL)
           {
                hInstDll = LoadLibrary(TEXT("MyLib"));
            }
           我们已经知道如何显示加载DLL,那如何显示调用其中的变量、函数、类呢?
          例如在DLL中,我们有一个变量 g_nResult, 函数Add,我们在执行程序中如何调用呢?可以利用GetProcAddress。
           对于变量:
           int nResult =*(int*)GetProcAddress(hInstDll, "g_nResult");
           对于函数:
            typedef   int   (*FUNC)(int, int); 
            FUNC pfn = (FUNC)GetProcAddress(hInstDll, "Add");

           这是今天学习到的关于DLL的一点知识。
            

posted @ 2009-10-02 16:43 Sandy 阅读(475) | 评论 (0)编辑 收藏
条款四 避免不必要的默认构造函数

默认构造函数(指没有参数的构造函数)是指C++语言中,你不用传参数就可以调用的构造函数。构造函数用于初始化对象,而默认构造函数可以在不依赖任何外部信息的情况下创建对象。

在一个完美的世界里,无需任何外部信息即可创建对象的类可以包含默认构造函数,而需要额外信息来创建对象的类则不包含默认构造函数。可是我们的世界是不完美的,所以我们必将一些额外的因素考虑在内。特别地,如果一个类没有默认构造函数,使用这个类的时候就会有一些限制。

没有默认构造函数,在三种情况下它的应用可能会出现问题:
第一个问题是创建数组的时候,通常没有很好的办法可以指定数组元素的构造函数的参数。
第二个问题是没有默认构造函数的类他们无法作为许多基于模板的容器类的类型参数使用。因为通常用于实例化模板的哪些类型需要提供默认构造函数。
第三个问题是在有虚基类的时候应该提供默认构造函数还是不提供默认构造函数。没有默认构造函数的虚基类使用起来很痛苦,这是因为虚基类的构造函数所要求的参数必须由创建对象所属的最远的派生类所提供。

正因为这些强加于没有默认构造函数的类上的重中限制,一些人认为所有的类都应该有默认构造函数,即使默认构造函数没有足够的信息来完全初始化一个对象。

但是默认构造函数会影响类的运行效率,有时会使其他成员函数变得复杂。

如果一个类的构造函数能够确保所有的数据成员被正确初始化,就能避免付出一些代价。通常默认构造函数不提供这些保证。如果默认构造函数对于某些类没有太大意义,最好避免使用他们。这给使用这种类加了一些限制,但是当你使用它时,它可以向你保证你能很放心地相信这个类被正确得初始化,并且具有高效的效率。

说实话,这一章节我看得不是很明白。
作者在一开始,列举了一些没有默认构造函数我们可能遇到的问题,在这些问题下,进而得出默认构造函数所带来的一些效率和代价困扰。看来还需要在实践中仔细揣摩揣摩。



posted @ 2009-09-22 13:33 Sandy 阅读(272) | 评论 (0)编辑 收藏

条款三 绝不要把多态应用于数组

继承的一大特性是,允许你通过指向基类的指针和引用来操纵派生类对象。也允许通过基类指针和引用来操纵派生类数组。

但是用基类指针操纵一个包含派生类对象的数组,就会发生各种个样的问题,其结果往往是不确定的。

我根据书中的例子,写了一个小程序:

#include <iostream>

using namespace std;

class BST
{
public:
    BST()
    
{
        i 
= 0;
    }

    
int i;
}
;

class BalancedBST: public BST
{
private:
    
int j;
}
;

void printBSTArray(ostream& outconst BST arr[], int numElements)
{
    
for (int i = 0; i < numElements; i++)
    
{
        
out << arr[i].i << endl;
    }

}


int main()
{
    cout 
<< "BST.\n";
    BST BSTArray[
10];
    printBSTArray(cout, BSTArray, 
10);

    cout 
<< "BalancedBST.\n";
    BalancedBST bBSTArray[
10];
    printBSTArray(cout, bBSTArray, 
10);

    system(
"pause");

    
return 0;
}


其结果如下:

 

 可以看到程序并不如我们所期望的那样,这说明什么呢?
 arr[i],表示的是*(arr+i),但是arr+i所指向的地址偏离arr所指向的地址是i*(an object in the array)。
因为参数被声明为BST数组类型,那么数组的每个元素必须是BST,那么它们的间隔也毕定是i*sizeof(BST)。如果传入BalancedBST数组,编译器可能就会犯错误,在这种情况下,编译器就会假定数组里每个对象的大小都和BST的大小一样。而通常派生类要比基类有更多的成员变量,所以派生类一般都比基类对象大。所以我们就看到了如上的结果。

 试图通过一个基类指针删除一个包含派生类对象的数组,也会有同样的问题。

所以不要把多台应用到数组上,还是很有好处的。

posted @ 2009-09-22 11:57 Sandy 阅读(197) | 评论 (0)编辑 收藏

昨天想做这么一个操作,就是将文件从一个文件夹拷贝到另一个文件夹中,同时删除源文件夹中的内容。想着挺简单的,于是就首先使用了MoveFile。
当目的文件夹中,不存在文件时,MoveFile成功。而当目的文件夹包含该文件时,MoveFile则调用不成功。他不支持覆盖。所以挪动文件,用MoveFile似乎不是首选。
此时,我想到的是DeleteAndRenameFile,
在SDK中解释到This function deletes the source file after it copies the content of the source file to the destination file. 拷贝源文件的内容到目的文件,然后删除源文件。看似符合要求,但是他要求调用该函数的时候目的文件必须存在。

这两个函数都不可行,结合起来到是不错的选择,可以满足要求。可惜我昨天没有这么想,昨天我想到了CopyFile。
CopyFile的原型是
BOOL CopyFile(
  LPCTSTR lpExistingFileName,
  LPCTSTR lpNewFileName,
  BOOL bFailIfExists
);

最后一个参数表示当目的文件已经存在的时候,CopyFile调用成功还是失败的操作。如果为False,就可以覆盖拷贝了。

于是我就自己写了一个CopyAndDel的小函数。先拷贝再删除原来的文件。删除文件的时候还需要考虑一下文件属性的问题。

BOOL CopyAndDel(LPCTSTR lpExistingFileName, LPCTSTR lpNewFileName)
{
    
if (CopyFile(lpExistingFileName, lpNewFileName, FALSE))
    
{
        
if (!DeleteFile(lpExistingFileName))
        
{
            SetFileAttributes(lpExistingFileName, FILE_ATTRIBUTE_NORMAL);
            
return DeleteFile(lpExistingFileName);
        }

        
return TRUE;
    }

    
return FALSE;
}


这个小函数的缺陷在于还需要考虑删除文件的属性问题。似乎利用MoveFile和DeleteAndRenameFile组合更好一些。

这里总结一下:
MoveFile 重命名一个文件或目录,包括它所有的孩子。可对文件夹进行操作。不过需要保证的是新文件或新文件夹不存在,否则调用失败。
DeleteAndRenameFile 当拷贝源文件的内容到目的文件后,删除源文件。需要保证目的文件在调用前已经存在。
CopyFile 拷贝一个已经存在的文件,并生成一个新的文件。不能对文件夹进行操作。
DeleteFile 删除一个文件。不能删除文件夹。该函数删除失败的时候,考虑一下文件的属性问题。
RemoveDirectory 删除一个空的文件夹。

 

posted @ 2009-09-17 09:38 Sandy 阅读(5933) | 评论 (3)编辑 收藏
仅列出标题  下一页