weitom1982

向各位技术前辈学习,学习再学习.
posts - 299, comments - 79, trackbacks - 0, articles - 0
  IT博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

才鸟初学,什么都不懂,hoho
》include <sys/types.h>的“/”什么用法啊。
》include <name>不用.h后缀时的用法是什么,哪儿有。
》namespace std 出现的位置和使用的规则。
》如何提高专业英文水平,有什么好书可以看。:P
》可执行文件的生成有哪几种方式。
暂时就想起这几个。

posted @ 2006-03-21 12:04 高山流水 阅读(194) | 评论 (1)编辑 收藏

 

内存分配方式有三种:
(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的
整个运行期间都存在。例如全局变量,static 变量。
(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函
数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集
中,效率很高,但是分配的内存容量有限。
(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多
少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期
由我们决定,使用非常灵活,但问题也最多。

?? 内存分配未成功,却使用了它。
编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,
在使用内存之前检查指针是否为NULL。如果指针p 是函数的参数,那么在函数的入口
处用assert(p!=NULL)进行检查。如果是用malloc 或new 来申请内存,应该用if(p==NULL)
或if(p!=NULL)进行防错处理。

?? 内存分配虽然成功,但是尚未初始化就引用它。

?? 内存分配成功并且已经初始化,但操作越过了内存的边界。

?? 忘记了释放内存,造成内存泄露。
含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你
看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽。
动态内存的申请与释放必须配对,程序中malloc 与free 的使用次数一定要相同,否
则肯定有错误(new/delete 同理)。

?? 释放了内存却继续使用它。
有三种情况:
(1)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内
存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。
(2)函数的return 语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,
因为该内存在函数体结束时被自动销毁。
(3)使用free 或delete 释放了内存后,没有将指针设置为NULL。导致产生“野指针”

 


指针参数是如何传递内存的?
如果函数的参数是一个指针,不要指望用该指针去申请动态内存


Test 函数的语句GetMemory(str, 200)并没有使str 获得期望的内存,str 依旧是NULL,
为什么?

void GetMemory(char *p, int num)
{
p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
char *str = NULL;
GetMemory(str, 100); // str 仍然为 NULL
strcpy(str, "hello"); // 运行错误
}

毛病出在函数GetMemory 中。编译器总是要为函数的每个参数制作临时副本,指针
参数p 的副本是 _p,编译器使 _p = p。如果函数体内的程序修改了_p 的内容,就导致
参数p 的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p 申请
了新的内存,只是把_p 所指的内存地址改变了,但是p 丝毫未变。所以函数GetMemory
并不能输出任何东西。事实上,每执行一次GetMemory 就会泄露一块内存,因为没有用
free 释放内存。
如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”
void GetMemory2(char **p, int num)
{
*p = (char *)malloc(sizeof(char) * num);
}
void Test2(void)
{
char *str = NULL;
GetMemory2(&str, 100); // 注意参数是 &str,而不是str
strcpy(str, "hello");
cout<< str << endl;
free(str);
}

由于“指向指针的指针”这个概念不容易理解,我们可以用函数返回值来传递动态
内存。这种方法更加简单
char *GetMemory3(int num)
{
char *p = (char *)malloc(sizeof(char) * num);
return p;
}
void Test3(void)
{
char *str = NULL;
str = GetMemory3(100);
strcpy(str, "hello");
cout<< str << endl;
free(str);
}
用函数返回值来传递动态内存这种方法虽然好用,但是常常有人把return 语句用错
了。这里强调不要用return 语句返回指向“栈内存”的指针,因为该内存在函数结束时
自动消亡
char *GetString(void)
{
char p[] = "hello world";
return p; // 编译器将提出警告
}
void Test4(void)
{
char *str = NULL;
str = GetString(); // str 的内容是垃圾
cout<< str << endl;
}
用调试器逐步跟踪Test4,发现执行str = GetString 语句后str 不再是NULL 指针,
但是str 的内容不是“hello world”而是垃圾。
char *GetString2(void)
{
char *p = "hello world";
return p;
}
void Test5(void)
{
char *str = NULL;
str = GetString2();
cout<< str << endl;
}
函数Test5 运行虽然不会出错,但是函数GetString2 的设计概念却是错误的。因为
GetString2 内的“hello world”是常量字符串,位于静态存储区,它在程序生命期内
恒定不变。无论什么时候调用GetString2,它返回的始终是同一个“只读”的内存块。

posted @ 2006-03-21 11:59 高山流水 阅读(654) | 评论 (0)编辑 收藏

 我自己觉得应该还要加一本书:
《 Inside the C++ Object Model》我自己现在也在看这本书,对C++对象模型介绍的非常好,是深入了解C++的资料书籍。C++学习应该从两个方面来学习,首先学习C++语言层面,也就是C++语法,如果深入学习的话就会进入C++底层。。。第二个层面就是对象,设计很广泛:对象设计,对象复用,STL,COM
BOOST。。。

1. 《The C++ Programming Language》 (Special 3rd Edition) by Bjarne Stroustrup
C++之父著作的大头作,非常不错,很厚,对C++介绍的非常详细。

2. 《The Design and Evolution of C++》 by Bjarne Stroustrup
如果你只打算购买一本书,那就选择1,如果还想要第二本 — 那就是这两本了。它们的作者是 C++语言的创建者。Stroustrup的文字语言丝毫不逊色于他所创建的程序语言。它们可以使你免于误入歧途。

3. 《Effective C++》: 50 Specific Ways to Improve Your Programs and Designs
 by Scott Meyers

4. 《More Effective C++》: 35 New Ways to Improve Your Programs and Designs
 by Scott Meyers

5. 《Effective STL》: 50 Specific Ways to Improve Your Use of the Standard Template Library by Scott Meyers
我强烈推荐Meyers这个系列。它们是菜鸟进阶必读之作。游刃有余的技术,高超的写作技巧。Meyers可能是世界上最优秀的C++技术作家。

6. 《The C++ Standard Library》 : A Tutorial and Reference by Nicolai M. Josuttis
C++标准库字典式著作。内容全面,结构清晰。

7. 《C++ Templates》: The Complete Guide by David Vandevoorde,
Nicolai M. Josuttis Alexandrescu的Modern C++ Design阅读门槛很高,这本书可以充当你的垫脚石。单单阅读The C++ Standard Library或许并不足以使你具备定制、扩充标准库组件的能力,这本书可以助你一臂之力。对于任何希望进入模板编程领域的C++程序员来说,这是一本必读之作。

8. 《Modern C++ Design》: Generic Programming and Design Patterns Applied
 by Andrei Alexandrescu
一本天才的著作!泛型模式,无限延伸你的视野,足以挑战任何一名C++程序员的思维极限。这本书几乎可以满足你对C++模板的所有幻想。

9. 《Design Patterns》
 by Erich Gamma , Richard Helm, Ralph Johnson, John Vlissides
设计可复用的面向对象的软件,你需要掌握设计模式。这并不是一本专门针对C++程序员的著作,但它采用了C++(和Smalltalk)作为主要示例语言,C++程序员尤其易于从中受益。学习设计模式,这本书需要一而再、再而三的咀嚼。



1. <<The C++ Programming Language>> , Bjarne Stroustrup
这是一本每个人都应该买但不一定要看的书,强烈建议初学者不要看这本书,否则可能会一头雾水,失去信心,BS写书本来就不怎么样,他自己都说他的书有些晦涩难懂,但是买一本放在书柜里,有什么不清楚的地方查查高人如何解析还是有必要的。中文版是裘老师翻译的,以裘老师的深厚功力,技术上大家大可放心,但语言流畅上我就不敢认同了,况且这还是一本大书,如果要追求语言流畅的话,可能没有两3年是出不来的,而且国内的出版社又那么急功近利,稿酬不高,做得那么好有必要吗?

2. <<C++ Primer>> , lippman
lippman的地位自然不用猜疑,此书也被评为C++的最佳入门书籍,也是Lipman写得最认真的一本书,第3版在国内98年就有了。全书可谓是C++大全,深度适当,比较适合初学者,或者想对C++和程序设计有大概了解的读者, 但就是比较厚,这也只能怪C++的源远流长,总是在打园场,不断增加新特性,让我们这群读者都不得不捧着这本大书品读。中文版是潘老师翻译的,文字通顺,但缺乏味道,看着一本千来页的书,而且前面一部分讲的都是程序设计思想,容易令人产生困倦,也难怪很多人半道出家学java。

3. <<effective C++ >> Scott meyer 同下

4. <<more effective C++>> , meyer
这年本书乃是改善设计思维和设计思想的必读书籍,书中每个item莫不是经过千锤百炼之后得出。其中,effective比较浅显易懂,每个item都比较短,而more设计思想更进一层,有些item已经多多少少有设计模式的味道。我看过的是繁体版,这两本书候老师翻译得不错(我觉得是候老师翻译得最好的两本书,比较认真),读起来比较有味道。建议初学者在看完一本C++比较完整的书后直接拜读此2书。

5. <<The C++ standard library>>
就是现在卖得比较热的书,梦魇和候节翻译的,翻译得很好,语言很通顺,技术上也很好,只是书中有些代码我是调试不过,或许只能怪STL里面有几个经典错误,不知看这本书的读者发现了没有?我严重建议C++的初学者看这本书,先知道应该怎么来用C++,怎么来充分利用标准库,而不是一头钻入C++的庞大晦涩语法里面,先用标准库才能养成良好的代码编写风格,更可以提高自己的实作能力,否则你如果花上一年半载在C++的语言细节上,可能自己都生气为什么还是写不出好的程序出来,公欲善其事,必先利其器就是这个道理。


6. <<modern C++ design >> Andrei Alexandrescu
这是天才Andrei Alexandrescu的作品,很好的一本高阶书,此书我只是浏览一下大概,没有仔细看,有些地方看不懂,全书把C++和设计模式融为一体,向人展示了许多不平常的设计技巧与设计规范,令人折服。但有些难度,还望各位慎重。至于里面的技术实不实用,就要因人而异了。


7. <<C++ Template>> 据说书中展示了模板的精华之处


8. <<exceptional C++ 和more exceptional C++>>
Herb Sutter的书读起来比较轻松,此2书可以作为effective与more effective的姐妹篇,写作风格有些如苏格拉底的提问式教学,但应用得还不够火候。书的内容确实不错,定位和effective差不多,但我个人而言还是更喜欢Meyer的书。More有中文版,有Lostmouse翻译,质量一般,翻译给人的感觉比较仓促,很多地方都过于直译,味道怪怪的。


9. <<essential C++>> lippman 很简单的一本书,初学者如果觉得自己确实很菜可以先看这本书,你肯定能看懂,也是lippman的书,但英文版有些地方值得商榷,还是看候节的中文版吧,翻译得还可以,就是台湾味道太浓了些,有点喧宾夺主之嫌。如了门的读者就没必要看这本书了,看它还不如看lippman的inside the C++ object model呢。


10 . <<designing and coding reusable C++>> 关于重用和程序库设计的一本书,是由AT&T的Ellis夫妇写的,书写得很好,充分考虑了设计可重用程序库的方方面面,对于每个想设计程序库和的读者都应该看这本书,中文版名是《设计和重用C++程序库》,即将出版。


11. <<thinking in C++>> 我是觉得此书一般,并没有外面吹的那么好,可以说是中等偏上吧,要看就要看英文版的,中文版的千万别看,那个译者太不负责了

12. <<Runmination on C++>> 据说人邮既要出版了,是由konnig夫妇写的,如果你想知道cfront的历史,还想清楚cfront的领导者,就找他们夫妇吧,那时,konig和BS是独立的,而其它的人都是由konig夫人Barbara领导的,konig在C++中可算是number 2,属于学术派的那种,实际上C++也是分派别的。 英文版的质量肯定很好,有很多新的设计观念,中文版还要期待,看看结果如何。

13. <<16Accelerated C++>> 也是koenig和Barbara夫妇写的,据说很好,但我没机会看,实在不敢评价。

14 <<C语言接口与实现:创建可重用软件的技术>>

posted @ 2006-03-21 11:54 高山流水 阅读(133) | 评论 (0)编辑 收藏

 

C++常类型(const)

常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。因此,定义或说明常类型时必须进行初始化。

  一般常量和对象常量

  1. 一般常量

  一般常量是指简单类型的常量。这种常量在定义时,修饰符const可以用在类型说明符前,也可以用在类型说明符后。如:

  int const x=2;

  或

  const int x=2;

  定义或说明一个常数组可采用如下格式:

  <类型说明符> const <数组名>[<大小>]…

  或者

  const <类型说明符> <数组名>[<大小>]…

  例如:

  int const a[5]={1, 2, 3, 4, 5};

  2. 常对象

  常对象是指对象常量,定义格式如下:

  <类名> const <对象名>

  或者

  const <类名> <对象名>

  定义常对象时,同样要进行初始化,并且该对象不能再被更新,修饰符const可以放在类名后面,也可以放在类名前面。

  常指针和常引用

  1. 常指针

  使用const修饰指针时,由于const的位置不同,而含意不同。下面举两个例子,说明它们的区别。

  下面定义的一个指向字符串的常量指针:

  char * const prt1 = stringprt1;

  其中,ptr1是一个常量指针。因此,下面赋值是非法的。

  ptr1 = stringprt2;

  而下面的赋值是合法的:

  *ptr1 = "m";

  因为指针ptr1所指向的变量是可以更新的,不可更新的是常量指针ptr1所指的方向(别的字符串)。

  下面定义了一个指向字符串常量的指针:

  const * ptr2 = stringprt1;

  其中,ptr2是一个指向字符串常量的指针。ptr2所指向的字符串不能更新的,而ptr2是可以更新的。因此,

  *ptr2 = "x";

  是非法的,而:

  ptr2 = stringptr2;

  是合法的。

  所以,在使用const修饰指针时,应该注意const的位置。定义一个指向字符串的指针常量和定义一个指向字符串常量的指针时,const修饰符的位置不同,前者const放在*和指针名之间,后者const放在类型说明符前。

  2. 常引用

  使用const修饰符也可以说明引用,被说明的引用为常引用,该引用所引用的对象不能被更新。其定义格式如下:

  const <类型说明符> & <引用名>

  例如:

  const double & v;

  在实际应用中,常指针和常引用往往用来作函数的形参,这样的参数称为常参数。

  在C++面向对象的程序设计中,指针和引用使用得较多,其中使用const修饰的常指针和常引用用得更多。使用常参数则表明该函数不会更新某个参数所指向或所引用的对象,这样,在参数传递过程中就不需要执行拷贝初始化构造函数,这将会改善程序的运行效率。

  下面举一例子说明常指针作函数参数的作法。

#include
const int N = 6;
void print(const int *p, int n);

void main()
{
int array[N];
for (int i=0; i cin>>array[i];
print(array, N);
}

void print(const int *p, int n)
{
cout<<"{"<<*p;
for (int i=1; i cout<<","<<*(p+i);
cout<<"}"< }


  常成员函数

  使用const关键字进行说明的成员函数,称为常成员函数。只有常成员函数才有资格操作常量或常对象,没有使用const关键字说明的成员函数不能用来操作常对象。常成员函数说明格式如下:

  <类型说明符> <函数名> (<参数表>) const;

其中,const是加在函数说明后面的类型修饰符,它是函数类型的一个组成部分,因此,在函数实现部分也要带const关键字。下面举一例子说明常成员函数的特征。

#include
class R
{
public:
R(int r1, int r2) { R1=r1; R2=r2; }
void print();
void print() const;
private:
int R1, R2;
};

void R::print()
{
cout< }

void R::print() const
{
cout< }

void main()
{
R a(5, 4);
a.print();
const R b(20, 52);
b.print();
}


  该例子的输出结果为:

  5,4
  20;52

  该程序的类声明了两个成员函数,其类型是不同的(其实就是重载成员函数)。有带const修饰符的成员函数处理const常量,这也体现出函数重载的特点。

  常数据成员

  类型修饰符const不仅可以说明成员函数,也可以说明数据成员。

  由于const类型对象必须被初始化,并且不能更新,因此,在类中说明了const数据成员时,只能通过成员初始化列表的方式来生成构造函数对数据成员初始化。

  下面通过一个例子讲述使用成员初始化列表来生成构造函数。

#include
class A
{
public:
A(int i);
void print();
const int &r;
private:
const int a;
static const int b;
};

const int A::b=10;
A::A(int i):a(i), r(a)
{
}

void A::print()
{
cout< }

void main()
{
A a1(100), a2(0);
a1.print();
a2.print();
}


  该程序的运行结果为:

  100:10:100
   0:10:0

  在该程序中,说明了如下三个常类型数据成员:

  const int & r;

  const int a;

  static const int b;

  其中,r是常int型引用,a是常int型变量,b是静态常int型变量。

  程序中对静态数据成员b进行初始化。

  值得注意的是构造函数的格式如下所示:

  A(int i):a(i),r(a)
  {
  }

  其中,冒号后边是一个数据成员初始化列表,它包含两个初始化项,用逗号进行了分隔,因为数据成员a和r都是常类型的,需要采用初始化格式

posted @ 2006-03-21 11:49 高山流水 阅读(112) | 评论 (0)编辑 收藏

虚函数地址的找法:

#include "stdafx.h"
#include 
<iostream>
using namespace std;
class CBase
{
public:
    
virtual void who(){cout<<"CBase"<<endl;}
}
;
class CDerived:public CBase
{
public:
    
virtual void who(){cout<<"CDerived"<<endl;}
}
;
int _tmain(int argc, _TCHAR* argv[])
{
    
long num1=0;
    
long num2=0;
    CBase 
*base=new CBase;//必须用new后,才可以找到
    CDerived *derived=new CDerived;
    num1
=*(long*)(*(long*)base);//找到第一个虚函数的地址
    num2=*(long*)(*(long*)derived);
    cout
<<num1<<endl;
    cout
<<num2<<endl;
    
return 0;
}


#include "stdafx.h"
#include <iostream>
using namespace std;
class CBase
{
public:
 virtual void who(){cout<<"CBase"<<endl;}
};
class CDerived:public CBase
{
public:
 virtual void who(){cout<<"CDerived"<<endl;}
};
int _tmain(int argc, _TCHAR* argv[])
{
 long num1=0;
 long num2=0;
 CBase *base=new CBase;//必须用new后,才可以找到
 CDerived *derived=new CDerived;
 num1=*(long*)(*(long*)base);//找到第一个虚函数的地址
 num2=*(long*)(*(long*)derived);
 cout<<num1<<endl;
 cout<<num2<<endl;
 return 0;
}

---------------------------------------
虚函数的定义要遵循以下重要规则:

  1.如果虚函数在基类与派生类中出现,仅仅是名字相同,而形式参数不同,或者是返回类型不同,那么即使加上了virtual关键字,也是不会进行滞后

联编的。

  2.只有类的成员函数才能说明为虚函数,因为虚函数仅适合用与有继承关系的类对象,所以普通函数不能说明为虚函数。

  3.静态成员函数不能是虚函数,因为静态成员函数的特点是不受限制于某个对象。

  4.内联(inline)函数不能是虚函数,因为内联函数不能在运行中动态确定位置。即使虚函数在类的内部定义定义,但是在编译的时候系统仍然将它看做是非内联的。

  5.构造函数不能是虚函数,因为构造的时候,对象还是一片位定型的空间,只有构造完成后,对象才是具体类的实例。

  6.析构函数可以是虚函数,而且通常声名为虚函数。
------------------------------------

1)Derived:public Base

  
        CBase  pBase;
 CDerived pDerived;
 CBase *pBase2= new CDerived;
        delete pBase2;
 CDerived pDerived2;

                //results:             
         //CBase::CBase               // CBase  pBase;
  //CBase::CBase               //CDerived pDerived;
  //CDerived:: CDerived
  //CBase::CBase               //CBase *pBase2= new CDerived;
  //CDerived:: CDerived
  //CBase::~CBase              // delete pBase2;//因为基类的析构不是虚,如                                                                是则先调用子类的析构
  //CBase::CBase               //CDerived pDerived2;
  //CDerived:: CDerived       
  //CDerived::~CDerived         //CDerived pDerived2;   
  //CBase::~CBase
  //CDerived::~CDerived         //CDerived pDerived;  
  //CBase::~CBase
  //CBase::~CBase               //CBase  pBase;    
  //Press any key to continue


     如果是子类构造先基类,再子类
     如果是子类析构先子类,再基类
     但如果基类析构不是虚,则CBase *pBase2= new CDerived;
             delete pBase2;只调用基类.否则如果基类析构是虚,则先调用子类,再调用基类。
     如果是用new初始化的,必须用delete来释放,否则不调用析构函数。

     CDerived pDerived(10);//the base class must be have the defaut constructor


//CBase::CBase
//this is defuat  constructor function
//CDerived:: CDerived
//CDerived::~CDerived
//CBase::~CBase
//CBase::~CBase
//Press any key to continue


如果子类中有基类的对象成员,则先调用基类的构造函数,再调用基类的构造函数对基类的对象成员初始化,最后才调用子类的构造函数,析构正好顺序相反。

 


2)CBase1:public  CBase
  CBase2:public  CBase
  CDervied:public  CBase1,CBase2

CDerived  pdereved;

如果没有定义为虚继承 ,先调用基类再子类然后再基类再子类最后才调用子子类构造。
CBase::CBase()!
CBase1::CBase()!
CBase::CBase()!
CBase2::CBase()!
CDerived::CDerived()!
CDerived::~CDerived()!
CBase2::~CBase()!
CBase::~CBase()!
CBase1::~CBase()!
CBase::~CBase()!
Press any key to continue

如果cbase1与cbase2是虚继承cbase,则只调用一次基类
CBase::CBase()!
CBase1::CBase()!
CBase2::CBase()!
CDerived::CDerived()!
CDerived::~CDerived()!
CBase2::~CBase()!
CBase1::~CBase()!
CBase::~CBase()!
Press any key to continue

-----------------------------------
多重继承的成员调用:


  cbase
  CBase1:public  CBase
  CBase2:public  CBase
  CDervied:public  CBase1,CBase2
1)当cbase1与cbase2不是虚继承的时:dervied不能访问基类继承而来(即共有的)的任何成员,不能识别。

2)当cbase1与cbase2是虚继承的时,dervied可以访问cbase中没有1和2重新定义的成员,也能访问只被1或只被2重新定义的成员,这是调用1或2中的成员,当dervied中有重写时,调用derived中重新定义的成员。(共有)

:保证我们在不考虑继承而来的隐藏成员时,能够识别该调用那个类中的!则编译器也能识别!
3)
在调用变量的时候,要指出时属于那个类。

