被无视的伊谢尔伦

雕栏玉砌应犹在,只是朱颜改

  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  9 随笔 :: 0 文章 :: 6 评论 :: 0 Trackbacks

2008年7月21日 #

转眼已经2008年了,留下一笔,继续努力!!
posted @ 2008-07-21 20:51 爱上青菜的包子 阅读(67) | 评论 (0)编辑 收藏

2006年11月29日 #

首先需要确认的是,编译器对非虚方法使用静态联编,对虚方法使用动态联编。
看起来,在大多数情况下,动态联编都更好,因为它让程序能够选择为特定类型设计的方法,这样问题就来了,既然动态联编这么好,为什么还要设计两种类型的联编?为什么默认的联编方法是静态的而不是动态?

原因关键就在于效率。Strousstrup说过(很经典,呵呵):C++的指导原则之一是,不要为不使用的特性付出代价(内存或处理时间)。

因为通常情况下,编译器处理虚函数的方法为:给每个对象添加一个隐藏成员,该成员中保存了一个指向函数地址数组的指针(称为虚函数表 virtual function table,vtbl)。虚函数表中存储了为类对象进行声明的虚函数的地址。例如,基类包含一个指针,指向了基类中所有虚函数的地址表,派生类对象将包含一个指向独立地址表的指针,如果派生类提供了虚函数的新定义,该虚函数表将保存新函数的地址,如果没有重新定义,则保留原始版本的地址。调用虚函数时,程序将查看存储在对象中的vtbl地址,然后转向相应的函数地址表。

所以显而易见的是,使用虚函数时,在内存和执行速度方面有一定的额外成本,包括:
每个对象都将增大,增大量为存储地址的空间;对每个类,编译器都创建一个虚函数地址表(数组);每个函数调用都需要执行一步额外的操作,即到表中查找地址。

所以咱们要养成的习惯是,在设计类时,可能包含一些不在派生类重新定义的成员函数,那么这些函数就不要设置为虚函数。这样首先会有更好的效率,其次被声明为虚函数的成员函数就表明是预期在派生类中会被重新定义的,在阅读代码时也将比较方便。
posted @ 2006-11-29 17:27 爱上青菜的包子 阅读(457) | 评论 (0)编辑 收藏

2006年11月10日 #

最近全面检查了下身体,拿到体检报告的时候,发现有一项血液尿酸偏高,医生在后面批注,少食动物内脏。

疑惑了一下,正准备网上查查,结果有同事发了封公司邮件,正好是说这个事的,貌似出现该情况的人也有不少。看了看内容,如下:

出现尿酸偏高的现象,这是酸性体质的表现。是由于进食过多高蛋白、高脂肪、高糖类美食造成的。要改变偏酸性体质,关键在于饮食,要多吃碱性食物。所谓食物的酸碱性,是说食物中的无机盐属于酸性还是属于碱性。一般金属元素钠、钙、镁等,在人体内其氧化物呈碱性,含这种元素较多的食物就是碱性食物,如大豆、豆腐、菠菜、莴笋、萝卜、土豆、藕、洋葱、海带、西瓜、香蕉、梨、苹果、牛奶等。一些食物中含有较多的非金属元素,如磷、硫、氯等,在人体内氧化后,生成带有阴离子的酸根,属于酸性食物。如猪肉、牛肉、鸡肉、鸭、蛋类、鲤鱼、牡蛎、虾,以及面粉、大米、花生、大麦、啤酒等。所以不要误以为苹果等吃起来很酸就是酸性食物,那是大错特错的,刚好相反,苹果是一种碱性的食物。

