weitom1982

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

组件( Component )应该是在现代软件工程中,除了对象( Object )之外的第二个炙手可热的词了。然而,什么是一个组件?是一个某些逻辑集合的实现?还是一个承载这些实现的物理实体(例如 DLL )?还是一个包括了逻辑实现、物理载体以及相关的类型信息、安全策略和版本信息的一个大而全的集合?也许你会说任何一种解释都是正确的。的确,不同的环境中, Component 有着不同的含义,但是,在 .NET 中,我们就可以把一个 .NET Class 当作一个 Component


既然类和组件有着这么多类似的地方,那么传统的面向对象编程和面向组件编程有什么区别呢?简单的说,面向对象关注的是组合在一个二进制可执行文件中的各个类的关系,而面向组件的编程关注的是在彼此独立的基础上模块之间的交互性,这种交互性使得你并不需要熟悉它们内部的工作原理。

分而治之 VS 集大成

这两种方法最基本的不同在于它们对最终的应用程序的观点。在传统的面向对象编程中,尽管你可以精心的把所有的商业逻辑分布在不同的类中,一旦这些类被编译,它们就被固化成了一个巨大的二进制代码。所有的类共享同一个物理单元(通常是一个可执行文件)、被操作系统认为是同一个进程,使用同一个地址空间以及共享相同的安全策略等等。如果多个开发者在同一份代码上进行开发,他们甚至还要共享源文件。在这种情况下,修改一个类可能会让整个项目被重新链接,并重新进行必要的测试,更严重的,还有可能要修改其他的类。但是,在面向组件开发中,应用程序是由一系列可以互相交互的二进制模块组合而成的。

一个具体的二进制组件可能并不能完成什么工作。有些组件是为了提供一些常规服务而编写的,例如通信的封装或者文件访问组件。也有一些是为了某些特定应用而专门开发的。一个应用程序的设计者可以通过把这些不同的组件提供的功能粘合在一起来实现他们需要的商业逻辑。很多面向组件的技术——例如: COM J2EE CORBA .NET 都为二进制组件提供了的无缝链接的机制。而唯一的不同就是你需要在组件通信上花费的力气。

把一个二进制应用程序分解成不同的二进制组件的动机和把不同的类放到不同的文件中是类似的。后者使得不同的类的开发人员可以彼此独立的工作,尽管即时修改了一个类也要重新链接整个应用程序,但是你只需要重新编译被修改的部分就可以了。

但是,面向组件的开发还是和简单软件项目的管理更复杂一些。因为一个面向组件的应用程序是一个二进制代码块的集合,你可以把组件当作是 LEGO 的积木块一样,随心所欲的拆装它们。如果你需要修改一个组件的实现,只需要修改那个组件就可以了,而组件的客户机不需要重新编译也不需要重新开发。对于那些不常用到的组件,组件甚至可以在一个程序运行的时候被更新。这些改进和增强使得组件可以立即进行更新,而所有该组件的客户都将立即受益。无论是在同一台机器上还是通过网络远程访问。

面向组件的应用程序也更易于扩展。当你需要实现新的需求的时候,你可以提供一个新的组件,而不去影响那些和新需求无关的组件。这些特点使得面向组件的开发降低了大型软件项目长期维护的成本,这是一个最实际的商业问题,也正是如此,组件技术才如此迅速的被接受。

面向组件的应用程序通常可以更快的响应市场,因为你可以有很大的选择空间,不仅仅是自己开发的组件,还可以从第三方厂商来购买某些组件,从而避免了重复制造轮子。这里, VB 就是一个很好的例子,丰富的 ActiveX 控件使得很多人在快速开发中得到了享受。

接口还是继承

面向对象和面向组件另一个重要的不同在于这两种技术在继承和重用模型上的侧重点不同。

在面向对象的分析和设计中,应用程序通常被分解成复杂的类继承结构。类的设计和要解决的商业问题紧密结合。你可以从已有基类继承并特化其行为来实现代码重用。问题在于,这是一种很糟糕的重用的方法。当你从一个基类派生一个子类的时候,你必须对基类的实现有透彻的理解才可能保证不出问题。例如:修改一个成员变量会不会带来副作用?这会对基类中的代码有什么影响?重载基类的方法会不会破坏那些想使用基类版本的客户的行为?等等。