posted @ 2006-03-21 11:35 高山流水 阅读(533) | 评论 (0)编辑 收藏

 

成员函数被重载的特征:  //////////////////基类中
(1)相同的范围(在同一个类中); 
(2)函数名字相同; 
(3)参数不同; 
(4)virtual关键字可有可无。 


覆盖是指派生类函数覆盖基类函数,特征是: /////////////基类与子类的关系
(1)不同的范围(分别位于派生类与基类); 
(2)函数名字相同; 
(3)参数相同; 
(4)基类函数必须有virtual关键字。 


“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:  ////////子类中
(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。 
(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

 

#include
class B {
public:
 void mf(){cout<<"1          ";};
 
};

class D: public B { 
public:
 void mf(){cout<<"2          ";};
};


main(){
 D x;                         
 
 B *pB = &x;                 
 
 pB->mf();                   
 
 
 D *pD = &x;                 
 
 pD->mf(); 

        //B   bb;
       // bb.mf();                  
}

两种情况下调用的都是对象x的成员函数mf,因为两种情况下都是相同的函数和相同的对象,所以行为会相同,对吗?

对,会相同。但,也许不会相同。特别是,如果mf是非虚函数而D又定义了自己的mf版本,行为就不会相同.

 


1)当类B中有虚函数mf()时,类D中没有重写此函数时,结果为:1        1。      一般的继承
2)当类B中有虚函数mf()时,类D中重写此函数时,结果为:2        2。          派生类函数覆盖基类函数
3)当类B中有非虚函数mf()时,类D中没有重写此函数时,结果为:1        1。    一般的继承
4)当类B中有非虚函数mf()时,类D中重写此函数时,结果为:1        2。       类D中的函数隐藏了从基类B中继承下来的函数
5)当类B中有虚或非虚函数mf()时,类D中有与此函数同名但时参数不同的函数时,从基类继承而来的函数也被隐藏!