酸性体质,健康新杀手

   吃得好了,身体却变 "酸"了
   "俺们那年代,缺饭又少菜;如今这年代,吃得真不赖"的确如此,现代人生活水平不断在提高,每天大鱼大肉,顿顿都离不开荤,年轻人去麦当劳、肯德基吃洋快餐更是家常便饭.可是根据一项调查研究表明:生活水平越来越好,医疗水平越来越高,现代文明病却越来越多;发病率越来越高;患者越来越年轻化。有数字显示:我国目前的高血压人群已达到1.6 个亿,我国的高血脂人群是 6500万,糖尿病人 4800万并以每年10%的速度高速增长,每年死于癌症的有130 多万人,死于心脑血管疾病的有200 多万人。70年代我们国家就呼吁要让高血压低头,可现在我们在高血压面前低了头。我国心脏病的发病年龄比80 年代初期整整提早了15年,也就是说原来50岁开始得心脏病的,现在35岁的人就开始得心脏病。多么触目惊心的数字,而这些数字只是官方统计,还没有加上未检查出来的。我们不禁要问:这到底是怎么了?越来越多的医学家、营养学家和研究机构为此现象设立课题进行研究,最后得出一致结论:酸性体质是病魔元凶。
    挖出酸性体质的根
    那么酸性体质是怎么来的呢?现代医学研究向我们揭示了真相:鱼肉禽蛋油米面等这些高蛋白、高脂肪、高糖类美食在为我们提供能量和营养的同时,也在提供大量的酸性物质。那么到底什么物质产生什么酸呢? 我们一起来看看: 蛋白质(主要来源于瘦肉、鱼、蛋等)在体内代谢产生含硫氨基酸、含磷氨基酸、尿酸等等;脂肪(主要来源于肥肉、油等)在体内代谢产生烃丁酸、乙酰乙酸(也称酮酸);碳水化合物(糖类,主要来源于米面)在体内代谢产生甘油酸、丙酮酸、乳酸。
    "酸"不除,后患无穷
    人体摄入的酸性食物过多,超过了人体酸碱平衡的调节能力,肝肾等身体重要部位超负荷运作,人体的酸碱平衡就会被破坏,使体内酸碱失衡,甚至出现轻度酸中毒反应,这就是我们所说的酸性体质。
    其实我们人体就象个养鱼的池塘,占人体体重70%的体液就是池塘里的水,水被污染了鱼就要生病死亡,同样的道理,人体体液被酸性物质污染了,组成我们人体的60万亿个细胞的生态环境就会发生变化,细胞就会发生突变和病变而影响我们的健康。我们都知道:酸性物质沉积在肝脏中会产生脂肪肝;酸性物质沉积在肾脏中会影响肾的排泄功能,使尿酸偏高而产生痛风;酸性物质沉积在关节或组织器宫内引起相应炎症,导致关节炎等疾病;酸性物质沉积在胰腺中,会影响胰岛细胞的活性、影响胰岛素的敏感性引发糖尿病及其并发症等等。更为严重的是酸性物质沉积在血液里、血管壁使血管壁增厚,引起动脉硬化,引发中风、心肌梗塞等各种心脑血管疾病。

…………

含钠、钾、钙、镁等金属元素较多的乃是碱性食品。并非味道酸的就是酸味食品,应该掌握一点:所有酸味的水果、豆制品都为碱性食物而不是酸性食物,碱性食物被认为是具有美容作用的食物。
豆腐、牛奶、芹菜、土豆、竹笋、香菇、胡萝卜、海带、绿豆、香蕉、西瓜、草莓等及大部分的蔬菜、水果都是碱性的。
食物的“酸碱性”会影响体态和皮肤健美。
 经测定,弱碱性的食物有:豆腐、豌豆、大豆、绿豆、油菜、芹菜、番薯、莲藕、洋葱、茄子、南瓜、黄瓜、蘑菇、萝卜、牛奶等。而呈碱性的食物有:菠菜、白菜、卷心菜、生菜、胡萝卜、竹笋、马铃薯、海带、柑橘类、西瓜、葡萄、香蕉、草莓、板粟、柿子、咖啡、葡萄酒等。  还有一些食物因吃起来酸,人们就错误地把它们当成了酸性食物,如山植、西红柿、醋等,其实这些东西正是典型的碱性食物

多吃碱性食物。研究发现,多食碱性食物,可保持血液呈弱碱性,使得血液中乳酸、尿素等酸性物质减少,并能防止其在管壁上沉积,因而有软化血管的作用,故有人称碱性食物为“血液和血管的清洁剂”。这里所说的酸碱性,不是食物本身的性质,而是指食物经过消化吸收后,留在体内元素的性质。常见的酸性元素有氮、碳、硫等;常见的碱性元素有钾、钠、钙、镁等。有的食物口味很酸,如番茄、橘子,却都是地地道道的强碱性食物,因为它们在体内代谢后的最终元素是钾元素等。