这种形式的重用被称为白盒重用(White-box reuse,因为当你重用的时候你就需要去了解基类实现的细节。显然。白盒重用在可扩展性较高的大型应用中并不经济,也很难得到第三方Framework厂商的支持。

面向组件的开发采用了黑盒重用(Black-box reuse)的方法,它可以让你对组件内部全然不知的情况下来使用组件公开的接口。这样,你就可以远离那些复杂的继承关系。而面向组件的开发者也可以把更多的精力放在制定组件和客户的沟通的接口上了。

最后,面向对象编程提供了有限的工具和设计模式来处理和应用程序运行时相关的问题,例如多线程、并发管理、安全、分布式应用和版本控制等。面向对象的开发者当面对这些“公共需求”的时候,或多或少的需要自己来解决问题。但是面向组件的开发方式却使你在这方面要灵活的多。

posted @ 2006-03-21 13:25 高山流水 阅读(917) | 评论 (0)编辑 收藏

     摘要: Posted on 2005-11-10 16:43 nacci 阅读(346) 评论(3)  编辑 收藏收藏至365Key 所属分类: C++ Experience --> 关于virtual desctructor的详细讨论。同样来自于《Effective C++》3rd Edition。 ...  阅读全文

posted @ 2006-03-21 13:20 高山流水 阅读(299) | 评论 (0)编辑 收藏

 

char   **  p1;                                    //     pointer to pointer to char
const   char   ** p2;                            //     pointer to pointer to const char
char   *   const   *  p3;                          //     pointer to const pointer to char
const   char   *   const   *  p4;                 //     pointer to const pointer to const char
char   **   const  p5;                           //     const pointer to pointer to char
const   char   **   const  p6;                  //     const pointer to pointer to const char
char   *   const   *   const  p7;                 //     const pointer to const pointer to char
const   char   *   const   *   const  p8;        //     const pointer to const pointer to const char


注:p1是指向char类型的指针的指针;
        p2是指向const char类型的指针的指针;
        p3是指向char类型的const指针;
        p4是指向const char类型的const指针;
        p5是指向char类型的指针的const指针;
        p6是指向const char类型的指针的const指针;
        p7是指向char类型const指针的const指针;
        p8是指向const char类型的const指针的const指针。

typedef  char   *  a;        //  a is a pointer to a char 

typedef a b();            
//  b is a function that returns a pointer to a char

typedef b 
* c;             //  c is a pointer to a function that returns a pointer to a char 

typedef c d();           
//  d is a function returning a pointer to a function that returns a pointer to a char 

typedef d 
* e;            //  e is a pointer to a function returning a pointer to a function that a pointer to a char 

e var[
10 ];                //  var is an array of 10 pointers to functions returning pointers to  functions returning pointers to chars.


原文地址:http://www.codeproject.com/cpp/complex_declarations.asp

posted on 2005-11-02 22:08 味全每日C++ 阅读(509) 评论(3)  编辑 收藏收藏至365Key 所属分类: STL

Feedback

# re: 比饶口令还饶口的复杂声明 2005-11-02 22:54 huangyi
所以不喜欢c++  回复
  

# re: 比饶口令还饶口的复杂声明 2005-11-04 10:34 任我行
晕,谁定义的这种变量?
拉出来弹JJ。
  回复
  

# re: 比饶口令还饶口的复杂声明2005-11-06 13:38 neoragex2002
http://neoragex2002.cnblogs.com/archive/2005/11/06/269974.html
推荐看看这里,如何一劳永逸的理解这些复合定义。

posted @ 2006-03-21 13:18 高山流水 阅读(134) | 评论 (0)编辑 收藏

[标题] 一劳永逸:关于C/C++中指针、数组与函数复合定义形式的直观解释

今天又捧起久违的K&R C拜读了一遍。其实有点东西在6年前就想写,借着今天这个机会,终于把它写出来了。

初看一眼标题中的变量定义感觉是不是很抓狂?:)一直以来,C语言中关于指针、数据和函数的复合定义都是一个难点,其实,理解它也是有规律可循的。然而,即便是国内在讲解指针方面久负盛名的“谭本”也没有将这一规律性说清楚,K&R C虽然提到了一点,却始终没有捅破这层窗户纸,也许是K&R觉得以“直观方式”解释太阳春白雪了点吧:)在Blog上面说说这种不值一提的dd倒正合适。

其实,理解C语言中复合定义的关键在于对变量声明语句中各修饰符结合律的把握,我们可以将它们的结合规律简单归纳如下:

(1) 函数修饰符 ( ) 从左至右
(2) 数组修饰符 [ ] 从左至右
(3) 指针修饰符  *  从右至左


其中,(1)与(2)的修饰优先级是相同的,而(3)比前两者的优先级都低,而且是写在左边的。下面我们给出3个直观的例子来说明如何借助结合律来理解复合变量声明,为了简单点,函数修饰符一律使用无形参的签名形式。

示例1. char (*(*x[3])())[5]
这是什么意思?别急,跟着走一遭咱就知道是什么了。根据结合律,我们可以依次写出与x结合的修饰符:

x -1-> [3] -2-> * -3-> () -4-> * -5-> [5] -6-> char

然后我们再来从左至右地对上述过程进行解释:

1说明:x是一个一维数组,数组中元素个数为3
2说明:上述数组中每一个元素都是一个指针
3说明:这些指针都是函数的指针,该函数的签名为( )
4说明:上面的函数所返回的值是一个指针
5说明:上面的指针所指向的是一个一维数组,其元素个数为5
6说明:上面的数组中的每一个元素均是一个字符

不知大家在上面的规范化步骤描述中看出端倪来了没有?:)这个声明的含义是:x是一个由3个指向函数A的指针所组成的一维数组,函数A返回指向一个元素个数为5的字符数组的指针。其实,以结合律来解析复合声明的方式是一种“由近及远”的方式:首先尝试着去说清楚离变量“近”的修饰符的含义,然后再对“远处”的修饰符进行依次说明,从抽象到具体,从顶到底,层层细化。

实际上,我比较反感这种一步到位的复合方式,它不仅把变量定义和类型声明混为一谈,而且也不能直观地体现出类型的含义,更糟糕的是,这不符合典型的“积木化”的程序思维,我更倾向于采用typedef,以一种“由远及近”的方式来逐步定义变量的形态,即先定义若干基本类型,然后再在其基础上将其扩充成复杂类型,最后利用复杂类型定义变量。例如,上述的例子,如果要我来定义,我觉得如此定义比较恰当:

typedef charArrayOfChar[5];
typedef ArrayOfChar* PointerOfArrayOfChar;
typedef PointerOfArrayOfChar (*PointerOfFunc)()
typedef PointerOfFunc ArrayOfPointerOfFunc[3]
ArrayOfPointerOfFunc pfa;


这种“堆积木”的方式实际上和那个复合声明是等价的,其看似繁冗,但对于程序员而言却很直观,所以平心而论,我比较推荐这种积木化声明方式,而不推荐以复合声明直接一步到位。

示例2.char (**x[3])()[5]
根据结合律,将上述声明改写如下:

x -1-> [3] -2-> * -3-> * -4-> () -5-> [5] -6-> char

1说明:x是一个数组,这个数组包括3个元素
2说明:每个元素均为一个指针
3说明:上面的指针又指向另一个指针
4说明:上面的第二个指针是一个函数的指针
5说明:上面的函数返回的是一个数组,这个数组包括5个元素?? (错误!)

从上述推导过程可以发现,当我们到达第5步时,其语义提到了“一个函数返回了一个数组”,这在C语言中实际上是错误的定义,即,( )与[ ]相邻是非法的,因此,编译器将拒绝接受这一关于x变量的声明。同样的,在推导过程中[ ]与( )相邻也是不合法的,什么叫做“一个数组,这个数组里面的每一个元素都是一个函数(而不是一个指针)”?在这种情况下,编译器也会100%报错。

示例3.char p[5][7]char (*q)[7]、char *r[5] 和 char **s
不知p、q、r、s这四个变量类型是否兼容?根据结合律,有:

p -> [5] -> ([7] -> char
const 
q ->  *  -> ([7] -> char)
r -> [5] -> { *  -> char} const
s -> 
*  -> { *
  -> char}

不难发现,无需经过类型强制转换即可将p赋值给q、将r赋给s,而其他的赋值方式均是错误的。为什么?首先,p和r是两个数组,不是指针,因此不能修改其值;其次,不妨让我们来对p与q(或者r与s)在其括号内的类型部分分别进行sizeof运算,可以发现,二者的结果是一样的,即:p、q(或者r、s)指针变量具备一致的增量寻址行为,所以二者才兼容。

看完了上述解释,想必最唬人的指针复合定义恐怕也难不倒你了。试试下面的挑战如何?

1. 解释一下x变量的含义:char
*(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173];
2. 在32位环境下,假设void* p=(void *)(x+1),x=0x1234;则p的16进制值为多少?sizeof(x)等于多少?

posted on 2005-11-06 12:39 neoragex2002 阅读(1210) 评论(11)  编辑 收藏收藏至365Key 所属分类: Win32环境

Feedback

# re: [原创] char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173] ?! 2005-11-06 14:00 Ninputer
我有一点弄不明白,为什么我们需要能看懂和写出char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173];这样的代码呢……  回复
  

# re: [原创] char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173] ?! 2005-11-06 14:15 neoragex2002
为了理解编译器。在抛弃指针之前最好还是先知道它是怎么一回事,否则丢了容易,捡回来就难了。   回复
  

# re: [原创] char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173] ?! 2005-11-06 14:20 无常
有意义吗?  回复
  

# re: [原创] char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173] ?! 2005-11-06 14:23 neoragex2002
看以什么标准对什么人做什么事了。  回复
  

# re: [原创] char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173] ?! 2005-11-06 15:34 neoragex2002
挑战答案:p=0x1238,sizeof(x)=20  回复
  

# re: [原创] char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173] ?! 2005-11-06 20:46 吕震宇
好文!  回复
  

# re: [原创] char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173] ?! 2005-11-07 10:35 DDD
跟天书一样  回复
  

# re: [原创] char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173] ?! 2005-11-07 11:34 oop80
感觉实用意义不大,但是学习学习,搞搞头脑还是有点意思的  回复
  

# re: [原创] char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173] ?! 2005-11-07 12:59 neoragex2002
对于应用开发而言,既然可以避开指针的泥潭,自然不甚实用,这个不用再谈了,没啥意思XD 请自行忽略。

说点有趣的,其实上文这点dd都是当初我在做完了一个蹩脚的qbasic解释器后,继续做一个同样蹩脚的C解释/编译器时总结的,由于是一个人做着玩,所以没有用表驱动,用的递归下降。不知有没有搞过C解释或编译的兄弟路过,我很想听听你们的意见,尤其是指针类型识别和验证方面的,随便聊聊。  回复
  

# re: [原创] char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173] ?! 2005-11-07 23:02 billjoy
看的我冒汗~~~~
有点意思!  回复
  

# re: [原创] char *(*(**(*(*(*x[5])(int,float))[][12])(double))(short,long))[][173] ?!2005-11-20 23:04 SEMAN
这是对最基本语法的掌握与扩充,不过这么复杂的表达式还是不要挑战为好 呵呵  回复

posted @ 2006-03-21 13:17 高山流水 阅读(130) | 评论 (0)编辑 收藏

     摘要: Posted on 2005-11-03 10:03 nacci 阅读(379) 评论(1)  编辑 收藏收藏至365Key 所属分类: C++ Generic --> 自从C++中引入了template后,以泛型技术为中心的设计得到了长足的进步。STL就是这个阶段杰出的产物。STL的目标就是要把数据和算法...  阅读全文