------------------------------------------------------------------------------------------

多重继承的成员调用:


  cbase
  CBase1:public  CBase
  CBase2:public  CBase
  CDervied:public  CBase1,CBase2
1)当cbase1与cbase2不是虚继承的时:dervied不能访问基类继承而来(即共有的)的任何成员,不能识别。

2)当cbase1与cbase2是虚继承的时,dervied可以访问cbase中没有1和2重新定义的成员,也能访问只被1或只被2重新定义的成员,这是调用1或2中的成员,当dervied中有重写时,调用derived中重新定义的成员。(共有)

:保证我们在不考虑继承而来的隐藏成员时,能够识别该调用那个类中的!则编译器也能识别!
3)
在调用变量的时候,要指出时属于那个类。

posted @ 2006-03-21 11:32 高山流水 阅读(675) | 评论 (0)编辑 收藏

今天在论坛里看到这样一个问题:
要求将字符串 "mallid=310103123456\ntermno=139654785231\norder=00003995\ngcode=0000130\npaydate=20051031\nname=\ncardno=5248000262355365\nlimit=0804\nprice=2210"\n为分界符,提取出所有的字符串。
刚好最近在看STL,感觉可以用stream来解决,于是就小试了一下。结果还真令人满意


#include 
< iostream >
#include 
< sstream >
#include 
< vector >
#include 
< algorithm >
#include 
< ITERATOR >

