zoukankan      html  css  js  c++  java
  • C++ Knowledge series STL & Const

    Thank to the pepole who devote theirself to the common libs.

    STL(http://www.cplusplus.com/reference/)

    1.  旧的C++头文件名如<iostream.h>将会继续被支持,尽管它们不在官方标准official standard中。这些头文件的内容不在名字空间std中。No _STDBEGIN (namespaces std {}and _STDEND。
    2. 新的C++头文件如<iostream>包含的基本功能和对应的旧头文件相同,但头文件的内容在名字空间std中。(在标准化的过程中,库library中有些部分的细节被修改了,所以旧头文件和新头文件中的实体不一定完全对应corresponding。)
    3. 关于标准库,需要知道的第二点是,库中的一切几乎都是模板template。看看你的老朋友iostream。(如果你和iostream不是朋友,转到条款2,看看你为什么要和它发展关系)iostream帮助你操作字符流,但什么是字符?是char吗?是wchar_t?是Unicode字符?一些其它的多字节字符?没有明显正确的答案,所以标准库standard library让你去选。所有的流类(stream class)实际上是类模板,在实例化instance流类stream class的时候指定字符类型。例如,标准库将cout类型定义为ostream,但ostream实际上是一个basic_ostream<char>类型定义(typedef )。
    4. 容器container。不要再写你自己的基本容器类!标准库提供了下列高效effective/efficient的实现:vector(就象动态可扩充的extendable数组),list(双链表),queue, stack,deque,map,set和bitset。竟然没有hash table(虽然很多制造商manufacturer/producer/vendor作为扩充提供),但多少可以作为补偿compensation的一点是, string是容器。
    5. 算法algorithm。标准容器当然好,如果存在易于使用它们的方法就更好。标准库就提供了大量简易的方法(即,预定义函数predefined function,官方称为算法(algorithm) ---- 实际上是函数模板template of function),其中的大多数适用于库中所有的容器 ---- 以及内建数组(built-in arrays).
    6. 标准库中容器和算法这部分一般称为标准模板库。STL中实际上还有第三个构件 ---- 迭代子(Iterator)迭代子是指针似的对象,它让STL算法和容器共同工作。
    7. 算法algorithm将容器的内容当作序列(sequence),每个算法可以应用于一个容器中所有值所对应的序列sequence,或者一个子序列(subsequence)。标准算法有for_each(为序列中的每个元素调用某个函数),find(在序列中查找包含某个值的第一个位置,展示了它的实现),count_if(计算序列中使得某个判定为真的所有元素的数量),equal(确定两个序列包含的元素的值是否完全相同),search(在一个序列中找出某个子序列的起始位置),copy(拷贝一个序列到另一个),unique(在序列中删除重复值),rotate(旋转序列中的值),sort(对序列中的值排序)。标准库中还包括其它很多算法。
    8. 和容器操作一样,算法也有性能保证。例如,stable_sort算法执行时要求不超过0比较级(N log N) 。它的意思实际上是,stable_sort提供的性能必须和最高效的通用排序算法在同一个级别。
    9. 使得STL具有创新意义的原因在于它实际上不是软件,而是一套规范(convention/criterion)。标准库中的STL组件只是具体体现了遵循follow/keep to这种规范所能带来的好处。You can write your own container by convention with algorithm ,container, iterator, allocator. Make them work well with other component in STL together.
    10. 避免返回内部数据的句柄 handle of inner/internal data member.
    11. const的一些强大的功能基于它在函数声明中的应用。在一个函数声明中,const可以指的是函数的返回值,或某个参数;对于成员函数,还可以指的是整个函数。
    12. 对返回值使用const有可能提高一个函数的安全性和效率,否则还会出问题。避免连续的赋值。
    13. 关于const参数没什么特别之处要强调——它们的运作和局部const对象一样。(但,见条款m19,const参数会导致一个临时对象的产生)然而,如果成员函数为const,那就是另一回事了。
    14. const成员函数的目的当然是为了指明哪个成员函数可以在const对象上被调用。但很多人忽视了这样一个事实:仅在const方面有不同的成员函数可以重载。
    15. 通过重载operator[]并给不同版本不同的返回值,就可以对const和非const string进行不同的处理:operator char *() const; return the inner pointer and it can be changed outside这个函数的缺陷pitfall在于它返回了一个"句柄"(在本例中,是个指针),而这个句柄所指向的信息本来是应该隐藏在被调用函数所在的string对象的内部。这样,这个句柄就给了调用者自由访问access所指的私有数据的机会。string::operator char*本身写的没有一点错,麻烦的是它可以用于const对象。如果这个函数不声明为const,就不会有问题,因为这样它就不能用于象b这样的const对象了。Only const member can be visited by const object. The correct implementation should be:  string::operator const char*() const.
    16. For const object, the member function should be const function and also return const object.So the class should have two same functions, one is used for non-const object and the other is used for const object. 指针并不是返回内部数据句柄的唯一途径。引用也很容易被滥用。Pointer and reference returned with inner data member.
    17. 并不是只有const成员函数需要担心返回句柄的问题,即使是非const成员函数也得承认:句柄的合法性validity失效的时间和它所对应的对象是完全相同的。这个时间可能比用户期望的要早很多,特别是当涉及的对象是由编译器产生的临时对象时temporary object。So return the object as possible as you can.
    18. 对于const成员函数来说,返回句柄是不明智unwise的,因为它会破坏数据抽象。对于非const成员函数来说,返回句柄会带来麻烦,特别是涉及到临时对象时。句柄就象指针一样,可以是悬浮(dangle)的。所以一定要象避免悬浮的指针那样,尽量避免悬浮的句柄。Dangling pointer
    19. 避免这样的成员函数:其返回值是指向成员的非const指针或引用,但成员的访问级level of access比这个函数要低. 使一个成员为private或protected的原因是想限制对它的访问.
    20. So the member data and the pointer to member function within class can’t be returned by non-const pointer or reference.
    21. typedef  void  (Class::*pointerOfMemberFunction)();//Define a pointer to a member function
    22. 至于static关键字,当它用于对成员的声明时,其含义是:整个类只有这个成员的一份拷贝only one copy的具体对象来访问。The static variable just belongs to the class not the object of class. All objects of class share it. Every access to static variable by object is transformed to access to it by class. For example:Object.static_variable or this->static_variable  Class::static_variable.
    23. 千万不要返回局部对象的引用reference of local object,也不要返回函数内部用new初始化的指针的引用. 返回一个局部对象的引用。它的问题在于,局部对象 ----- 顾名思义 ---- just as its name implies仅仅是局部的。也就是说,局部对象是在被定义时创建,在离开生命空间时被销毁的。所谓生命空间 lifetime,是指它们所在的函数体。当函数返回时,程序的控制离开leave/be off/be away了这个空间,所以函数内部所有的局部对象被自动销毁。因此,如果返回局部对象的引用,那个局部对象其实已经在函数调用者使用它之前被销毁了。
    24. 返回局部静态(static)对象的引用也会工作不正常。Because the static variable is shared by all object of class. So if the two objects are compared with static variable, they will be same all the time/forever.
    25. 尽可能地推迟defer/postpone/delay/put off变量(construct or destruct)的定义.
    26. 如果定义了一个有构造函数和析构函数的类型的变量,当程序运行到变量定义之处时,必然面临构造的开销cost/spending;当变量离开它的生命空间时,又要承担析构的开销。这意味着定义无用的变量必然伴随着不必要的开销,所以只要可能,就要避免evitable/avoidable这种情况发生。
    27. "缺省构造一个对象然后对它赋值"比"用真正想要的值来初始化这个对象"效率要低得多。1.Call constructor  2 Create a temporary object ( if necessary. In the case of the right object being not of same class). 3 Call assignment operator  4 destroy the temporary object(if necessary).
    28. Invocation of copy constructor: Class A(b); (only call copy constructor) or Class *A =new Class( b)
    29. No invocation of copy constructor: Class A = b; Class A = Class(b).
    30. 内联函数的基本思想在于将每个函数调用以它的代码体来替换replace/substitute.  expansion of code
    31. inline指令就象register,它只是对编译器的一种提示prompt,而不是命令command。也就是说,只要编译器愿意,它就可以随意地忽略ignore掉你的指令,事实上编译器常常会这么做。Inline instruction will be ignored if the function is declared with attribute of being virtual at some times. In turn, at some time there is a virtual function to generate a pointer to be put in virtual table and as well as there is a normal function.
    32. 大多数编译器拒绝内联"复杂"complex/complicated的函数(例如,包含循环cycle和递归recursion的函数);还有,即使是最简单的虚函数调用,编译器的内联处理程序对它也爱莫能助willing to help but unable to do so。(这一点也不奇怪。virtual的意思是"等到运行时再决定调用哪个函数 at run-time",inline的意思是"在编译期间将调用之处用被调函数来代替 at call-time",如果编译器甚至还不知道哪个函数将被调用,当然就不能责怪blame/scold它拒绝生成内联调用了)。以上可以归结conclude为:一个给定的内联函数是否真的被内联取决于所用的编译器的具体实现。
    33. 如果内联函数没被内联,每个调用内联函数的地方还是得承担函数调用的开销;如果是旧规则,还得忍受代码体积的增加,因为每个包含(或调用) f的被编译单元都有一份f的代码及其静态变量的拷贝!(更糟糕的是,每个f的拷贝以及每个f的静态变量的拷贝往往处于不同的虚拟内存页面,所以两个对f的不同拷贝进行调用有可能导致多个页面错误。
    34. 有时,可怜的随时准备为您效劳的编译器即使很想内联一个函数,却不得不为这个内联函数生成一个函数体。特别是,如果程序中要取一个内联函数的地址 address of inline function,编译器就必须为此生成一个函数体。The body of function must be created when the address of function is needed.
    35. 内联函数inline function中的静态对象常常表现出违反直觉violate instinct的行为。所以,如果函数中包含静态对象,通常要避免将它声明为内联函数。There could not be static object in inline function.
    36. 将文件间的编译依赖性compile dependence降至最低. 问题发生的原因在于,在将接口interface从实现implementation分离这方面,C++做得不是很出色。尤其是,C++的类定义中不仅包含接口规范,还有不少实现细节.
    37. "将一个对象的实现隐藏在指针身后"(Handle Class). Pointer of handle can lessen the dependence of compiling.  Using the pointer only is a good way of lessening the dependence of compiling, so that the declaration of class is only necessary without the detailed implementation. Loose coupling / decoupling / decompose.
    38. 如果可以使用对象的引用和指针,就要避免使用对象本身。定义某个类型的引用和指针只会涉及到这个类型的声明declaration。定义此类型的对象则需要类型定义的参与participation。Using the pointer or reference as possible as you can.
    39. 尽可能使用类的声明,而不使用类的定义。因为在声明一个函数时,如果用到某个类,是绝对不需要这个类的定义的,即使函数是通过传值来传递和返回这个类.
    40. At the beginning of C++ file, it is the best way to declare class and use pointer of class in the following code. You can use pointer of class without definition of class, but if you use object of class, you must have definition of class and take cost in compiling time.
    41. 不要在头文件中再(通过#include指令)包含其它头文件,除非缺少了它们就不能编译。相反on the contrary/contrarily/by contraries,要一个一个地声明所需要的类,让使用这个头文件的用户自己(通过#include指令)去包含其它的头文件,以使用户代码最终得以通过编译。一些用户会抱怨complain这样做对他们来说很不方便inconvenient,但实际上你为他们避免了许多你曾饱受的痛苦。事实上,这种技术很受推崇,并被运用apply to到C++标准库(参见条款49)中;头文件<iosfwd>就包含了iostream库中的类型声明(而且仅仅是类型声明).
    42. Just include the declaration of class, instead of definition of class in header file or at beginning of source file.


    Separate interface from implementation

    1. Person类仅仅用一个指针来指向某个不确定的实现,这样的类常常被称为句炳类(Handle class)或信封类(Envelope class).
      • Declaration of class 
      • Define a pointer of class对于它们所指向的类来说,前一种情况下对应的叫法是主体类(Body class);后一种情况下则叫信件类(Letter class)。它只是把所有的函数调用都转移到了对应的主体类(pointer of object)中,主体类真正完成工作。AutoPointer/SmartPointer/
    2. 除了句柄类handle class,另一选择是使Person成为一种特殊类型的抽象基类,称为协议类(Protocol class)(called as interface in Java)。根据定义,协议类没有实现;它存在的目的是为派生类确定certain/explicit一个接口(参见条款36)。所以,它一般没有数据成员,没有构造函数;有一个虚析构函数,还有一套set纯虚函数,用于制定接口。The protocol class which is identical to the interface in Java consists of a set of pure virtual function.
    3. Person类的用户必须通过Person的指针和引用来使用它,因为实例化一个包含纯虚函数的类是不可能的(但是,可以实例化Person的派生类----参见下文)。和句柄类的用户一样,协议类的用户只是在类的接口被修改的情况下才需要重新编译recompile. It is impossible to initialize/create a instance with a abstract class.
    4. 协议类protocol class的用户必然要有什么办法来创建新对象。这常常通过调用一个函数来实现,此函数扮演构造函数的角色,而这个构造函数所在的类即那个真正被实例化的隐藏在后的派生类。这种函数叫法挺多(如工厂函数(factory function),虚构造函数(virtual constructor)),但行为却一样:返回一个指针return a pointer,此指针指向支持协议类接口(见条款M25)的动态分配对象。Interface-based programming.
    5. 句柄类和协议类分离separate/apart/part/divide/sever/segregate了接口和实现,从而降低了文件间编译的依赖性。
    6. 句柄类的情况下,成员函数必须通过(指向实现的 pointer to implementation)指针来获得对象数据。这样,每次访问的间接性indirection就多一层。此外,计算每个对象所占用的内存大小时,还应该算上这个指针。还有,指针本身还要被初始化initialization(在句柄类的构造函数内),以使之指向被动态分配的实现对象,所以,还要承担动态内存分配(以及后续succeeding的内存释放)所带来的开销.
    7. 对于协议类(pure abstract class),每个函数都是虚函数,所有每次调用函数时必须承担间接跳转indirect jump的开销pointer to vbtl。而且,每个从协议类派生而来的对象必然包含一个虚指针。这个指针可能会增加对象存储所需要的内存数量(具体取决于lie on/rest with/due to:对于对象的虚函数来说,此协议类是不是它们的唯一来源)
    8. 最后一点,句柄类handle和协议protocol类都不大会使用内联函数。使用任何内联函数时都要访问access实现细节detail of implementation,而设计句柄类和协议类的初衷original intention正是为了避免这种情况。
    9. 类的用户接口是指使用这个类的程序员所能访问得到的接口。典型的接口里只有函数存在,因为在用户接口里放上数据成员会有很多缺点defect/pitfall.
    10. 一方面,设计出来的类要易于理解understandable,易于使用usable,易于实现implement。这意味着函数的数量要尽可能地少,每一个函数都完成各自不同的任务。另一方面,类的功能要强大,要方便使用,这意味着要不时增加函数以提供对各种通用功能的支持support。CRC = class-responsibility-collaboration
    11. 类接口的目标是完整且最小。一个完整的接口是指那种允许用户做他们想做的任何合理的事情的接口。也就是说,对用户想完成的任何合理的任务,都有一个合理的reasonable方法去实现,即使这个方法对用户来说没有所想象的那样方便。相反,一个最小的minimum接口,是指那种函数尽可能少、每两个函数都没有重叠功能的接口。如果能提供一个完整completed、最小minimal的接口,用户就可以做任何他们想做的事,但类的接口不必再那样复杂complex.
    12. 另外,不要忘记友元函数friend function在所有实际practical/pragmatic应用中都是类的接口的一部分。这意味着友元函数影响着类的接口的完整性integrality和最小性。
    13. 成员函数member function和非成员函数non-member function最大的区别difference/distinguish在于成员函数可以是虚拟的virtual而非成员函数non-virtual不行。所以,如果有个函数必须进行动态绑定dynamic binding,就要采用虚拟函数,而虚拟函数必定是某个类的成员函数。
    14. explicit构造函数不能用于隐式转换implicit conversion,这正是explicit的含义. That means that this method must be explicitly called without help of compiler or it can't be called by compiler, must be called by user code.
    15. 实际上actually/virtually,如果需要的话,编译器会对每个函数的每个参数argument执行这种隐式类型转换。但它只对函数参数表中列出的参数进行转换,决不会对成员函数所在的对象(即,成员函数中的*this指针所对应的对象)进行转换。
    16. 只要能避免使用友元函数就要避免avoidance,因为,和现实生活中差不多,友元(朋友)带来的麻烦往往比它(他/她)对你的帮助多。
    17. 虚函数必须是成员函数。如果f必须是虚函数,就让它成为c的成员函数。
    18. operator>>和operator<<决不能是成员函数。如果f是operator>>或operator<<,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数。Friend class.
    19. 只有非成员函数对最左边的most-left参数进行类型转换。如果f需要对最左边的参数进行类型转换type conversion,让f成为非成员函数。如果f还需要访问c的非公有成员,让f成为c的友元函数。
    20. 其它情况下都声明为成员函数。如果以上情况都不是,让f成为c的成员函数.
    21. 避免public接口出现数据成员.
    22. 首先,从“一致性”consistency/conformability的角度来看这个问题。如果public接口里都是函数,用户每次访问类的成员时就用不着抓脑袋去想:是该用括号还是不该用括号bracket呢?——用括号就是了!因为每个成员都是函数。
    23. 如果使数据成员为public,每个人都可以对它读写;如果用函数来获取或设定它的值,就可以实现禁止访问、只读访问和读写访问等多种控制。
    24. 功能分离(functional abstraction)。如果用函数来实现对数据成员的访问access,以后就有可能用一段计算calculation来取代replace/substitute这个数据成员,而使用这个类的用户却一无所知utterly/completely/absolutely/entirely ignorant。
    25. Design Pattern: Abstract Factory, Builder, Adaptor, Proxy, Composite, State, Template Method, Strategy, Bridge, Observer, Visitor, Decorator, Iterator, Command,
    26. Add  additional indirection by defining a reference or pointer to other interface in the abstract class interacting with user. Introduce additional extra level of indirection to offer interface-based programming.


    Const

    1. 尽可能使用const. 使用const的好处在于它允许指定一种语意上semantic的约束——某种对象不能被修改——编译器具体来实施这种约束constrain/restriction。通过const,你可以通知编译器和其他程序员某个值要保持不变invariable。只要是这种情况,你就要明确地使用const ,因为这样做就可以借助编译器的帮助确保这种约束不被破坏.  Abstract class / Concrete class.  Const对象只能调用const 函数,const 函数中不能修改任何数据成员.
    2. const关键字实在是神通广大。在类的外面,它可以用于全局或名字空间常量,以及静态对象(某一文件或程序块范围内的局部对象)。在类的内部,它可以用于静态和非静态成员(见条款12).
    3. 一般来说,你可以在头脑里画一条垂直线穿过指针声明中的星号(*)位置,如果const出现在线的左边,指针指向的数据为常量;如果const出现在线的右边,指针本身为常量;如果const在线的两边都出现,二者都是常量。Const class *pointer == class const *pointer.
    4. Class const * const pointer / const class * const pointer which indicate that pointer is const and the object pointer pointed to also const.
    5. const的一些强大的功能基于它在函数声明中的应用。在一个函数声明中,const可以指的是函数的返回值 return value,或某个参数argument;对于成员函数member function,还可以指的是整个函数. const  return_type  class::function(const class &A) const;
    6. 关于const参数没什么特别之处要强调——它们的运作和局部const对象一样。(但,见条款m19,const参数会导致一个临时对象的产生)然而,如果成员函数为const,那就是另一回事了.
    7. const成员函数的目的当然是为了指明哪个成员函数可以在const对象上被调用。但很多人忽视了这样一个事实fact/reality/truth:仅在const方面有不同的成员函数可以重载。这是c++的一个重要特性。Non-const member function is used for non-const object, const member function is used for const object. 
      • char& operator[]( int position); 
      • const char & operator[](int position) const.
    8. 让函数返回一个常量值经常可以在不降低安全性和效率的情况下减少用户出错的几率odds/probability。
    9. 有两种主要的看法:数据意义上的const(bitwise constness)和概念意义上的const(conceptual constness)bitwise constness的坚持者认为,当且仅当成员函数不修改对象的任何数据成员(静态数据成员除外)时,即不修改对象中任何一个比特(bit)时,这个成员函数才是const的。bitwise constness最大的好处是可以很容易地检测到违反bitwise constness规定的事件:编译器只用去寻找有无对数据成员的赋值就可以了。实际上,bitwise constness正是c++对const问题的定义,const成员函数不被允许修改它所在对象的任何一个数据成员.
    10. 利用c++标准组织针对这类情况专门提供的有关const问题的另一个可选optional方案。此方案使用了关键字mutable,当对非静态数据成员运用mutable时,这些成员的“bitwise constness”限制constraint就被解除.
    11. 如果想使那个有问题的string::length版本对const和非const对象都合法,就只有把this的类型从const c * const改成c * const。不能直接这么做,但可以通过初始化一个局部变量指针 pointer to local variable,使之指向this所指的同一个对象来间接indirect实现。然后,就可以通过这个局部指针来访问你想修改的成员string * const localthis  =   const_cast<string * const>(this);
  • 相关阅读:
    poj1068
    make&&gcc/g++ 生成 map file
    游戏系统开发笔记(九)——构建战斗系统
    归并排序
    士兵杀敌(四)
    ftp报错 200 port command successful. consider using pasv 425 failed to establish connection
    HDU 4649 Professor Tian
    如何将ASM中的数据文件复制到操作系统中
    Struts2 学习笔记 10 Result部分 part1
    交通管理系统
  • 原文地址:https://www.cnblogs.com/iiiDragon/p/3254511.html
Copyright © 2011-2022 走看看