posted @ 2006-03-21 13:15 高山流水 阅读(3578) | 评论 (3)编辑 收藏

 

Posted on 2005-11-03 09:17 nacci 阅读(284) 评论(1)  编辑 收藏收藏至365Key 所属分类: C++ Experience

2005年5月份,Scott Mayers发布了《Effective C++》第三版。作者根据当前C++的特点和设计模式,对第二版中半数以上的内容作了更新。此等佳作,不敢独享,以肆同好。


什么时候一个空的class不是空的?C++会在何时做些什么事情?如果你不声明它们,编译器会为你声明它们自己的拷贝构造函数、一个赋值运算符和一个析构函数。另外,如果你不声明一个构造函数,编译器还会为你创建一个。所有这些自动生成的函数都是publicinline的。例如,如果你写下:

class Empty {};

这和你写下如下的代码本质上是一样的:

class Empty {

    Empty() {…} // default constructor

    Empty(const Empty& rhs) {…} // Copy constructor

    ~Empty() {…}  // destructor - whether it's virtual?

   

    // copy assignment operator

    Empty& operator=(const Empty& rhs) {}

}

当然,这些函数只有它们真正被需要的时候才会被创建。下面这些情况会使得这些函数被创建:

Empty e1; // default constructor & destructor

Empty e2(e1); // copy constructor

e2 = e1; // copy assignment operator

既然编译器会为你创建这些函数,那么这些函数都做些什么工作呢?默认的构造和析构函数主要是让编译器放置一些执行“幕后工作”的代码,例如调用基类和非静态数据成员的构造和析构函数等。需要注意的是编译器为你生成的这个析构函数并不是虚拟的,除非这个类的基类明确声明了一个虚拟的析构函数。

对于拷贝构造函数和赋值运算符,编译器生成的版本只是简单的copy每一个非静态数据成员。例如,考虑一个名为NamedObject的模板,它可以让你把名字和类型T关联起来。

template <typename T>

class NamedObject {

public :

    NamedObject(constchar* name, const T& value);

    NamedObject(const std::string& name, const T& value);

 

private :

    std::string nameValue;

    T objectValue;

};

由于NamedObject中声明了构造函数,编译器便不会再自做主张为你生成一个默认的。这是很重要的。这意味着如果你精心设计的类的构造方式,你就不用再去担心编译器会愚蠢的为你添加一个不带参数的构造函数而破坏你的设计。

NamedObject 中既没有声明拷贝构造函数也没有声明赋值运算符,所以当需要的时候,编译器会自动为你生成。显然,下面的代码需要拷贝构造函数的支持:

NamedObject<int> no1("Smallest Prime Number", 2);

NamedObject<int> no2(no1);

编译器生成的拷贝构造函数必须要使用no1.nameValueno1.objectValue来初始化no2中对应的成员。由于nameValue的类型是string,并且标准的string有一个拷贝构造函数,所以no2.nameValue就可以通过string的拷贝构造函数完成。另外objectValue是一个整数,对于这个内置类型,简单的bit-copy就可以完成复制的任务了。

其实,如果需要的话,编译器会按照和上面提到的相同的手法来为NamedObject来生成一个赋值运算符。但是,只有当生成的代码在语法和语义都都正确的时候,编译器才会为你执行生成工作,如果其中任何一方面除了问题,编译器就会拒绝为你重载operator =

例如:如果我们这样定义NamedObject

template <typename T>

class NamedObject {

public :

    NamedObject(const std::string& name, const T& value);

 

private :

    std::string& nameValue;

    const T objectValue;
};

之后,下面的代码会怎样呢?

std::string newDog("Persephone");

std::string oldDog("Satch");

NamedObject<int> p(newDog, 2);

NamedObject<int> s(oldDog, 36);
p = s; // What should happen?

在复制前,p.nameValues.nameValue分别指向不同的string对象。这个复制应该对p.nameValue做怎样的改变呢?直觉上,p.nameValue将会指向s.nameValue所指的string对象。但是这破坏了C++的一条基本的准则,C++不允许引用指向不同的对象。换句话说,难道改变p.nameValue所引用的对象应该要影响到其它对象所引用的字符串吗?这是编译器生成的赋值运算符应该做的事情吗?

C++ 对于这个问题的解答方法是拒绝编译这样的代码。如果你想让含有引用数据成员的类支持赋值功能,那么你就必须自己定义赋值运算符。对于含有const数据成员的类来说,故事是类似的。修改对象中的const成员总是非法的,所以编译器对于如何处理这种问题一无所知。最后,如果基类把operator=声明为private,那么编译器同样会拒绝为派生类生成operator=。毕竟,一方面,即便编译器可以生成,operator=也只能处理派生类中属于基类的那一部分;另一方面,派生类也根本无权访问基类中的private成员。

时时刻刻让自己记住

l          编译器会在必要的时候隐式生成类的默认构造函数、拷贝构造函数、operator=和析构函数

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

三军未动,资料先行。书山碟海,只列其中最好的。统统都可以emule找到。

1.快速入门
   〈Accelerated.C++〉2000 
   〈Essential C++〉2002 Lipman
     两本都是开篇就讲C++/STL,绝口不提C,而且都有中文版。