#define  INPUT_STRING "mallid=310103123456\ntermno=139654785231\norder=00003995\ngcode=0000130\npaydate=20051031\nname=\ncardno=5248000262355365\nlimit=0804\nprice=2210"
    
typedef std::vector
< std:: string >  strvec;

int  _tmain( int  argc, _TCHAR *  argv[])
{
    
using   namespace  std;

    istringstream iss(INPUT_STRING);
    strvec strList;
    
string  strTemp;

    
while (iss  >>  strTemp)
    
{
        strList.push_back(strTemp);
    }

        
        
//  out put the result
    copy(strList.begin(), strList.end(), ostream_iterator < string > (cout,  " \n " ));

    
return   0 ;
}



posted on 2005-11-03 10:14 力为 阅读(242) 评论(6)  编辑 收藏收藏至365Key 所属分类: C++ FAQ

FeedBack:
# re: 用istringstream 解决问题一则
2005-11-29 11:21 | Squirrel
stringstream,我现在常用它来做字符串数组的存取。喜欢将它用作函数的输出参数,来得到字符串列表,不知道这样做是否合适,似乎用vector来做更好些。  回复
  
# re: 用istringstream 解决问题一则
2005-12-01 22:42 | 力为
我感觉用vector好一些  回复
  
# re: 用istringstream 解决问题一则
2006-03-02 09:35 | 周星星
#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <iterator>

