美文网首页
第 7 章:类

第 7 章:类

作者: 修司敦 | 来源:发表于2018-11-07 22:51 被阅读0次
    1. 类有两项基本能力:数据抽象和封装。数据抽象即为定义数据成员和成员函数的能力。封装即通过设访问说明符为 private 即可保护类的成员不被随意访问的能力。

    2. 定义在类内部的函数是隐式的 inline 函数,会在适当的时候在调用的过程中自动展开以节省开销。可以在类内显式的把函数声明成 inline,然后在类外普通地定义;也可以只在类外函数定义的时候指定 inline,而在类内普通声明。

    3. 在成员函数的声明中,如果在参数列表后面跟随一个 const 关键字,作用是修改 this 指针的类型。默认情况下,this 的类型是个常量指针,是个顶层 const,指向一个非常量对象。由于 this 不是底层 const,我们不能把 this 指针绑定到一个常量对象上,这就意味着在一些情况下,常量对象不能够调用自己的成员函数。这个问题如何解决呢?如果要让这个 this 指针指向一个常量对象,那么就需要在成员函数的参数列表之后添加一个 const 关键字。如此使用 const 的成员函数称为常量成员函数。例如:

      void MyClass::memFunc()
      { 
           cout << this->what() << endl; // this 指针的类型是 MyClass *const
      }
      void MyClass::constMemFunc() const 
      { 
           cout << this->what() << endl; // this 指针的类型是 const MyClass *const
      }
      

      这样的话,常量对象也能调用自己的函数了。但是注意,由于这个对象本身是常量,所以只能调用自己的常量成员函数,而不能调用自己的其他的非常量成员函数。而且,指向这个常量对象的指针,以及对这个常量对象的引用,同样也只能调用常量成员函数。

    4. 当创建一个类的 const 对象的时候,直到构造函数完成初始化过程 (完成初始化列表进入函数体),对象才能真正获得其"常量"属性。因此,构造函数在 const 对象的初始化过程中可以向其写值。

    5. 如果想要在常量成员函数内修改一个常量对象的某个数据成员,可以在类内声明数据成员的时候用 mutable 关键词修饰。这个关键词的含义是,这个数据成员即使在一个常量对象内,也依旧可以被修改。这样子就可以在常量成员函数中修改这个可变数据成员了。

    6. 默认构造函数是编译器帮我们做的事情,它不接受任何实参将按照如下规则初始化类的数据成员:如果存在类内初始值,那就用!否则,默认初始化该成员。只有在类没有声明任何构造函数时,编译器才会自动的生成默认构造函数;否则如果我们自己定义了自己的构造函数,那么编译器将不再为我们生成一个默认构造函数,在这种情况下如果我们还想要一个默认构造函数,我们需要自己定义一个一样的,并且用 = default 修饰。如果类内包含有内置类型或者复合类型 (比如数组和指针) 的成员,则只有当这些成员全都被赋予了类内初始值时,这个类才适合于使用合成的默认构造函数。如果类内有一个其他类类型的成员而且这个成员的类型没有默认构造函数,那么编译器将无法初始化该成员。

    7. 在默认构造函数参数列表后面加上 = default 意味着要求编译器生成这个默认构造函数。如果这个 = default 和声明一起出现在类的内部,那么默认构造函数是内联的;如果它在类外部,那么该默认构造函数在默认状况下不是内联的。

    8. 如果你定义了一个构造函数,为所有的参数都提供了默认实参,那么这个函数实际上也就定义了默认构造函数。

    9. 构造函数不应轻易覆盖掉类内初始值,除非用户在新建对象的时候新赋的值与类内初始值不同。

    10. 只接受一个其他类型的实参的构造函数也被称为转换构造函数,它定义了一条从参数类型到类类型的隐式转换的规则。如果想要抑制这条规则,那就在构造函数的声明前使用 explicit 关键字。

    11. publicprivate 这类的访问说明符,在类内没有顺序、数量的限制。classstruct 的唯一区别就是在第一个访问说明符前的成员定义的访问权限,class 的是 private,而 struct 的是 public

    12. 在类内声明类外函数或其他类,并在前面加上 friend 关键词,即可使这些类外函数或其他类获得访问本类的私有数据成员和成员函数。这些友元的声明只能出现在类定义的内部,但是类内出现的具体位置不限。一般来说推荐在类定义开始或类定义结束的时候集中声明友元。友元的声明仅仅赋予了访问权限,具体的友元函数或友元类还需要在其他地方具体定义。一般来说,友元的类外声明放在和类的成员函数定义的同一个文件中。友元关系不具有传递性,所以每个类都负责维护自己的友元关系。

    13. 除了定义数据和函数成员之外,类还可以定义某种类型在类内的别名,并赋予他们访问权限。也就是说,这个别名可以是 public,也可以是 private。这些类型别名只有在定义之后才可以被使用,所以一定要出现在类开始的地方。

    14. 当一个类的名字出现之后,这个类被认为是被声明过了,这个时候就可以在类内声明这个类的指针或引用了 (哪怕这个时候该类还没有定义完全)。但是只有当类定义完毕之后,编译器才能知道一个这样类的对象需要多少存储空间,这个时候才能创建这个类的对象,并且使用这个类的引用或指针。因此,在类内只可以创建同一个类的指针或引用,但是不能在类内包含一个类的对象。一个例外是静态成员,在一个类内可以定义同一个类的静态数据成员。

    15. 前向声明:仅仅声明类的名字。例如 class MyClass; 通常用于表示位于类定义之前的类声明。

    16. 构造函数的初始化列表负责初始化数据成员,函数体负责干赋值和其他的事情。所以对于类内的这些数据成员:常量、引用和其他没有定义默认构造函数的类的对象,必须在构造函数的函数体前、初始化列表内对其进行初始化。一旦完成了初始化列表进入函数体,那所有的动作都是赋值,而对未初始化的某些变量进行赋值会引发错误。

    17. 建议养成使用构造函数初始值的习惯,这样又能避免某些意想不到的编译错误,又能在一些情况下提高底层效率。

    18. 数据成员的初始化顺序并不按照构造函数初始化列表里面的初始化顺序,反而按照这些数据成员在类内声明的顺序!所以在写初始化列表的时候,尽量按照类内声明的顺序写初始化顺序,这样子可以提醒自己避免犯一些错误。并且,尽量避免使用一些成员来初始化另一些成员,因为这样往往会引发奇奇怪怪的初始化问题。如果可能的话,尽量使用构造函数的参数作为成员的初始值。

    19. 构造函数初始化列表也可以调用其他构造函数来委托构造。这种构造函数叫做委托构造函数:它把一些初始化的任务委托给了其他构造函数来完成。

    20. 类的静态数据成员和静态成员函数通过在类内的声明前加入 static 关键字来声明。一般来说在类外部 (实现文件内) 初始化并定义静态数据成员和静态成员函数,而且不能再次添加 static 关键字。静态成员不占据类对象的内存空间。可以在类内默认初始化静态成员,但是初始化的静态成员就必须是 constexprconst,而且默认初始值也是常量表达式,而且在提供了默认初始值之后,这个成员也要在类外定义,而且定义的时候不能再指定一个初始值 (因此看起来就像是在类内定义而在类外声明)。

    21. 静态成员可以作为类内的默认实参,而普通的数据成员不可以。

    相关文章

      网友评论

          本文标题:第 7 章:类

          本文链接:https://www.haomeiwen.com/subject/fumsxqtx.html