2.枕头参考
  〈C++.Primer.4th〉2005 Lippman,第3版有中文版,当入门书看也无不可。
  〈The.C++.Programming.Language.3rd〉Bjarne Stroustrup ,简称TCPL。

3.专门书籍
    STL参考:〈The.C++.Standard.Library-A.Tutorial.And.Reference〉1999
                           号称最好的STL参考,但我觉得很像JDK文档。

    Boost 参考:〈Beyond the C++ Standard Library - An Introduction to Boost〉2005
                           介绍了Boost的一些重要类库,但其余的还是要看Boost自带文档。

    C++ Template参考:〈C++ Templates - The Complete Guide〉2002    
    对template讲得相当深,无愧于The Complete Guide的书名,ytam说可以作为MCD的前传和续集。

〈C++ Common Knowledge〉 中文版, Stephen C. Dewhurst 
   跑杯茶怡然的看看C++里面那些值得吹嘘的知识点是个不错的场景。

4.Effective 类
   Herb Sutter, Andrei Alexandrescu合著一本:
 〈C++.Coding.Standards - 101.Rules.Guidelines〉2004

    Meyers的三本Effecive:
 〈Effective C++ 3rd〉 2005
 〈More Effective C++〉
 〈Effective.STL.50.Specific.Ways.to.Improve.Your.Use.of.STL〉

 〈C++ Gotchas〉2002,Stephen C. Dewhurst

    对于后四本,我老觉得是在语言规范下绕来绕去,从工作角度来看是实用,从个人角度看来是无聊。

5.精力过剩类
  〈Modern C++ Design - Generic Programming and Design Patterns 〉2001 Andrei Alexandrescu ,推荐,C++里最值得一读的书。

   〈C++ Template Metaprogramming Concepts,Tools and Techniques from Boost and Beyond 〉
     
    Herb Sutter的三本Exceptional,rayman说是打击信心用的:
   〈Exceptional C++ - 47 Engineering Puzzles, Programming Problems, and Solutions 〉1999
   〈More Exceptional C++〉2001
   〈Exceptional C++ Style - 40.New.Engineering.Puzzles.Programming.Problems.and.Solutions〉2004

posted on 2005-10-11 11:55 江南白衣 阅读(4078) 评论(17)  编辑 收藏收藏至365Key

评论

# re: C++ 完全不完全资源导引--书籍篇 2005-10-11 12:14 water ye



今晚开始emule  回复  

# re: C++ 完全不完全资源导引--书籍篇 2005-10-11 12:16 梦延续的港湾

收藏先   回复  

# re: C++ 完全不完全资源导引--书籍篇 2005-10-11 14:39 任我行

〈Essential C++〉 中文版,2002 Lipman
〈C++.Primer.4th〉2005 Lippman,
〈Effective C++ 3rd〉 2005
〈More Effective C++〉
以上几本,我这里有电子版的,不过还是喜欢看纸版的。电子邮件联系。

谁有〈The.C++.Programming.Language.3rd〉Bjarne Stroustrup这本书?Mail给我,谢谢。  回复  

# re: C++ 完全不完全资源导引--书籍篇 2005-10-11 16:26 江南白衣

装个emule,什么C++书都可以找到:)  回复  

# re: C++ 完全不完全资源导引--书籍篇 2005-10-12 21:23 冷夜

不错,是好书  回复  

# C 完全不完全资源导引--书籍篇[TrackBack] 2005-10-13 12:57 江南白衣

Ping Back来自:blog.csdn.net
江南白衣引用了该文章,地址:http://blog.csdn.net/calvinxiu/archive/2005/10/13/502618.aspx  回复  

# re: C++ 完全不完全资源导引--书籍篇 2005-10-13 21:14 深白色

emule是什么啊?????????  回复  

# re: C++ 完全不完全资源导引--书籍篇 2005-10-14 11:03 江南白衣

一个p2p工具阿,google一下。  回复  

# re: C++ 完全不完全资源导引--书籍篇 2005-10-14 13:12 深白色

谢谢  回复  

# re: C++ 完全不完全资源导引--书籍篇 2005-10-14 13:38 理寻欢

佩服,看了那么多C++书,好贵哦!
电子书没得现实中的书看起来有味道^Q^.  回复  

# re: C++ 完全不完全资源导引--书籍篇 2005-10-15 10:26 aba

C++学起来可真难啊, 这个语言实在是一个很失败的东西  回复  

# re: C++ 完全不完全资源导引--书籍篇 2005-10-16 11:07 dyn

# re: C++ 完全不完全资源导引--书籍篇 2005-10-15 10:26 aba
C++学起来可真难啊, 这个语言实在是一个很失败的东西

/////////////////////////////////

如果智商确实较低的话,可以考虑只学习C++中相当于java的那部分。  回复  

# re: C++ 完全不完全资源导引--书籍篇 2005-10-16 14:21 Earl

不要自己蠢就说别人的东西不好。你不会相对论吧?那你骂不骂爱斯坦?  回复  

# C 完全不完全资源导引--书籍篇 [TrackBack] 2005-10-20 00:21 xkxingkong

Ping Back来自:blog.csdn.net
xkxingkong引用了该文章,地址:http://blog.csdn.net/xkxingkong/archive/2005/10/20/509671.aspx  回复  

# re: C++ 完全不完全资源导引--书籍篇 2005-10-23 20:40 Ninputer

C++确实有设计得不好的地方,但世界上没有第二个语言可以做这么多事。所以我们只能接受现实。。  回复  