#define INPUT_STRING "mallid=310103123456\ntermno=139654785231\norder=00003995\ngcode=0000130\npaydate=20051031\nname=\ncardno=5248000262355365\nlimit=0804\nprice=2210"

typedef std::vector<std::string> strvec;

int main(int argc, char* argv[])
{
using namespace std;

istringstream iss( INPUT_STRING );
strvec strList;

// in
copy( istream_iterator<string>(iss), istream_iterator<string>(), back_inserter(strList) );

// out
copy( strList.begin(), strList.end(), ostream_iterator<string>(cout,"\n") );

return 0;
}

  回复
  
# 要来再少点儿? :P
2006-03-02 19:16 | LeavesOfFloat
#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>

int main(int argc, char* argv[])
{
using namespace std;

const string str("mallid=310103123456\ntermno=139654785231\norder=00003995\ngcode=0000130\npaydate=20051031\nname=\ncardno=5248000262355365\nlimit=0804\nprice=2210")
vector<string> strList;

copy( istream_iterator<string>(istringstream(str)), istream_iterator<string>(), back_inserter(strList) );

copy( strList.begin(), strList.end(), ostream_iterator<string>(cout,"\n") );

return 0;
}
  回复
  
# re: 用istringstream 解决问题一则
2006-03-04 10:00 | 力为
看来完美了:)  回复
  
