`
shi5jin
  • 浏览: 37185 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
社区版块
存档分类
最新评论

站在巨人肩上的思考 [连载] (2)

阅读更多
 

<st1:chmetcnv tcsc="0" w:st="on" unitname="C" sourcevalue="1.3" numbertype="1" negative="False" hasspace="True">1.3 C</st1:chmetcnv>++的设计

tips: "If builders built buildings the way programmers wrote programs, then the first woodpecker that came along would detroy civilization."  -- Gerald Weinberg
--------------------------------
   “一个设计良好的用户自定义类型与一个内部类型之间的差异仅仅在于其定义的方式,而不在其使用的方式”[1]
       这一句是stroustrup关于类设计的一句忠告。我觉得是一个设计风格的问题,也挺重要的。
       首先说说“定义”。在C++中有几个相互关联的术语:“声明”(declaration)、“定义”(definition)和“实现”(implement)。初次学习C/C++的时候,我总是把他们三个混为一谈,或者说没有想去认真区别一下彼此。看过好多书之后发现,这点其实蛮重要的。
u       声明:“是一个语句,它为程序引入了一个名字,还为这个名字确定了一个类型。”[2]<o:p></o:p>
也就是说,声明一般仅仅为两项内容:名字(变量),以及名字的类型(当然一般还需要一个逗号,或分号)。例如:
cpp 代码
  1. int intVar;   
  2. double yourCash, myCash;   
  3. class Student;   
  4. std::string studentName;   
  5. int intMax(intint);  

<v:shapetype id="_x0000_t202" path="m,l,21600r21600,l21600,xe" coordsize="21600,21600" o:spt="202"><v:stroke joinstyle="miter"></v:stroke><v:path o:connecttype="rect" gradientshapeok="t"></v:path></v:shapetype><v:shapetype id="_x0000_t75" path="m@4@5l@4@11@9@11@9@5xe" coordsize="21600,21600" o:spt="75" o:preferrelative="t" filled="f" stroked="f"><v:stroke joinstyle="miter"></v:stroke><v:formulas><v:f eqn="if lineDrawn pixelLineWidth 0"></v:f><v:f eqn="sum @0 1 0"></v:f><v:f eqn="sum 0 0 @1"></v:f><v:f eqn="prod @2 1 2"></v:f><v:f eqn="prod @3 21600 pixelWidth"></v:f><v:f eqn="prod @3 21600 pixelHeight"></v:f><v:f eqn="sum @0 0 1"></v:f><v:f eqn="prod @6 1 2"></v:f><v:f eqn="prod @7 21600 pixelWidth"></v:f><v:f eqn="sum @8 21600 0"></v:f><v:f eqn="prod @7 21600 pixelHeight"></v:f><v:f eqn="sum @10 21600 0"></v:f></v:formulas><v:path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></v:path><o:lock aspectratio="t" v:ext="edit"></o:lock></v:shapetype>

而向如下的样式则不能称之为“声明”,而是“声明”+“定义”:
cpp 代码
  1. int intVar = 0;   
  2. double yourCash = 0.0, myCash = 10e+10;   
  3. class myClass {}; //an empty class;   
  4. std::string studentName(“”);   

u       定义(或对内置类型也可叫初始化):一个语句(块),它为程序中的一个类型(或变量)规定了其内部框架(或值)。例如:
cpp 代码
  1. int intVar;       //declaration;   
  2. intVar = 3;   //definition;   
  3.     
  4.  //class definition;   
  5. class Student {   
  6.     int _studentNo;   
  7.     char _gender;   
  8.     std::string _birth;   
  9. public:   
  10.     int number(voidconst;   
  11.     char gender(voidconst;   
  12.     std::string birth(voidconst;   
  13. };     
  14.   
  15. //a function definition/implement;   
  16. int intMax(int a, int b)  {   
  17.     return (a > b ? a : b);   
  18. }     

u       实现:主要针对类成员以及函数而言,即用具体算法实现成员、函数既定的功能。例如:
cpp 代码
  1. int    
  2. Student::number(voidconst {   
  3.     return _studentNo;   
  4. }    

        所以有的时候,函数的定义也可以成为实现。而有的时候,类的定义却可以成为类的声明。[3]
       通过上面的例子,我们已经可以看出,一个内置的类型和一个用户自定义的类型(class类)的定义的确不同。为了实现自定义类型所期望表达的类型内涵,我们必须给出需要的数据成员和用户接口[4]。但内置类型则不用,这是内置定义好了的。我们所要关心的是:为保持程序整体风格的一致,通过定义合适的用户接口,使用户在使用这些接口时,感觉就像使用一个内置类型一样。这将大大降低使用及维护成本。
       例如我们想有一个用于数学、工程计算的Matrix类。当我们设计的该类的时候,我们就应该提供与STL相一致的接口、即功能,如我们可以这样声明、使用一个Matrix类:
cpp 代码
  1. using namespace gavin::math;   
  2. Matrix matrix1(3, 5), matrix(5,3);   
  3. matrix[0][0] = 3, matrix1[0][1] = 2;   
  4. //…初始化matrix1和matrix2;   
  5. Matrix result( matrix1 * matrix2 );   
  6. cout << “We can print out the result:  “ << result << endl;   
  7. cout << “after multiply with 3 is: “ << 3*result << endl;   
  8. cout << “And we can plus them: “ << matrix1 + matrix2 << endl;   
  9. cout << “Sure can minus: “ << matrix1 – matrix2 << endl;   
  10. cout << “Even get a negative: “ << -matrix2 << endl;   
  11. cout << “Get the Matrix’s size: “ << matrix2.size() << endl;   
  12. Matrix::iterator iter = result.begin(),   
  13.                 iterEnd = result.end();   
  14. for(; iter != iterEnd; ++iter) {   
  15.     cout << *iter << endl;   
  16. }    
             
       我们所编写、自定义的类型,在使用上不仅要有与标准STL相同的接口名称,而且更主要的是要提供与STL接口相同的语义--相同的接口名称就应该实现相同或起码也是相近、不冲突的功能。而不应该采用C或其他语言的惯例,用一个函数去实现类似的功能。例如以下的用法是可以避免的:
cpp 代码
  1. printMatrix(matrix1);  printMatrix(matrix2);   
  2. matrixPlus(matrix1, matrix2);   
             
       假如你有些实践经验,那么采取标准STL风格的自定义类型的好处是显而易见的。无论是使用,还是后期的维护。
       “使用类型系统去检查函数的参数,去保护数据不被意外地破坏,去提供新的类型,去提供新的操作,如此等等,这些在C++里都没有增加任何运行中的时间或者空间开销”[5]
       C++设计的目的之一,是使一个平均行的C++代码能够表达出远比一个平均行的CPascal代码更多的东西。”[6]
       “一个声明了函数参数类型、使用了自定义类等机制的C++程序,通常比没有使用这些功能的等价C程序短一些;在那些使用了库的地方,C++程序就要比等价的C程序短的多。”[7]


[1] The C++ Programming Language-- Chapter 1.3, paragraph 2
[2] The C++ Programming Language-- Chapter <st1:chsdate w:st="on" isrocdate="False" year="1899" day="30" islunardate="False" month="12">2.3.1</st1:chsdate>, paragraph 2
[3] 请参考《The C++ Programming LanguageChapter <st1:chsdate w:st="on" isrocdate="False" year="1899" day="30" islunardate="False" month="12">10.2.1</st1:chsdate> 以及Chapter 9.2.3
[4] UIUser Interfaces。也有翻译作“用户界面”。但是依据我的经验,翻译为用户接口更好些,所以下文皆采用“用户接口”的翻译风格。
[5] The C++ Programming Language-- Chapter <st1:chsdate w:st="on" isrocdate="False" year="1899" day="30" islunardate="False" month="12">1.3.1</st1:chsdate>, paragraph 3。大部分都是编译器的工作—Gavin
[6] The C++ Programming Language-- Chapter <st1:chsdate w:st="on" isrocdate="False" year="1899" day="30" islunardate="False" month="12">1.3.1</st1:chsdate>, paragraph 4,根据段落大意做小部分更改。
[7] The C++ Programming Language-- Chapter <st1:chsdate w:st="on" isrocdate="False" year="1899" day="30" islunardate="False" month="12">1.3.1</st1:chsdate>, paragraph 8
分享到:
评论
1 楼 spinach 2007-04-09  
终于等到了,慢慢看。

相关推荐

Global site tag (gtag.js) - Google Analytics