# re: C++ 完全不完全资源导引--书籍篇 2005-10-31 15:37 飞翔的鸟儿拒绝忧伤

好好学习先!~!~  回复  

# re: C++ 完全不完全资源导引--书籍篇2005-10-31 16:08 dragonpro

C++的确学的东西瞒多的,慢慢学吧  回复  

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

函数存放在内存的代码区域内,它们同样有地址,我们如何能获得函数的地址呢?

  如果我们有一个int test(int a)的函数,那么,它的地址就是函数的名字,这一点如同数组一样,数组的名字就是数组的起始地址。

  定义一个指向函数的指针用如下的形式,以上面的test()为例:

int (*fp)(int a);//这里就定义了一个指向函数的指针

  函数指针不能绝对不能指向不同类型,或者是带不同形参的函数,在定义函数指针的时候我们很容易犯如下的错误。

int *fp(int a);//这里是错误的,因为按照结合性和优先级来看就是先和()结合,然后变成了一个返回整形指针的函数了,而不是函数指针,这一点尤其需要注意!

  下面我们来看一个具体的例子:


#include <iostream> 
#include <string> 
using namespace std; 
 
int test(int a); 
 
void main(int argc,char* argv[])   

    cout<<test<<endl;//显示函数地址 
    int (*fp)(int a); 
    fp=test;//将函数test的地址赋给函数学指针fp 
    cout<<fp(5)<<"|"<<(*fp)(10)<<endl; 
//上面的输出fp(5),这是标准c++的写法,(*fp)(10)这是兼容c语言的标准写法,两种同意,但注意区分,避免写的程序产生移植性问题! 
    cin.get(); 

 
int test(int a) 

    return a; 
}


typedef定义可以简化函数指针的定义,在定义一个的时候感觉不出来,但定义多了就知道方便了,上面的代码改写成如下的形式:

#include <iostream> 
#include <string> 
using namespace std; 
 
int test(int a); 
 
void main(int argc,char* argv[])   

    cout<<test<<endl; 
    typedef int (*fp)(int a);//注意,这里不是生命函数指针,而是定义一个函数指针的类型,这个类型是自己定义的,类型名为fp 
    fp fpi;//这里利用自己定义的类型名fp定义了一个fpi的函数指针! 
    fpi=test; 
    cout<<fpi(5)<<"|"<<(*fpi)(10)<<endl; 
    cin.get(); 

 
int test(int a) 

    return a; 
}

posted @ 2006-03-21 12:07 高山流水 阅读(130) | 评论 (0)编辑 收藏

 
  一、什么是 C#?
C# 是由Microsoft开发的一种新型编程语言,由于它是从C和C++ 中派生出来的,因此具有C++的功能。同时,由于是Microsoft公司的产品,它又同 VB一样简单。对于web开发而言,C#象 Java,同时具有Delphi的一些优点。Microsoft宣称:C#是开发.NET框架应用程序的最好语言。
二、Java与 C#
要学习C#,不能不首先看一看Java语言。
相对于其他编程语音,Java有一个无庸置疑的优点:用户以及编译器第一次不必了解生成可执行代码的特定CPU细节。Java引入了一个编译代码中间层,叫做字节代码,并使用一个虚拟抽象的机器,而不是一个真实的机器。当Java编译器结束了一个源文件的编译后,你所得到的不是可以立即在一个给定平台上运行的代码,而是可以在任何真实的平台上运行的字节代码,唯一的条件就是这个平台要理解和支持Java。这些发展包含着一个文化的变革。作为一个开发人员,你只需要确定Java虚拟机(JVM)提供的抽象层,不同的OS销售商负责执行代码层,从而将中立于平台的字节代码映射到主机平台的机构中。在这种情况下,Java似乎是统一分布式计算机世界的领袖候选人了。“编写一次,永远运行”(并且无论在哪里)一度成为Java诱人但却真实的口号。
那么为什么说Java只是“似乎”是一个好东西呢?跨平台理论的发展很好地证明了这一点。我们看到,将Java代码从一个平台移植到另一个平台—Java这个语言最重要和最受吹捧的特点—并不象宣传的那样容易。任何Java平台都有其自己的虚拟机,它可以理解通用的字节代码,并且及时地将其编译为本地代码。矛盾由此产生,不同虚拟机的执行也很不相同,这一点足以使代码的移植比预期耗费多得多的时间,而且基本上不是自动的。
那么,Java模型的好处在哪里呢?首先,Java是一种先进的、面向对象的语言,包含了预防常见错误的内置功能,并在仅仅一两个对象中携带了许多经常需要用到的功能。与C++相比,Java更易于读写,不容易出错,而且更加美观,但是它速度较慢也不太灵活。想实现在任何软件和硬件平台上都可虚拟移植,Java尽可能少地使用了公分母模型,也就是说放弃了将每个平台开发到极限的能力。第二,虚拟机的概念本身就是可移植和可共用的,因此对于分布式环境来说是理想的。Java对于为非Windows平台开发代码是最好的语言。
那么对于Windows平台来说,Java又怎么样呢?让Java适应Windows是不可能的,这是由于Sun的许可约束问题。但是Java实在是太吸引人了,Microsoft比谁都能更清楚这一点。因此,Microsoft又一次采取了“拿来主义”的手法,很好地利用了Java 的众多特性,隆重推出了Windows平台的新锐力量,它就是相当简单但十分强大的面向对象的C#编程语言。C#超过了C++,它天生就包含了.NET框架类库中的所有类,并使语法简单化。
可以说,Java具备的优点,C#都可以或者都将具备!
三、C#的主要特征
C# 是.NET的关键性语言,它是整个.NET平台的基础。与C#相比,.NET所支持的其它语言显然是配角身份。比如,VB.NET的存在主要是对千万个VB开发人员的负责。对于JScript.NET和 Managed C++ 也同样可以这么说,后者只是增加了调用.NET类的C++ 语言。C#是唯一没有在设计思路中加入了前辈语言某种遗传的新事物。
.NET平台将 C#作为其固有语言,重温了许多Java的技术规则。C#中也有一个虚拟机,叫做公用语言运行环境(CLR),它的对象也具有同样的层次。但是 C#的设计意图是要使用全部的Win32 API甚至更多。由于C#与 Windows的体系结构相似,因此 C# 很容易被开发人员所熟悉。
Java的目的是要拯救分布式计算世界,C# 则不同。C#本质上是C++的进化产物,使用了包括声明、表达式及操作符在内的许多C++特征,但是 C#还有更多的增强功能,比如类型安全(type-Safe)、事件处理、碎片帐集、代码安全性等。在C#中,除了可以使用许多API,更能使用.NET类。特别地是,我们可以处理COM的自动化和C类型的函数。
C#还让你调用无管理的代码,也就是在CLR引擎控制之外的代码。这种不安全的模式允许你操作原始指针来读和写内置碎片帐集控制以外的内存。
四、安装运行环境
安装.NET SDK 是在机器上运行C# 的第一步。.NET SDK 可以安装在Windows ME、Windows NT或Windows 2000上,但是最好的选择是Windows 2000上。选择了操作系统后,再执行以下步骤:
●安装 IE 5.5
●安装Microsoft .NET Framework SDK。它是免费的,可以从以下站点下载.NET Framework SDK.
●完成以上安装后,就可以在任何文本编辑器中编写代码了,最后保存为扩展名为.cs的文件

