• C++ Knowledge series Template & Class


    Function

    1. Function is composed of name, parameter (operand, type of operand), return value, body with another adornment like: inline, virtual, static, const, throw().
    2. 我们必须在调用函数之前,就声明该函数否则会引起编译错误.
    3. 函数声明由函数返回类型,函数名和参数表构成. 这三个元素被称为函数声明function declaration 或函数原型function prototype, 一个函数可在一个文件中被声明多次. 函数生命不需要指定参数的名字,只需要每个参数的类型。
    4. 函数原型由函数返回类型,函数名,以及参数表构成。函数原型描述的是函数的接口,它详细描述了调用函数时需要提供的参数的类型和个数以及函数返回值的类型。
    5. 数组不能作返回类型。
    6. 函数必须指定一个返回值,没有显式返回值的声明或者定义将引起编译错。C++是一种强类型strong typed 语言,每个函数调用的实参在编译期间都要经过类型检查type-checked, 若实参类型与相应的参数类型不匹配,如果有可能,就会应用一个隐式的类型转换。如果不可能进行隐式转换,或者实参的个数不正确,就会产生一个编译错误,这就是函数必须先被声明才能被使用的原因。编译器必须根据函数参数表对函数凋用的实参执行类型检查,就此而言,声明是必不可少的
    7. 所有的函数都使用在程序运行栈run-time stack 中分配的存储区,该存储区一直保持与该函数相关联,直到函数结束为止,那时存储区将自动释放以便重新使用,该函数的整个存储区被称为活动记录activation record。
    8. C++中参数传递的缺省初始化方法是把实参的值拷贝到参数的存储区中,这被称为按值传递pass-by-value。返回值也是在存储区中,函数只是操作存在存储区中参数,把结果放在存储区中返回值里。
    9. 按值传递时,函数不会访问当前调用的实参。函数处理的值是它本地的拷贝,这些拷贝被存储在运行栈中,因此改变这些值不会影响实参的值。一旦函数结束了,函数的活动记录将从栈中弹出,这些局部值也就消失了。
    10. 当大型的类对象必须作为参数传递时,对实际的应用程序而言分配对象并拷贝到栈中的时间和空间开销往往过大。
    11. 当实参的值必须被修改时,必须 pass-by-reference or pointer.
    12. 把参数声明成引用,实际上改变了缺省的按值传递参数的传递机制。在按值传递时函数操纵的是实参的本地拷贝,当参数是引用时函数接收的是实参的左值而不是值的拷贝,这意味着,函数知道实参在内存中的位置因而能够改变它的值或取它的地址。
    13. 有人可能希望用引用参数以避免拷贝用作实参的大型类对象,同时又希望防止函数修改实参的值,如果引用参数不希望在被调用的函数内部被修改,那么把参数声明为const 型的引用是个不错的办法,这种方式能够使编译器防止无意的改变。
    14. void foo( const X&) and void foo( X ): overloading void foo( X&) ??????
    15. int *&vi; 应该从右向左读: v1 是一个引用,它引用一个指针,指针指向int 型的对象。Int * p = &Dragon; vi = p; vi is a reference to p which is pointer to integer variable ‘Dragon”.
    16. 引用必须被初始化为指向一个对象,一旦初始化了它就不能再指向其他对象。指针可以指向一系列不同的对象,也可以什么都不指向。
    17. 因为指针可能指向一个对象或没有任何对象,所以函数在确定指针实际指向一个有效的对象之前不能安全地解引用dereference 一个指针,must checking whether the pointer is NULL or not.
    18. 如果一个参数可能在函数中指向不同的对象或者这个参数可能不指向任何对象,则必须使用指针参数。
    19. 在C++中数组永远不会按值传递。
    20. The following declaration is completely same: void Dragon( int *p); void Dragon(int[]); void Dragon(int[100]);
    21. 另外一种机制是将参数声明为数组的引用,当参数是一个数组类型的引用时,数组长度成为参数和实参类型的一部分,编译器检查数组实参的长度与在函数参数类型中指定的长度是否匹配。void putValues( int (&arr)[10] );
    22. 调用包含缺省实参的函数时,我们可以也可以不为该参数提供实参,如果提供了实参则它将覆盖缺省的实参值。
    23. 函数调用的实参,按位置解析缺省实参只能用来替换函数调用缺少的尾部tailing 实参。
    24. 函数声明可以为全部或部分参数指定缺省实参,在左边参数的任何缺省实参被提供之前,最右边未初始化参数必须被提供缺省实参,这是由于函数调用的实参是按照位置来解析的。
    25. 有时候我们无法列出传递给函数的所有实参的类型和数目,在这种情况下我们可以用省略号...ellipsis 指定函数参数表。
    26. 如果被返回的值的类型与函数返回类型不匹配,那么如果可能的话将应用隐式类型转换如果无法隐式转换则产生一个编译错误。
    27. 如果返回值是一个大型类对象用引用或指针,返回类型比按值返回类对象效率要高得多,在某些情况下,编译器自动将按值返回转换到按引用返回,该优化被称为命名返回值优化named return value optimization。(NRV optimization)
    28. 返回一个指向局部对象的引用,局部对象的生命期随函数的结束而结束,这个是错误的做法。
    29. 函数返回一个左值对返回值的任何修改都将改变被返回的实际对象,这个是错误的做法。
    30. 为防止对引用返回值的无意修改返回值应该被声明为const。return a const reference to non-local variant.
    31. 一个程序中的各种函数,可以通过两种机制进行通信这里的通信,communicate 指的是值的交换。一种方法是使用全局对象,一种方法是使用函数参数表和返回值。
    32. 向一个函数传递参数发生错误的可能性随参数表的长度的增加而提高,作为一个通用规则,8 个参数应该是最大值了,为了替换一个大型的参数表程序员可以将参数声明为类数组或某一种容器类型,这样的参数可以用来包含一组参数值。
    33. 直接或间接调用自己的函数被称为递归函数recursive function
    34. 递归函数必须定义一个停止条件stopping condition, 否则,函数会永远递归下去。有时候这被称作无限递归infinit recursion 错误。
    35. 注意inline 指示对编译器来说只是一个建议,编译器可以选择忽略该建议,因为把一个函数声明为inline 函数并不见得真的适合在调用点上展开。
    36. int main( int argc, char *argv[] ) { ... }。 argv[0]总是被设置为当前正被调用的命令从索引1 到argc-1 表示被传递给命令的实际选项。
    37. int (*pf)( const string &, const string & ); // ok: 正确, pf is a pointer to function
    38. int * pf ( const string &, const string & ); // ok: 正确, pf is a function name, return a pointer with int type.

    Class

    1. 数据成员的声明看起来很像在块域或名字空间域中的变量声明, 但是除了静态static 数据成员外,数据成员不能在类体中被显式地初始化.
    2.  类的成员函数被声明在类体中成员函数的声明看起来像是名字空间域中所出现的函数声明.
    3. 如果没有指定访问限定符则缺省情况下, 在类体的开始左括号后面的区是private 区.
    4. 在某些情况下允许某个函数而不是整个程序可以访问类的私有成员, 这样做会比较方便. 友元friend 机制允许一个类授权其他的函数访问它的非公有成员.
    5. 一个友元或许是一个名字空间函数, 另一个前面定义的类的一个成员函数, 也可能是一个完整的类. 在使一个类成为友元时, 友元类的所有成员函数都被给予访问授权友谊的类的非公有成员的权.
    6. 我们可以声明指向该类类型的指针或引用。允许指针和引用是因为它们都有固定的大小,这与它们指向的对象的大小无关。但是,因为该类的大小和类成员都是未知的,所以要等到完全定义了该类我们才能将解引用操作符* 应用在这样的指针上,或者使用指针或引用来指向某一个类成员。
    7. 因为只有当一个类的类体已经完整时,它才被视为已经被定义。所以一个类不能有自身类型的数据成员,但是当一个类的类头被看到时,它就被视为已经被声明了。所以一个类可以用指向自身类型的指针或引用作为数据成员。Class Dragon { Dragon *pNext; }
    8. 类的定义如类Screen 不会引起存储区分配, 只有当定义一个类的对象时系统才会分配存储区.
    9. Class and Objects ( state, behavior, identify)
    10. 我们也可以声明类对象的指针和引用。类类型的指针可以用同一类类型的类对象的地址做初始化或赋值,类似地类类型的引用也可以用同一类类型的对象的左值作初始化。面向对象的程序设计对此作了扩展,允许基类的引用或指针引用到派生类的对象。
    11. 这些函数被称为在类定义中定义的内联inline 函数,这些函数被自动作为inline 函数处理。
    12. 成员函数必须先在其类体内被声明而且类体必须在成员函数被定义之前先出现。
    13. 通常在类体外定义的成员函数不是inline 的,但是这样的函数也可以被声明为inline函数,可以通过显式地在类体中出现的函数声明上使用关键字inline, 或者通过在类体外出现的函数定义上显式使用关键字inline 或者两者都用。
    14. In member function of class, if type of parameter is the same class or base class, parameter’s private or protected data member can be visited directly in member function, else it is not allowed. 
    15. Void Dragon::memberfunction( const Dragon & ll); data member of Dragon can be visited directly through ll.
    16. Void Dragon::memberfunction( const Marcia & ll); data member of Marcia can not be visited directly through ll, except Marcia is base class of Dragon;
    17. 有一组特殊的成员函数可以管理类对象, 并处理诸如初始化, 赋值, 内存管理, 类型转换, 以及析构,等活动, 这些函数通常由编译器隐式调用。
    18. 类的设计者通过把成员函数声明为const 以表明它们不修改类对象。
    19. 只有被声明为const 的成员函数才能被一个const 类对象调用。关键字const 被放在成员函数的参数表和函数体之间,对于在类体之外定义的const 成员函数,我们必须在它的定义和声明中同时指定关键字const。
    20. const 成员函数可以被相同参数表的非const 成员函数重载, only for reference or pointer。
    21. const member function can only be operated on const object or non-object. Non-const member function can only be operated on non-const object.
    22. 构造函数和析构函数是两个例外( they are const and static and non-const, volatile),即使构造函数和析构函数不是const 成员函数,const类对象也可以调用它们,当构造函数执行结束,类对象已经被初始化时,类对象的常量性就被建立起来了,析构函数一被调用,常量性就消失,所以一个const 类对象从构造完成时刻到析构开始时刻这段时间内被认为是const。
    23. 与const 类对象类似,对于一个volatile 类对象只有volatile成员函数构造函数和析构函数可以被调用。
    24. 为了允许修改一个类的数据成员,即使它是一个const 对象的数据成员,我们也可以把该数据成员声明为mutable 易变的,mutable 数据成员永远不会是const 成员,即使它是一个const 对象的数据成员,mutable 成员总可以被更新即使是在一个const 成员函数中。
    25. 每个类成员函数都含有一个指向被调用对象的指针这个指针被称为this. 在非const成员函数中它的类型是指向该类类型的指针 class * const this, 在const 成员函数中是指向const 类类型的指针const class * const this.
    26. ClassType * const this, const ClassType * const this
    27. 静态成员函数没有this 指针因此在静态成员函数中隐式或显式地引用这个指针都将导致编译时刻错误试图访问隐式引用this 指针的非静态数据成员也会导致编译时刻错误.So the non-static member of class can not be visited in static method of class.
    28. The pointer to member variable of class: 1 参数的类型和个数; 2 返回类型; 3 它所属的类类型. short Screen::*ps_Screen = &Screen::_height;
    29. The pointer to member method of class: int (Screen::*pmf2)( int, long) = &Screen::height; Three elements must be matched totally, return type, the type of member function pointer to, parameter
    30. 指向成员函数类型的指针可以被用来声明函数参数和函数返回类型, 我们也可以为成员函数类型的参数指定缺省实参.
    31. typedef Screen& (Screen::*Action)(int, string); Screen& action( Screen&, Action );
    32. 指向静态类成员的指针的声明看起来与非类成员的指针相同, 解引用该指针不需要类对象. Class instance. 
    33. double (*pf)() = &Account::interest     //( static method) ;
    34. double *pd = &Account::_interestRate // ( static variable); 
    35. union 不能有静态数据成员或是引用成员,如果一个类类型定义了构造函数,析构函数或拷贝赋值操作符,则它不能成为union 的成员类型。
    36. 类成员在类体中被声明的顺序很重要,在类体中后声明的成员不能被先声明的成员声明使用(because of order of initialization)。
    37. 静态数据成员被当作该类类型的全局对象, 对于非静态数据成员, 每个类对象都有自己的拷贝, 而静态数据成员对每个类类型只有一个拷贝, 静态数据成员只有一份由该类类型的所有对象共享访问. (take into account the way of how static member of class is initialized only once when it is used first time).
    38. Access to static member or private, protected public member is ensured by compiler at compiling-time.
    39. 静态数据成员没有进入程序的全局名字空间, 因此不存在与程序中其他全局名字冲突的可能性.
    40. 可以实现信息隐藏静态成员可以是private 成员而全局对象不能.
    41. 静态数据成员在该类定义之外被初始化. Normally at the beginning of cpp file, and allocated in static data segment.
    42. 静态数据成员可以被作为类成员函数的缺省实参, 而非static 成员不能.
    43. 静态成员函数的声明除了在类体中的函数声明前加上关键字static, 以及不能声明为const 或volatile 之外, 与非静态成员函数相同出现在类体外的函数定义不能指定关键字static.
    44. 静态成员函数没有this 指针因此在静态成员函数中隐式或显式地引用这个指针都将导致编译时刻错误.
    45. 指向成员函数的指针, 必须与向其赋值的函数类型匹配, 不是两个而是三个方面都要匹配: 1 参数的类型和个数; 2 返回类型; 3 它所属的类类型.
    46. Use a pointer to member of class together with this pointer to object.
    47. 类成员的指针必须总是通过特定的对象或指向该类类型的对象的指针来访问.
    48. Dragon& (Dragon::* pmf) ( const Dragon&, int, string ) = &Dragon::copy;(oDragon.*pmf)( oDragon,88,"This is right."); //use pointer to member of class.
    49. myScreen.*pmfi() is transformed into myScreen.*(pmfi()).
    50. 指向类成员的指针语法, 不能被用来引用类的静态成员静态类成员, 是属于该类的全局对象和函数, 它们的指针是普通指针, 请记住静态成员函数没有this 指针.
    51. 指向静态类成员的指针的声明看起来与非类成员的指针相同解引用该指针不需要类对象.
    52. double * lDragon = &Dragon::m_lDragon; // static double m_lDragon in class
    53. double (*pf) ( ) = &Dragon::GetDragon(); //static double Dragon::GetDragon() in class.
    54. union 不能有静态数据成员, 或是引用成员, 如果一个类类型定义了构造函数, 析构函数或拷贝赋值操作符则它不能成为union 的成员类.
    55. 被用在类成员函数定义中的名字的解析过程如下:
      • 1. 在成员函数局部域中的声明首先被考虑, 包括参数;
      • 2. 如果在步骤1 中的解析不成功, 则考虑所有的类成员声明;
      • 3. 则考虑在成员函数定义之前的名字空间域中出现的声明

    Template

    1. template <class T> class Queue {….};
    2. 类模板的定义和声明都以关键字template 开头, 关键字后面是一个用逗号分隔的模板参数表, 用尖括号<> 括起来, 这个表被称为类模板的模板参数表template parameter list, 它不能为空模板参数,可以是一个类型参数, 也可以是一个非类型参数, 如果是非类型参数则代表一个常量表达式.
    3. 模板的类型参数type parameter 由关键字class 或关键字typename 及其后的标识符构成, 在模板参数表中关键字class 和typename 的意义相同. 
    4. The parameter after keywords class or typename stands for a user-defined type or built-in type.(value type or reference type)
    5. 每个模板类型参数的前面都必须有关键宇class 或typename.
    6. 模板非类型参数nontype parameter 由一个普通参数声明构成, 一个非类型参数指示该参数代表了一个潜在的值, 而这个值又代表类模板定义中的一个常量.
    7. 类型替换的过程被称为模板实例化template instantiation == substitution of type.
    8. 模板参数的名字在它被声明为模板参数后一直到模板声明或定义的结束都可以被使用, 如果在全局域中声明了与模板参数同名的变量, 则该变量被隐藏掉.
    9. Local stuff hides global stuff for variant, function, type definition, namespace and so on.
    10. 模板参数名不能被用作在类模板定义中声明的类成员的名字.
    11. 模板参数的名字在模板参数表中只能被引入一次. Template < class T, typename T> ( wrong).
    12. 在不同的类模板声明或定义之间模板参数的名字可以被重复使用.
    13. 在类模板的前向声明和类模板定义中模板参数的名字可以不同.
    14. 类模板的参数可以有缺省实参这对类型参数和非类型参数都一样.
    15. 类模板的后续声明可以为模板参数提供附加的缺省实参, 如函数参数的缺省实参的情形一样, 在向左边的参数提供缺省实参之前, 必须首先给最右边未初始化的参数提供缺省实参.
    16. 当QueueItem 在其他模板定义中被用作一个类型指示符时, 我们必须指定完整的模板参数表. QueueItem<Type> *pqi = &qi;
    17. 从通用的类模板定义中生成类的过程被称为模板实例化template instantiation, 当一个针对int 型对象的Queue 类被实例化时类模板定义中每次出现的模板参数Type 都被int取代.
    18. 声明一个类模板实例的指针和引用不会引起类模板被实例.
    19. 定义一个类类型的对象时需要该类的定义,but defining a pointer of class type is OK without definition of class.
    20. 如果一个指针或引用指向一个类模板实例, 那么只有当检查这个指针或引用所指的那个对象时类模板才会被实例化.
    21. 如果模板实参是一个具有构造函数的类例如string, 它将导致item 被初始化两次, 在QueueItem 的构造函数体执行之前string 的缺省构造函数被调用来初始化item, 然后, 新构造的item 又被按成员赋值在QueueItem 构造函数的定义中我们只需在构造函数成员初始化表initialization list中显式地初始化item 就可以解决这个问题.
    22. 类模板参数也可以是一个非类型模板参数.绑定给非类型参数的表达式必须是一个常量表达式,即它必须能在编译时被计算出结果.
    23. 非const 对象的值不是一个常量表达式, 它不能被用作非类型模板参数的实参. 但是名字空间域中任何对象的地址即使该对象不是const 类型是一个常量表达式, 而局部对象的地址则不是, 因此名字空间域的对象的地址可以被用作非类型模板参数的实参, 类似地sizeof 表达式的结果是一个常量表达式所以它可以被用作非类型模板参数的实参.
    24. 对于一个模板非类型参数, 如果两个不同的表达式的求值结果相同, 则它们被认为是等价的模板实参.
    25. 在模板实参的类型和非类型模板参数的类型之间允许进行一些转换, 能被允许的转换集是函数实参上被允许的转换的子集:
      • 1. 左值转换包括从左值到右值的转换, 从数组到指针的转换, 以及从函数到指针的转换;
      • 2. 限定修饰转换, int * to const int *;
      • 3. 提升 promotion;
      • 4. 整值转换; 把整型0 转换成指针值的转换是不允许的.
    26. 与非模板类一样类, 模板的成员函数也可以在类模板的定义中定义, 在这种情况下, 该成员函数是inline 成员函数或者成员函数, 也可以被定义在类模板定义之外.
    27. 被定义在类模板定义之外的成员函数必须使用特殊的语法来指明它是一个类模板的成员成员函数, 定义的前面必须加上关键字template 以及模板参数.
    28. template < class T> inline Queue<T>::getLength() const{ …} ;
    29. 类模板的成员函数本身也是一个模板, 标准C++要求这样的成员函数只有在被调用或者取地址时才被实例化.
    30. 当类模板被实例化时, 类模板的成员函数并不自动被实例化, 只有当一个成员函数被程序用到.函数调用或取地址时, 它才被实例化, 类模板的成员函数被实例化的时间会影响到在类模板成员函数定义中名字的解析.
    31. 有三种友元声明可以出现在类模板中:
      • 1. 非模板友元类或友元函数;
      • 2. 绑定的bound 友元类模板或函数模板;
      • 3. 非绑定的unbound 友元类模板或函数模板; 
    32. 类模板也可以声明静态数据成员类模板的每个实例都有自己的一组静态数据成员.
    33. 操作符new()和delete()被声明为private, 以此来防止普通的程序在自由存储区heap中创建QueueItem 类型的对象,只有QueueItem 的成员和友元比如模板Queue 才能够在自由存储区中创建和删除QueueItem 类型的对象.
    34. 静态数据成员的模板定义必须出现在类模板定义之外, 因此模板定义以关键字template开始后面是类模板参数表<class T>,静态数据成员的名字前需要加上前缀QueueItem<T>::表明该成员属于类模板QueueItem。
      • template<class T>
      • inline ReturnType<T>&  Queue<T>::Method(…);
      • template<class T, class B>
      • B& TemplateGlobalMethod(T,T){........};
    35. How to use: B& object = TemplateGlobalMethod<T,B>(aObject, bObject);
    36. 1 template< class TofReturn,class _Diff2,class _Ty> 
      2 inline TofReturn  search_n(TofReturn _First1, TofReturn _Last1, _Diff2 _Count, const _Ty& _Val)
      3 {
      4       .....
      5 }
      6 Using: search_n<T,C,B>(d, d, d, d);

      template <class T> const int Queue<T>:: iStaticDragon = 100;

    37. Tick:Let one class only used by another class , not all class :

      • 1. Make its constructor private, and declare another class is friend;

      • 2. Make it as another class’s private nested class.

      • 3. Make its constructor protected, and another class is derived from this class.

    38. 因为Queue 要求每个被实例化的类型都有相关的QueueItem 类, 所以嵌套的类也是一个类模板, 类模板的嵌套类自动成为一个类模板, 在嵌套类模板内部可以使用外围类模板的模板参数.

    39. 函数或类模板可以是一个普通类的成员, 也可以是一个类模板的成员, 成员模板的定义看起来像一般模板的定义成员定义前面加上关键字template 及模板参数表.

    40. 在类模板Queue 中声明一个成员模板, 意味着Queue 的一个实例包含了可能无限多个联套类CL 和可能无限多个成员函数assign().

    41. 类模板定义只是无限多个类类型的定义的规范描述prescription而已, 模板定义本身并没有定义任何一个类类型.

    42. 只有当编译器看到了实际的类模板定义, 而不仅仅只是声明时, 它才能实例化类模板, 当程序使用一个类模板并且要求其实例时, 程序必须首先提供类模板的定义.

    43. 类模板的成员的定义被用来为每个特定类的模板实例生成成员实例.

    44. 类模板也可以声明静态数据成员, 类模板的每个实例都有自己的一组静态数据成员.

    45. 类模板中的名字解析: 1.在模板被定义时,解析出不依赖于模板参数的名字; 2. 在模板被实力化的时候,解析出倚赖于模板参数的名字.

    46. 作为类模板设计者,我们想尽可能控制模板定义中的名字解析过程,如果类模板是一个库的一部分,并且这个库还定义了其他的模板和函数,那么我们希望让类模板的实例及其成员尽可能使用库中的其他组件,名字解析过程的第一步可以保证这个要求,当模板定义中用到的名字不依赖于模板参数时,名字解析过程只考虑在模板定义之前头文件中可见的声明。

    47. 类模板的设计者,必须确保为所有用在模板定义中且不依赖于模板参数的名字提供声明,如果在模板定义中用到的名字,不依赖于模板参数,该名字的声明在模板被定义时未能找到则模板定义是错误的。

    48. 类模板的实例化点,总是在名字空间域中,而且它总是在引用类模板实例的声明或定义之前,类模板的成员函数或静态数据成员的实例化点,也总是跟在引用类模板成员实例的声明或定义之后。

    49. 当类模板被实例化时,类模板实例的成员不会自动被实例化,只有当程序用到这些成员时它们才被实例化。

    50. 对于这样的类模板定义,其意义与在全局域中定义的类模板相同,只不过模板名被隐藏在名字空间中,当在名字空间之外使用模板时,模板名字必须被名字空间名限定修饰或者提供一个using 声明。

    51. 类模板或类模板成员的特化声明必须被声明在定义通用模板的名字空间中。

    52. You can define the special template outside namespace where it is declared.

    53. template<> class cplusplus_primer::Queue<char*> { … } //specialization of template for special type.

    54. 数组作为参数,禁止数组退化为指针:
      1 template<class T, int size>
      2 void OperationOnArray(T (&arry)[size])
      3 {
      4       ..........
      5 }

    Nested class & Local class

    1. Nested class : one class can be defined within other class.
    2. Nested class is a member of outer class. Nested class have private or public access.
    3. 嵌套类的名字在其外围类域中是可见的但是在其他类域或名字空间中是不可见的.
    4. 与非嵌套类一样, 嵌套类可以有与自身同样类型的成员.
    5. 私有成员是指这样的成员, 它只能在该类的成员或友元定义中被访问,除非外围类被声明为嵌套类的友元,否则它没有权利访问嵌套类的私有成员.
    6. Two ways: 1. define nested class as public, the member of nested class is public, the outer class is a friend of nested class; 2. define nested class as private, the member of nested class is private;
    7. 用外围类名限定修饰嵌套类名: nested class’s constructor: List::ListItem::ListItem( int val ){ … };
    8. 如果ListItem 已经声明了一个静态成员, 那么它的定义也要放在全局域中.
    9. 嵌套类也可以被定义在其外围类之外。class List::ListItem {。。。} and declaration of nested class must appear in enclosing class.
    10. 在嵌套类的定义被看到之前, 我们只能声明嵌套类的指针和引用,即使类ListItem 是在全局域中被定义的.
    11. 为什么会希望在类定义之外定义嵌套类呢, 或许嵌套类支持外围类的实现细节我们不想让List 类的用户看到ListItem 的细节, 因此我们不愿把嵌套类的定义放在含有List 类接口的头文件中, 于是我们只能在含有List 类及其成员实现的文本文件中给出嵌套类ListItem的定义.
    12. 嵌套类可以先被声明, 然后再在外围类体中被定义, 这允许多个嵌套类具有互相引用的成员.
    13. 嵌套类不能直接访问其外围类的非静态成员, 即使这些成员是公有的, 任何对外围类的非静态成员的访问都要求通过外围类的指针引用或对象来完成. Note this pointer.
    14. 使用类的非静态成员时, 编译器必须能够识别出非静态成员属于哪个对象.
    15. 尽管访问外围类的非静态数据成员需要通过对象指针或引用才能完成, 但是嵌套类可以直接访问外围类的静态成员类型,名枚举值假定这些成员是公有的.
    16. Name resolution(just consideration before the appearance point of using ): from inside to outside; from local to global; from near to far; 考虑出现在名字使用点之前的嵌套类的成员声明.
    17. 在外围域List 之外定义嵌套类ListItem , 则List 类的所有成员都已经被声明完毕因而编译器将考虑其所有声明.
    18. 被用在嵌套类的成员函数定义中的名字其解析过程如下:
      • 1. 首先考虑在成员函数局部域中的声明;
      • 2. 如果第1 步没有成功则考虑所有嵌套类成员的声明;
      • 3. 如果第2 步没有成功则考虑所有外围类成员的声明;
      • 4. 如果第3 步没有成功则考虑在成员函数定义之前的名字空间域中出现的声明.
    19. 外围类List 的成员list 隐藏了全局域中的对象.
    20. 为了访问全局对象list 必须使用全局域解析操作符: m_oDragon = :: oDragon;
    21. 类也可以定义在函数体内这样的类被称为局部类local class.
    22. 局部类只在定义它的局部域内可见, 与嵌套类不同的是, 在定义该类的局部域外没有语法能够引用局部类的成员.
    23. 局部类只能访问在外围局部域中定义的类型名,静态变量以及枚举值例, can’t access non-static data member ( there is not this pointer).
  • 相关阅读:
    PYthon继承链(egg)的思考和实战
    C++不同类型变量参与运算时的规则
    qt通过QFileDialog获取文件路径&保存文件&选择文件夹
    visual studio 2015调试程序
    C++Primer第五版——书店程序实现
    git rm命令 & git reset和checkout区别
    git diff命令输出解释 & git checkout还原文件到特定版本
    Qt使用connect传参数的两种方式
    QFrame的setFrameStyle函数 && QPalette设置背景
    tr函数作用
  • 原文地址:https://www.cnblogs.com/iiiDragon/p/3262065.html
Copyright © 2020-2023  润新知