# re: 用istringstream 解决问题一则
2006-03-09 11:06 | Squirrel
有这样一个关于stringstream的问题。
将一系列以string 和unsigned long 组成的序列放入流中,如何将它们还原出来?(string中允许有空格)

#include "stringstream"
#include "iostream"

using namespace std;

void main()
{
stringstream ss;

string item1;
unsigned long item2;

ss << "Hello World" <<endl << 80 <<endl
<< "Hi Jack" << endl << 90 <<endl
<< "Hello China" << endl << 100 <<endl;

while ( ss >> item1 >> item2 )
{
cout << item1 << "\t" << item2 <<endl;
}

}

上面的程序打印不出来任何东西,因为空格是作为流的分隔符的.
不知道大家有什么好的办法? 

posted @ 2006-03-21 11:16 高山流水 阅读(153) | 评论 (0)编辑 收藏

 关于标准IO流的两个常见误区

1)关于打开方式标志  ios::binary
   标准IO流主要是为了进行文本IO而设计的,所以这里的 ios::binary 主要是进行控制字符的转换,例如对于回车符的处理,而不是区分文本和二进制数据,使用 ios::binary标志的作用是禁止控制字符的自动转换,它处理的同样是文本IO。

2)关于对宽字符文件的读写及 wfstream
  wfstream是一个名不符实的命名,它并不用于对于宽字符(wide character)文件的读写,它处理的是多字节(mutilbytes character)文件。实际上,标准库并没有直接提供对于宽字符文件的读写。如果你正在进行 wchar_t 类型的编程,那么进行文件IO时一定要小心,使用wfstream也许结果和你想象中的不太一样