何谓酸性或碱性食物  所谓酸性食物或碱性食物,并不是指味道酸或咸的食物,而是指食物经过消化吸收和代谢后产生的阳离子或阴离子占优势的食物。也就是说,某种食物如经代谢后产生的钾、钠、钙、镁等阳离子占优势的则属碱性食物;而代谢后产生磷、氯、硫等阴离子占优势的食物属酸性食物。柠檬、柑桔、杨桃等味道虽酸,但它经代谢后,有机酸变成了水和二氧化碳,后者经肺呼出体外,剩下的阳离子占优势,仍属碱性食物;同理,肉、鱼、蛋类和米面虽无酸味,但代谢后产生的阴离子较多,仍属于酸性食物。因此,不能从食物的味道来区分酸性或碱性食物

看起来挺恐怖的。。。看来得注意下伙食了,以后要多吃水果、蔬菜。。。~~~

posted @ 2006-11-10 16:09 爱上青菜的包子 阅读(121) | 评论 (0)编辑 收藏

2006年10月26日 #

这几天拿到公司以前项目中的一个用C++Builder做的程序,在我机器上调试,结果提示出错:Operation not applicable

使用断点跟踪之后发现错误出在使用TQuery时执行open方法时,回追根源,得出以下反馈:

m_pqQuery -> SQL -> Add(  " Select * From Table1 Where Id = :PId  "  );
m_pqQuery
-> ParamByName(  " PId "  ) -> AsInteger      =  某个int变量;


执行完这句之后,按正常情况,参数PId应该被赋予了一个整形值,但是Debug跟踪显示其值还是跟未赋值前是同样表示的未知值。所以在执行open方法时出错了。

如果我不是用该方法,直接把SQL语句写死:

m_pqQuery -> SQL -> Add(  " Select * From Table1 Where Id = 0  "  );


运行正常

或者用

char  sql[ 80 ];
int  n  =  某整形变量 ;
sprintf(sql,
" Select * From Table1 Where Id = %d " ,n);
pqQuery
-> SQL -> Add(sql);


也运行正常

查了下,原来给参数赋值的方法并没有错误,编译也能通过,况且以前该程序肯定是能正常运行的。。真是奇怪为什么在我这里就赋不上值。。调试了很久也没能找到原因,只好作罢,为了顺利运行,只能改为sprintf和直接连接字符串的方式。
在这里记上一笔,希望以后能够找到原因。。。

posted @ 2006-10-26 21:25 爱上青菜的包子 阅读(222) | 评论 (0)编辑 收藏

2006年9月28日 #

  1. 重载<<操作符
    要重新定义<<操作符,以便将它和cout一起用来显示对象的内容,请定义下面的友元操作符函数:
    ostream & operator << (ostream & os,const c_name & obj)
    {
    os 
    << ;// display object contents
    return os;
    }

    其中c_name是类名。如果该类提供了能够返回所需内容的公有方法,则可在操作符函数中使用这些方法,这样便不会将他们设置为友元函数了。
  2. 转换函数
    要将单个值转换为类类型,需要创建原型如下所示的类构造函数:
    c_name(type_name value);

    其中c_name为类名,type_name是要转换的类型的名称。
    要将类转换为其他类型,需要创建原型如下所示的类成员函数:
    operator type_name();

    虽然该函数没有声明返回类型,但应返回所需类型的值。
    使用转换函数时要小心。可以在声明构造函数时使用关键字explicit,以防止它被用于隐式转换。
  3. 其构造函数使用new的类
    如果类使用new操作符来分配类成员指向的内存,在设计时应采取一些预防措施。
  • 对于指向的内存是由new分配的所有类成员,都应在类的析构函数中对其使用delete,该操作符将释放分配的内存。
  • 如果析构函数通过对指针类成员使用delete来释放内存,则每个构造函数都应当使用new来初始化指针,或将它设置为空指针。
  • 构造函数中要么使用new[],要么使用new,而不能混用。如果构造函数使用的是new[],则析构函数应使用delete[];如果构造函数使用的是new,则析构函数应适用delete。
  • 应定义一个分配内存(而不是将指针指向已有内存)的复制构造函数。这样程序能够将类对象初始化为另一个对象。这种构造函数原型如下:
    className(const className &)
  • 应定义一个重载赋值操作符的类成员函数,其函数定义如下(其中c_pointer是c_name的类成员,类型为指向type_name的指针)。下面的范例假设使用new[]来初始化变量c_pointer):
    c_name & c_name::operator = (const c_name & cn)
    {
    if (this == & cn)
    return *this;

    delete [] c_pointer;
    // set size number of type_name units to be copyied
    c_pointer = new type_name[size];
    // then copy data pointed to by cn.c_pointer to
    // location pointed to by c_pointer

    return *this;
    }

