|
地板
楼主 |
发表于 2011-10-22 15:39:19
|
只看该作者
4.11 C的抽象范式之OOP
其实我觉得C++之父不必把它的c前端版本的C++弄成一个独立的编译器,因为不这样的话,写出来的OO代码也是C的,这样就能极大范围地做到语法上统一,也能将接口保持在C函数的级别供脚本语言使用,而即使在这些模拟中,不少正是直接对应一模一样或几乎一样的C++的本质。无论如何,下面我们讨论一个前端版本的OO模拟,顺便学习一下C++面向对象的内容本质。
先来看看面向对象几种层次抽象
我们知道在C++实现的OO机制中,存在class,class=type,即template中的type,但是我们知道template更像是一种建立在C++类型机制上的脚本语言,其语法古怪,不接近C++本身,,C++用class作为派生type的类型,用type作为template中的类型。
class可用来派生子类型也可用来产生对象(我们用措词上的区别来区分这二者),派生的子类型subclass,此时它也是一个class,,产生的object,我们称一个对象为class的instance.class为object model
这其中存在一些特例,比如纯抽象的class(用来实现C++版本的interface),,或者静态类,,
为了产生逻辑,我们可以继承,也可以组合,继承就是从相对基类中(除了单根继续的语言,基类都是严格相对的)单层继承或多层继续,产生新的类,这样的子类在抽象上继续了父类的所有属性,在底层实现上其实是叠加了子类的代码在基类上的组合代码,是发生在类间的逻辑。也可以由类产生对象以产生逻辑,是发生在类和对象之间的逻辑,,,注意多层继续在现代OO中是不受鼓励的。。
我们将会在下面的RTTI部分讨论以上机制在底层的对应实现。。
而发生在对象之间的逻辑呢,是采用将一个对象作为另一个对象的数据成员来作为产生逻辑的方式,,对象之间交互的方式就称为消息,C开发的函数范式在这里被消息所代替,实际上我们知道消息也是函数调用,只不过这里的函数是加了访问控制和多态的特殊性函数。。
C的模拟OOP主要有以下几个技术,1,用预处理宏定义keyword和定义结构,2,实现rtti.当然,在C++中写多态有三种方式,1,是C++扩展了C的普通多态函数(重载只是多态的简单手段,稍后谈到的2,3才是高级手段),2是OO里面的多态函数,3是模板的泛型多态,,我们只在这里讨论oo里面的东西,,并不打算讨论其它的机制。比如C++所有的3种多态。纯adt的interface机制,也不讨论C++中C没有的异常机制。更不打算讨论templaste。我们只模拟OOP的典型和核心特征。。
首先就class来说,如何模拟呢,
4.12 C的观点:底层不需要直接抽象
抽象是为了简单化,抽象=对机器的简单性=对人的复杂性
C++在语言语法级集成的抽象太多了,而且它的库级抽象也太多了,BOOST,编译期的泛型抽象,运行期的OO泛型抽象等,,OO抽象等,,无一不表示,C++更适合当一门靠近应用的语言(只有高层应用要求抽象,底层开发就要不得太多抽象,因为机器本身就是机器,C就是对应机器的,无抽象必要),,,这造成的结果是要全面掌握C++要求人们掌握的知识太多了,C++的这么多因素使C++变成了多范型,完全密封远离低层(然而你要知道,抽象意味着对底层的迂回,对人类思维的靠扰,这迂回多了,对底层的访问就少了),,,,其实C++也明显没有损耗C操作底层的能力(我这里仅指它OO范型和模板等高级语法机制),,,,但是它提供高级的语言级和库级的抽象反而对人们要求掌握它的成本变得过高了,这就是说,C++对于开发人员提供的"形式,工具"过多(至少比C多).,提出了很多抽象,.在提供的抽象方面,C只有指针,函数指针,结构体,这些有限的东西,而C++呢,有OO,有TEMPLATE(语法级),有STL,有BOOST(库级)这么多
在开发人员这端,C++代码复杂难解,因为一个会读C++源码的人首先要理解语言本身,才能理解作者要想在代码中描述的现实事物(机器逻辑通过C++对于应用逻辑和现实事物的变换),,,而这里面,抽象太多了(甚至过度抽象,过度设计)
用OO表现的现实事物,相对于C用结构和函数表达的现实事物来说,,,后者更接近机器.C本身的那么有限的语法机制,即使离开了语法级的OO,C也可直接用函数级的第一类型(组成的模块)和结构体数据抽象,和指针,,这些东西描述应用领域,比如Windows用C表达消息,,jxta用c表达管道,端点这些概念,,,
语言的选择永远离不开应用,要开发底层,,C比C++好,WEB开发是那些远离底层的逻辑,用JAVA可以,但是C其实也是OO的,它的结构体,本身就是一种数据抽象,比如我要描述一个学生,我就选择性的定义一个struct student,里面加上学分啊,身高啊这些数据,(这是最好的方法,比C++的类都要好,为什么呢,因为C语言的类型机制就是计算机的类型机制,,这些定义数据抽象做到了机器跟应用域事物的一一对应,非常地好)
而定义关于数据的抽象是冯氏语言模型的根本,我们知道可以用设计数据的方式抽象DSL概念词汇,系统编程支持词汇。
JAVA明显不好用来开发底端,,这就是语言适配应用的道理,,但是计算机终久是计算机,如果你想学习计算机本身而不仅仅是想通过掌握JAVA开发WEB,那么C+LINUX永远是最好的选择,因为开源的东西一般出现在LINUX下.而且通常用C开发的,世界上开源项目最多的就是C了.
一句话,C比C++简单,如果想开发图形啊,网络啊,这些跟计算机本身有关的东西,这样的一个想靠掌握语言马上编程的初学者最好去学习C,而不是C++
4.13 指针:间接操作者
type*
这个形式就表示一种类型,,,,如果说type本身也是一种类型的话,那么在type后加一个星号也表示一种类型,,这种类型叫“指向这种type的指针类型(重要的是最后的指针类型这四个字)”,,你可以联想dephi中的ptype类型
所以你就可以这样定义东西
type* someobj;
(*可向type靠近或someobj靠近,C程序员偏向于向someobj而C++程序员偏向于向type靠近,这样的话就有二种等价意义,type *someobj表示*someobj是一种type变量,type* someobj表示someobj是一个指向type的指针)
someobj就是一个指针变量,它代表指向此type的的指针变量,,因此在这里type*是类型(是一种指针意义上的数据类型),someobj是这种类型的一个变量
根本上引用只是一个指针的别名,因此你可以由一个指针得到一个或多个引用(即关于该指针指向的对象的引用)
指针本质上是一个32位long int(因此在Generic progamming领域有“用整型代替类型”的说法),定义一个指针时可定义void*型指针(因为指针并不一定出生时就要指向一个对象或一块内存,而引用要求有一个初始值,因为引用本质是指针的别名),因此它可以被指定为0,即空指针,也可以被重新赋值(此时它就不指向它原来指向的对象或内存了),,换言之,,多个指针可以同时指向一个对象,你可以为一个对象定义多个指向它的指针(但是它并不拥有这一内存,也就是说,当它指向的对象发生了变化,指针仅仅作为指向这个对象在内存中的位置的意义就会失效或过时,它只拥有对象的adress值而非value值),而你可以通过这些指针或引用来操作该对象
一般来说,引用常常跟const在一起(加上const只是为了强制跟保险),那是因为定义出来的引用常常不会改变
type*
这个形式就表示一种类型,,,,如果说type本身也是一种类型的话,那么在type后加一个星号也表示一种类型,,这种类型叫“指向这种type的指针类型(重要的是最后的指针类型这四个字)”,,你可以联想dephi中的ptype类型
所以你就可以这样定义东西
type* someobj;
(*可向type靠近或someobj靠近,C程序员偏向于向someobj而C++程序员偏向于向type靠近,这样的话就有二种等价意义,type *someobj表示*someobj是一种type变量,type* someobj表示someobj是一个指向type的指针)
someobj就是一个指针变量,它代表指向此type的的指针变量,,因此在这里type*是类型(是一种指针意义上的数据类型),someobj是这种类型的一个变量
根本上引用只是一个指针的别名,因此你可以由一个指针得到一个或多个引用(即关于该指针指向的对象的引用)
指针本质上是一个32位long int,定义一个指针时可定义void*型指针(因为指针并不一定出生时就要指向一个对象或一块内存,而引用要求有一个初始值,因为引用本质是指针的别名),因此它可以被指定为0,即空指针,也可以被重新赋值(此时它就不指向它原来指向的对象或内存了),,换言之,,多个指针可以同时指向一个对象,你可以为一个对象定义多个指向它的指针(但是它并不拥有这一内存,也就是说,当它指向的对象发生了变化,指针仅仅作为指向这个对象在内存中的位置的意义就会失效或过时,它只拥有对象的adress值而非value值),而你可以通过这些指针或引用来操作该对象
一般来说,引用常常跟const在一起(加上const只是为了强制跟保险),那是因为定义出来的引用常常不会改变
4.14 真正的typedef
typedef是类型替代名(typedef=type dfine嘛),在理解时可以按以下的方法进行
1.typedef是定义一种类型的子集,,,比如typedef int INT;(这意思就是说,INT是int的一个子集)
2.类extern的这样一种声明(也即仅仅是对类型的向外的一种再声明),如extern int myint;(这意思就是说,作为变量的myint这里作为一种“新的类型”,而且是一种“int类型”,,,这是一种当已有类型的再声明)
也即,extern就是相对于变量,typedef就相对于类型(注意它们二者并不实际等价,也即不会有myint这个实际变量被声明出来)
注意,一般myint要大写,但是这里为了说明还是采用它的小写形式
由第二种理解方式可以导出很多,如typedef int (*myint)(),,,可理解为extern int (*myint)(),,这里的myint也是一种类型,这种类型表示“指向一个返回int类型的函数的指针类型”
4.15 真正的指针类型
type*
这个形式就表示一种类型,,,,如果说type本身也是一种类型的话,那么在type后加一个星号也表示一种类型,,这种类型叫“指向这种type的指针类型(重要的是最后的指针类型这四个字)”,,你可以联想dephi中的ptype类型
所以你就可以这样定义东西
type* someobj;
(*可向type靠近或someobj靠近,C程序员偏向于向someobj而C++程序员偏向于向type靠近,这样的话就有二种等价意义,type *someobj表示*someobj是一种type变量,type* someobj表示someobj是一个指向type的指针)
someobj就是一个指针变量,它代表指向此type的的指针变量,,因此在这里type*是类型(是一种指针意义上的数据类型),someobj是这种类型的一个变量
根本上引用只是一个指针的别名,因此你可以由一个指针得到一个或多个引用(即关于该指针指向的对象的引用)
指针本质上是一个32位long int(因此在Generic progamming领域有“用整型代替类型”的说法),定义一个指针时可定义void*型指针(因为指针并不一定出生时就要指向一个对象或一块内存,而引用要求有一个初始值,因为引用本质是指针的别名),因此它可以被指定为0,即空指针,也可以被重新赋值(此时它就不指向它原来指向的对象或内存了),,换言之,,多个指针可以同时指向一个对象,你可以为一个对象定义多个指向它的指针(但是它并不拥有这一内存,也就是说,当它指向的对象发生了变化,指针仅仅作为指向这个对象在内存中的位置的意义就会失效或过时,它只拥有对象的adress值而非value值),而你可以通过这些指针或引用来操作该对象
一般来说,引用常常跟const在一起(加上const只是为了强制跟保险),那是因为定义出来的引用常常不会改变
type*
这个形式就表示一种类型,,,,如果说type本身也是一种类型的话,那么在type后加一个星号也表示一种类型,,这种类型叫“指向这种type的指针类型(重要的是最后的指针类型这四个字)”,,你可以联想dephi中的ptype类型
所以你就可以这样定义东西
type* someobj;
(*可向type靠近或someobj靠近,C程序员偏向于向someobj而C++程序员偏向于向type靠近,这样的话就有二种等价意义,type *someobj表示*someobj是一种type变量,type* someobj表示someobj是一个指向type的指针)
someobj就是一个指针变量,它代表指向此type的的指针变量,,因此在这里type*是类型(是一种指针意义上的数据类型),someobj是这种类型的一个变量
根本上引用只是一个指针的别名,因此你可以由一个指针得到一个或多个引用(即关于该指针指向的对象的引用)
指针本质上是一个32位long int,定义一个指针时可定义void*型指针(因为指针并不一定出生时就要指向一个对象或一块内存,而引用要求有一个初始值,因为引用本质是指针的别名),因此它可以被指定为0,即空指针,也可以被重新赋值(此时它就不指向它原来指向的对象或内存了),,换言之,,多个指针可以同时指向一个对象,你可以为一个对象定义多个指向它的指针(但是它并不拥有这一内存,也就是说,当它指向的对象发生了变化,指针仅仅作为指向这个对象在内存中的位置的意义就会失效或过时,它只拥有对象的adress值而非value值),而你可以通过这些指针或引用来操作该对象
一般来说,引用常常跟const在一起(加上const只是为了强制跟保险),那是因为定义出来的引用常常不会改变
4.16 真正的函数指针
C和C++都不允许一个真正的函数作为参数,,将函数作为参数传送的办法是将它用一个指针去指向这个函数,,,,
而且,通过函数指针和函数模板,可以很有效地实现业务逻辑跟界面逻辑分开
学习函数指针完全是一种挑战,你能分别出以下的吗?
int *(*(*foo)(int))[5];
这个就表示:foo是一个函数指针(这就是括号带星号的结果-即把*foo括起来的那个括号),它指向一个函数(以指向它的指针foo命名的函数),它带一个int参数(即函数指针foo紧靠右的那个括号和括号内的int),,并返回一个指向数组的指针 |
|