五、C# 编辑器
编写C#程序可以在文本编辑器中进行,或者在集成开发环境Visual Studio中进行。市场上还有一些第三方编辑器,其中一些是免费的。
六、C#的程序结构
一个C#程序包含一个类,这个类中至少有一个公用的静态方法Main,这个方法对程序进行初始化并终止程序。在Main方法中创建子对象、执行方法并实现软件的逻辑处理。下面是一个典型的微型C#例程:
using System;
class MyFirstApp
{
static int Main(String[] args)
{
System.Console.WriteLine ("Hello .NET");
return 1;
}
}
在C#中,要使用下面的声明来引入外部定义,而不是用象C++中的#include:
using System;
using System.Data;
然后,使用C#编译器csc.exe编译代码。假定将前面的代码保存为文件hello.cs,使用以下命令:
csc hello.cs
结果就生成了hello.exe,它向控制台输出窗口写入信息 "Hello .NET"。
尽管编译后的结果文件包含.exe后缀,但hello.exe却不是一个真正的、明确的CPU代码段。实际上,它包含了.NET字节代码。当启动hello.exe时,CLR 将提取编译器写入代码中的重要元数据。接着,一个叫做 Just-In-Time 编译器的模块将代码映射到特定的 CPU中,开始实际的执行过程。
七、C# 和名称空间
实际中的 C#程序通常包含多个文件,其中每个文件都可以包含一个或多个名称空间。一个名称空间就是一个名字,它向编译器描绘出一些软件实体,如类、界面、枚举以及嵌入的名称空间。名称空间和数据类型一样必须有唯一的名称。在一个C#程序中,可以通过一个元素的完整资格名称来识别它,这个资格名称表明出层次关系。例如,System.String是 .NET String类型完整的资格名称。但是为了简化代码起见,只要声明正在使用System名称空间:
using System;
就可以使用一个相对名称如String来作为完整名称的同义词,而最后依然代表 System.String。
通过使用namesapce关键字,我们还可以将C#程序或者类包裹在自身的名称空间中,比如:
namespace MyOwn
{
using System; // for String
class MyFirstApp
{
static int Main(String[] args)
{
System.Console.WriteLine ("Hello .NET");
return 1;
}
}
}
名称空间 MyOwn 是全局名称空间的一部分。调用它不需要再使用前缀,因为其完整资格名称就是简单的MyOwn。定义一个名称空间是保持公共名称唯一性的一个途径。实际上,如果两个类的名称发生冲突,但只要它们分别属于不同的名称空间,两个类仍然是各自唯一的。

posted @ 2006-03-21 12:06 高山流水 阅读(162) | 评论 (0)编辑 收藏

 

 

ASCII   输出:
ofstream fout;
fout.open("output.txt");
//ofstream fout("output.txt");
int num = 150;
char name[] = "John Doe";
fout << "Here is a number: " << num << "\n";
fout << "Now here is a string: " << name << "\n";
fout << flush;
fout.close();
//Here is a number: 150 Now here is a string: John Doe