posted @ 2006-03-21 10:47 高山流水 阅读(103) | 评论 (0)编辑 收藏

内存分配方式有三种:
(1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的
整个运行期间都存在。例如全局变量,static 变量。
(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函
数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集
中,效率很高,但是分配的内存容量有限。
(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多
少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期
由我们决定,使用非常灵活,但问题也最多。

?? 内存分配未成功,却使用了它。
编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,
在使用内存之前检查指针是否为NULL。如果指针p 是函数的参数,那么在函数的入口
处用assert(p!=NULL)进行检查。如果是用malloc 或new 来申请内存,应该用if(p==NULL)
或if(p!=NULL)进行防错处理。

?? 内存分配虽然成功,但是尚未初始化就引用它。

?? 内存分配成功并且已经初始化,但操作越过了内存的边界。

?? 忘记了释放内存,造成内存泄露。
含有这种错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你
看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽。
动态内存的申请与释放必须配对,程序中malloc 与free 的使用次数一定要相同,否
则肯定有错误(new/delete 同理)。

?? 释放了内存却继续使用它。
有三种情况:
(1)程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内
存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。
(2)函数的return 语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,
因为该内存在函数体结束时被自动销毁。
(3)使用free 或delete 释放了内存后,没有将指针设置为NULL。导致产生“野指针”

 


指针参数是如何传递内存的?
如果函数的参数是一个指针,不要指望用该指针去申请动态内存


Test 函数的语句GetMemory(str, 200)并没有使str 获得期望的内存,str 依旧是NULL,
为什么?

void GetMemory(char *p, int num)
{
p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
char *str = NULL;
GetMemory(str, 100); // str 仍然为 NULL
strcpy(str, "hello"); // 运行错误
}

毛病出在函数GetMemory 中。编译器总是要为函数的每个参数制作临时副本,指针
参数p 的副本是 _p,编译器使 _p = p。如果函数体内的程序修改了_p 的内容,就导致
参数p 的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p 申请
了新的内存,只是把_p 所指的内存地址改变了,但是p 丝毫未变。所以函数GetMemory
并不能输出任何东西。事实上,每执行一次GetMemory 就会泄露一块内存,因为没有用
free 释放内存。
如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”
void GetMemory2(char **p, int num)
{
*p = (char *)malloc(sizeof(char) * num);
}
void Test2(void)
{
char *str = NULL;
GetMemory2(&str, 100); // 注意参数是 &str,而不是str
strcpy(str, "hello");
cout<< str << endl;
free(str);
}

由于“指向指针的指针”这个概念不容易理解,我们可以用函数返回值来传递动态
内存。这种方法更加简单
char *GetMemory3(int num)
{
char *p = (char *)malloc(sizeof(char) * num);
return p;
}
void Test3(void)
{
char *str = NULL;
str = GetMemory3(100);
strcpy(str, "hello");
cout<< str << endl;
free(str);
}
用函数返回值来传递动态内存这种方法虽然好用,但是常常有人把return 语句用错
了。这里强调不要用return 语句返回指向“栈内存”的指针,因为该内存在函数结束时
自动消亡
char *GetString(void)
{
char p[] = "hello world";
return p; // 编译器将提出警告
}
void Test4(void)
{
char *str = NULL;
str = GetString(); // str 的内容是垃圾
cout<< str << endl;
}
用调试器逐步跟踪Test4,发现执行str = GetString 语句后str 不再是NULL 指针,
但是str 的内容不是“hello world”而是垃圾。
char *GetString2(void)
{
char *p = "hello world";
return p;
}
void Test5(void)
{
char *str = NULL;
str = GetString2();
cout<< str << endl;
}
函数Test5 运行虽然不会出错,但是函数GetString2 的设计概念却是错误的。因为
GetString2 内的“hello world”是常量字符串,位于静态存储区,它在程序生命期内
恒定不变。无论什么时候调用GetString2,它返回的始终是同一个“只读”的内存块。

posted @ 2006-03-21 10:04 高山流水 阅读(438) | 评论 (1)编辑 收藏

     摘要: 时间和日历类的设计 C++ 通用框架的设计 作者: naven 1           介绍 ...  阅读全文

posted @ 2006-03-21 10:02 高山流水 阅读(1174) | 评论 (2)编辑 收藏

仅列出标题
共30页: First 18 19 20 21 22 23 24 25 26 Last