本文对我前面几篇随笔中提到的问题也作出了一个总结,感觉很有必要记下来。
以上内容原文引用自参考书籍中内容。

参考书籍:C++PrimerPlus author:Stephen Prata

posted @ 2006-09-28 15:18 爱上青菜的包子 阅读(541) | 评论 (1)编辑 收藏

 

const   int  BUF  =   512  ;

class  JustTesting
{
    
private :
        JustTesting(
const   string   &  s  =   " Just Testing " , int  n  =   0 )
        
{}  
        
~ JustTesting() {}
    
public :
    
}
;

int  main()
{
    
char   *  buffer  =   new   char  [BUF];

    JustTesting 
* pc1, * pc2, * pc3, * pc4;

    pc1 
=   new  (buffer) JustTesting;
    pc2 
=   new  JustTesting(  "  Heap1  "  ,  20  );

    pc3 
=   new  (buffer  +   sizeof  (JustTesting)) JustTesting( " Bad Idea " , 6 );   //  此处用一个JustTesging对象大小的偏移量避免pc3与pc1占用同一块内存,因为如果类动态的为其成员分配内存,占用同一内存将会产生问题。 
    pc4  =   new  JustTesting( " Heap2 " , 10

    

    delete pc2;
    delete pc4;
    delete [] buffer;
    
return   0 ;
}


以上代码片断中,pc1和pc3为布局new操作符来分配内存,而pc2和pc4为常规new操作符来分配内存 。
对于常规new操作符分配的内存,可以直接使用:delete pc2; 这样的语句操作来释放内存。

而对于布局new操作符分配的内存就不能这样做:delete pc1;

因为pc1和pc3并没有直接收到new操作符返回的地址,而是由布局操作符指向了buffer的地址,new/delete系统知道已分配的512字节块buffer,但对布局new操作符对该内存块做了何种处理一无所知。
另一方面,buffer的地址是用new []初始化的,因此必须使用delete[]而不是delete
注意:即使buffer是使用new而不是new[]初始化的,delete pc1 也将释放buffer,而不是pc1。

以上的代码确实释放了buffer:delete [] buffer;
但是由此产生了新的问题,它没有为布局new操作符在该内存块中创建的对象调用析构函数,我们只需要在析构函数中放入一段显示语句就可以清楚的看到,程序并没有销毁“JustTesting”和“Bad Idea”,也就是pc1和pc3指向的对象。
那么这里就需要我们显式的为布局new操作符创建的对象调用析构函数。正常情况下将自动调用析构函数,这是需要显示调用析构函数的少数几种情况之一。
显式调用析构函数时,必须指定要销毁的对象。由于有指向对象的指针,因此可以这样写:

pc3->~JustTesting();
pc1->~JustTesting();

把这段代码放到delete [] buffer;之前,这段程序才算完整无错。

参考书籍:C++PrimerPlus author:Stephen Prata

posted @ 2006-09-28 14:42 爱上青菜的包子 阅读(1972) | 评论 (3)编辑 收藏

在使用new来初始化对象的指针成员时必须特别小心,以下是几点注意事项:

  • 如果在构造函数中使用new来初始化指针成员,则应在析构函数中使用delete。
  • new和delete必须相互兼容。new对应于delete,new[]对应于delete[]。
  • 如果有多个构造函数,则必须以相同的方式使用new,要么都带中括号,要么都不带。因为只有一个析构函数,因此所有的构造函数都必须与它兼容。不过可以在一个构造函数中使用new来初始化指针,而在另外一个构造函数中将指针初始化为空(Null或0),这是因为delete(无论是带中括号还是不带中括号)可以用于空指针。
posted @ 2006-09-28 14:41 爱上青菜的包子 阅读(1494) | 评论 (1)编辑 收藏

例如有以下class:

class  StringBad
{
    
private :
        
char   *  str;
        
int  len;
         
    
public :
        StringBad(
const   char   *  s);
        StringBad();
        
~ StringBad();
        
}
;

在构造函数和析构函数定义当中有如下定义:

StringBad::StringBad( const   char   *  s)
{
    len 
=  std::strlen(s);
    str 
=   new   char  [len  +   1 ];
    
}
 

StringBad::StringBad()
{
    len 
=   4  ;
    str 
=   new   char [ 4 ];
    
}
 

StringBad::
~ StringBad()
{
    
    delete [] str;
}

那么在程序当中如果有以下代码:

StringBad sports( " Spinach Leaves Bow1 for bollars " );
StringBad sailor 
=  sports;

以上的第二条初始化语句将会调用什么构造函数?记住,这种形式的初始化等效于下面的语句:

StringBad sailor  =  StringBad(sports);

因为sports的类型为StringBad,因此相应的构造函数原型应该如下:

StringBad( const  StringBad  & );

当我们使用一个对象来初始化另一个对象时,编译器将自动生成上述构造函数(称为复制构造函数,因为它创建对象的一个副本)。
现在我们不妨总结一下所谓的隐式成员函数,即C++自动提供了以下这些成员函数:

  • 默认构造函数,如果没有定义构造函数。
  • 复制构造函数,如果没有定义。
  • 赋值操作符,如果没有定义。
  • 默认析构函数,如果没有定义。
  • 地址操作符,如果没有定义。

现在我们来看看我们没有定义复制构造函数的情况下调用隐式复制构造函数将会出现什么情况。
从构造函数定义的代码片断可以看到,当中使用new操作符初始化了一个指针str,而隐式的复制构造函数是按值进行复制的,那么对于指针str,将会进行如下复制:

sailor.str  =  sports.str;

这里复制的不是字符串,而是一个指向字符串的指针!也就是说,我们将得到两个指向同一个字符串的指针!由此会产生的问题将不言而喻。当其中一个对象调用了析构函数之后,其str指向的内存将被释放,这个时候我们如果调用另一个对象,其str指向的地址数据会是什么?很明显将会出现不可预料的结果。

所以由此可见,如果类中包含了使用new初始化的指针成员,应当定义一个复制构造函数,以复制指向的数据,而不是指针,这被称为深度复制。因为默认的浅复制(或成为成员复制)仅浅浅的赋值指针信息。

我们再看以下代码片断,我们稍做修改:

StringBad headline1( " Celery Stalks at Midnight " );
StringBad knot;
knot 
=  headline1;

这里的最后一行将与以上例子有所区别,现在是将已有对象赋给另一个已有对象,这将会采取其他操作,即使用重载的赋值操作符。(我们需要知道的是:初始化总是会调用复制构造函数,而使用=操作符时也可能调用赋值操作符)因为C++允许对象赋值,这是通过自动为类重载赋值操作符实现的。其原型如下:

Class_name  &  Class_name:: operator   =  ( const  Class_name  & );

它接受并返回一个指向类对象的引用。
与隐式的复制构造函数一样,隐式的对象赋值操作符也会产生同样的问题,即包含了使用new初始化的指针成员时,只会采用浅复制。所以我们需要使用同样的解决办法,即定义一个重载的赋值操作符来实现深度复制。

所以综上所述,如果类中包含了使用new初始化的指针成员,我们应该显式定义一个复制构造函数和一个重载的赋值操作符来实现其深度复制,避免由此带来的成员复制问题

参考书籍:C++PrimerPlus author:Stephen Prata

posted @ 2006-09-28 14:33 爱上青菜的包子 阅读(1246) | 评论 (0)编辑 收藏

很喜欢C++,学习也有一段时间了。但是看书+练手的同时,总感觉有必要将自己学习中觉得重要的地方整理出来,以便以后回头参考。最终选择了cppblog,呵呵。以后就在这里安家了~~
笔记中参考了一些书籍中的范例和一些说明。都是自己学习过程中整理出来的,主要目的是给自己日后参考。
posted @ 2006-09-28 14:23 爱上青菜的包子 阅读(208) | 评论 (1)编辑 收藏

仅列出标题