ASCII  输入:
//12 GameDev 15.45 L This is really awesome!
ifstream fin("input.txt");
int number;
float real;
char letter, word[8];
fin >> number; fin >> word; fin >> real; fin >> letter;
//fin >> number >> word >> real >> letter;
文件的每个空白之后, ">>" 操作符会停止读取内容, 直到遇到另一个>>操作符. 因为我们读取的每一行都被换行符分割开(是空白字符), ">>" 操作符只把这一行的内容读入变量。这就是这个代码也能正常工作的原因。
如果你想把整行读入一个char数组, 我们没办法用">>"?操作符,因为每个单词之间的空格(空白字符)会中止文件的读取。为了验证:
fin.getline(sentence, 100);

二进制 输入输出
ofstream fout("file.dat", ios::binary);
int number = 30; fout.write((char *)(&number), sizeof(number));
二进制文件最好的地方是可以在一行把一个结构写入文件。 如果说,你的结构有12个不同的成员。 用ASCII?文件,你不得不每次一条的写入所有成员。 但二进制文件替你做好了。 看这个。

struct OBJECT { int number; char letter; } obj;
obj.number = 15;
obj.letter = ‘M’;
fout.write((char *)(&obj), sizeof(obj));
  这样就写入了整个结构! 接下来是输入. 输入也很简单
ifstream fin("file.dat", ios::binary); fin.read((char *)(&obj), sizeof(obj));

 

更多方法
 

检查文件
你已经学会了open() 和close() 方法, 不过这里还有其它你可能用到的方法。
方法good() 返回一个布尔值,表示文件打开是否正确。
类似的,bad() 返回一个布尔值表示文件打开是否错误。 如果出错,就不要继续进一步的操作了。
最后一个检查的方法是fail(), 和bad()有点相似, 但没那么严重。

读文件
方法get() 每次返回一个字符。
方法ignore(int,char) 跳过一定数量的某个字符, 但你必须传给它两个参数。第一个是需要跳过的字符数。 第二个是一个字符, 当遇到的时候就会停止。 例子,

fin.ignore(100, ‘\n’);
会跳过100个字符,或者不足100的时候,跳过所有之前的字符,包括 ‘\n’。
方法peek() 返回文件中的下一个字符, 但并不实际读取它。所以如果你用peek() 查看下一个字符, 用get() 在peek()之后读取,会得到同一个字符, 然后移动文件计数器。
方法putback(char) 输入字符, 一次一个, 到流中。我没有见到过它的使用,但这个函数确实存在。

写文件
只有一个你可能会关注的方法.?那就是 put(char), 它每次向输出流中写入一个字符。

打开文件
当我们用这样的语法打开二进制文件:

ofstream fout("file.dat", ios::binary);
  "ios::binary"是你提供的打开选项的额外标志. 默认的, 文件以ASCII方式打开, 不存在则创建, 存在就覆盖. 这里有些额外的标志用来改变选项。

ios::app 添加到文件尾
ios::ate 把文件标志放在末尾而非起始。
ios::trunc 默认. 截断并覆写文件。
ios::nocreate 文件不存在也不创建。
ios::noreplace    文件存在则失败。

文件状态
  我用过的唯一一个状态函数是eof(), 它返回是否标志已经到了文件末尾。 我主要用在循环中。 例如, 这个代码断统计小写‘e’ 在文件中出现的次数。

ifstream fin("file.txt");
char ch; int counter;
while (!fin.eof()) {
      ch = fin.get();
      if (ch == ‘e’) counter++;
}
fin.close();
  我从未用过这里没有提到的其他方法。 还有很多方法,但是他们很少被使用。参考C++书籍或者文件流的帮助文档来了解其他的方法。

结论
  你应该已经掌握了如何使用ASCII文件和二进制文件。有很多方法可以帮你实现输入输出,尽管很少有人使用他们。 我知道很多人不熟悉文件I/O操作,我希望这篇文章对你有所帮助。 每个人都应该知道. 文件I/O还有很多显而易见的方法,?例如包含文件 <stdio.h>. 我更喜欢用流是因为他们更简单。 祝所有读了这篇文章的人好运, 也许以后我还会为你们写些东西。 
 
特殊
 get()成员函数会在文件读到默尾的时候返回假值,所以我们可以利用它的这个特性作为while循环的终止条件,


  string  s;
 getline( cin, s );
cout 
<<   " You entered  "   <<  s  <<  endl;

// s input to buffer .
in : 123   456   789
out :you entered  123   456   789  


char  c[ 10 ];

   cin.getline( 
& c[ 0 ],  5 ' '  );
   cout 
<<  c  <<  endl;

//  max is 5, when  'a  '(<5) is come , input is over .




getline ();



there 
is  a way to read  in  the whole line, and it  is  the method getline(). This  is  how we would  do  it.


fin.getline(sentence, 
100 );


Here are the parameters to the function. The first parameter 
is  obviously the  char  array we want to read  in  to. The second  is  the maximum number of characters we will read  in  until we encounter a  new  line. So now sentence contains  " This is really awesome! "  just like we wanted.



memset()
///Sets buffers to a specified character.

char buffer[] = "This is a test of the memset function";

   printf( "Before: %s\n", buffer );
   memset( buffer, '*', 4 );
   printf( "After:  %s\n", buffer );

 

 

 //char buf[30];
 //memset( buf, '\0', 30 );   //set '\0' to buffer   
 //string str = "Trying is the first step towards failure.";
 //str.copy( buf, 1 );
 //cout << buf << endl;

posted @ 2006-03-21 12:05 高山流水 阅读(305) | 评论 (0)编辑 收藏

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