美文网首页程序员
[C++ Primer Note6] 类

[C++ Primer Note6] 类

作者: 梦中睡觉的巴子 | 来源:发表于2018-11-20 23:17 被阅读2次
    1. 成员函数一个名为this的额外隐式参数来访问调用它的那个对象。当我们调用一个成员函数时,编译器负责把对象的地址传递给隐式参数this。this实际上是一个常量指针,不允许改变this中的地址。
    2. 跟随在成员函数参数列表之后的const关键字表示this是一个指向常量的指针。这种使用const的成员函数被称作常量成员函数(const member function)
    3. 常量对象以及常量对象的引用或指针都只能调用常量成员函数
    4. 类本身就是一个作用域,类的成员函数的定义嵌套在类的作用域之内,成员函数可以随意使用类中的其他成员而无须在意出现次序。
    5. 在类的外部定义成员函数时,成员函数的定义必须与它的声明匹配,如果成员函数被声明为常量成员函数,那么定义也必须在参数列表后指定const属性
      double Sales_data::avg_price() const{
        .....
      }
    
    1. 可以通过*this来返回执行该成员函数的对象
    2. 类的成员通常需要定义一些辅助函数,这些函数定义的操作概念上属于类的接口的组成部分,但实际上不属于类的本身。这些函数的声明一般应与类声明在同一个头文件内
    3. 类通过一个或几个特殊的成员函数来控制其对象的初始化过程,这些函数叫做构造函数(constructor),构造函数名字和类名相同,但是没有返回类型
    4. 构造函数不能被声明为const的,当我们创建一个类的const对象时,直到构造函数完成初始化过程,对象才能真正取得“常量”属性。因此,构造函数在const对象的构造过程中可以向其写值。
    5. 类通过一个特殊的构造函数来控制默认初始化过程,这个函数叫做默认构造函数(default constructor),默认构造函数无需任何实参
    6. 如果我们的类没有显式定义构造函数,编译器就会隐式地定义一个默认构造函数,又被称为合成的默认构造函数(synthesized default constructor),对大多数类型来说,这个函数按照如下规则初始化类的数据成员;
    • 如果存在类内初始值,用它来初始化成员。
    • 否则,默认初始化该成员。
    1. 对于一个普通的类来说,必须定义它自己的默认构造函数。
    2. 如果类中包含其他类类型的成员且该成员的类型没有默认构造函数,那么编译器将无法生成默认构造函数
    3. C++11标准中,如果我们需要默认的行为,那么可以通过在参数列表后面写上=default来要求编译器生成构造函数。
      Sales_data() = default;
    

    =default既可以和声明一起在类的内部,也可以作为定义在类的外部。和其他函数一样,如果在类的内部,则默认构造函数是内联的;如果它在类的外部,则默认情况下不是内联的。

    1. 构造函数初始值列表(constructor initialize list)负责为新创建的对象的一个或几个数据成员赋初值。
      Sales_data(const string &s):bookNo(s),units_sold(0),revenue(0){}
    
    1. 没有出现在构造函数初始值列表中的成员将通过相应的类内初始值(如果存在的话)初始化,或者执行默认初始化。如果不支持类内初始值,则所有构造函数都应该显式地初始化每个内置类型的成员
    2. 除了定义类的对象如何初始化外,类还需要控制拷贝赋值销毁对象时发生的行为。对象在几种情况下会被拷贝,如我们初始化变量以及以值得方式传递或返回一个对象等。如果我们不主动定义这些操作,编译器将替我们合成它们。一般来说,生成的版本将对对象的每个成员执行拷贝,赋值和销毁操作。
    3. 一个类可以包括0个或多个访问说明符(public,private)
    4. 使用classstruct定义类唯一的区别就是默认的访问权限。struct默认为public,而class默认为private
    5. 类可以允许其他类或者函数访问它的非公有成员,方法是令其他类或者函数成为它的友元(friend)。如果类想把函数作为它的友元,只需要增加一条以friend关键字开始的函数声明语句即可:
      class Sales_data{
        friend Sales_data add(const Sales_data&, const Sales_data&);
        ...
    }
    

    友元声明只能出现在类定义的内部,但是具体位置不限。友元不是类的成员也不受区域访问控制级别的约束。一般来说,最好在类定义开始结束前的位置集中声明友元。

    1. 友元的声明仅仅指定了访问的权限,而非一个通常意义上的函数声明。如果我们希望类的用户能够调用某个友元函数,那么我们就必须在友元声明之外再专门对函数进行一次声明
    2. 除了定义数据和函数成员之外,类还可以自定义某种类型在类中的别名,其也存在访问限制,可以是public或private。
    class Screen{
    public:
      typedef string::size_type pos;
      ....
    }
    

    用来定义类型的成员必须先定义后使用,因此类型成员通常出现在类开始的地方。

    1. 定义在类内部的函数是默认内联的,也可以在类的内部或外部用inline显式声明成员函数。不过,最好只在类外部定义的地方说明inline,便于理解。
    2. 如果我们希望修改类的某个数据成员,即使是在一个const成员函数内,可以通过在变量的声明中加入mutable关键字。
    3. 可以通过是否为const实现多个成员函数的重载版本,这样,const对象会调用const版本,非常量对象则调用非const版本。
    4. 我们可以把类名作为类型的名字使用,或者我们也可以把类名跟在关键字class或struct后面:
      Sales_data item1;
      class Sales_data item1;     //一条等价的声明
    
    1. 就像可以把函数的声明和定义分离开一样,我们也能仅声明类而暂时不定义它:
      class Screen;
    
    1. 除了把非成员函数定义为友元之外,类还可以把其他类定义成友元,也可以把其他类(之前已定义过的)的成员函数定义成友元。
    class Screen{
      friend class Window_mgr;
      ...
    }
    

    如果指定了友元类,则友元类的成员函数可以访问此类所有成员

    class Screen{
      friend void Window_mgr::clear();
      ...
    }
    

    要想令某个成员函数成为友元,需要仔细组织程序的结构以满足声明和定义的彼此依赖关系

    • 先定义友元函数所属的类,声明友元函数但不定义
    • 定义需要友元函数的类,包括对友元函数的友元声明
    • 最后定义友元函数,因为这样才能使用另一个类的成员。
    1. 类和非成员函数的声明不是必须在它们的友元声明之前。当一个名字出现在一个友元声明时,我们隐式地假定改名字在当前作用域中是可见的。友元函数可以在类的内部定义,但仍必须在类的外部提供相应的声明。本质上友元声明的作用是影响访问权限,本身并非普通意义上的声明
    2. 一个类就是一个作用域,所以我们在类外部定义成员函数必须同时提供类名和函数名。一旦遇到类名,定义的剩余部分就在类的作用域之内了,所以可以直接使用类的成员。
    3. 类的定义分两步处理:
    • 编译成员的声明
    • 直到类全部可见才编译函数体
      但是对于声明使用的名字,包括返回类型和参数,都必须在声明前确保可见。如果某个成员的声明使用了类中尚未出现的名字,编译器会在定义该类的作用域中(类定义之前)继续查找。
    1. 如果类中成员使用了外层作用域中的某个名字,而该名字代表一种类型,则类不能在之后重新定义该名字。
    2. 类型名的定义通常出现在类开始的地方,确保所有该类型的成员都出现在定义之后。
    3. 如果成员函数在外部定义,则它还会考虑在成员函数定义之前和类定义之后的作用域中的声明。
    4. 如果没有在构造函数的初始值列表中显式地初始化成员,则该成员将在构造函数体之前执行默认初始化。即使在构造函数中显式赋值也并不是初始化而是赋值。
    5. 成员的初始化顺序与它们在类定义中出现顺序一致
    6. 如果一个构造函数为所有参数都提供了默认实参,则它也实际上定义了默认构造函数
    7. C++11标准使得我们可以定义所谓的委托构造函数,即使用所属类的其他构造函数执行它自己的初始化过程。
      class Sales_data{
        Sales_data():Sales_data("",0,0){}
        ...  
    };
    

    受委托的构造函数的初始值列表和函数体被依次执行,然后控制权才会交还给委托者的函数体。

    1. 我们能够为类定义隐式转换机制,如果构造函数只接受一个实参,则它实际上定义了转换为此类类型的隐式转换机制,有时我们把这种构造函数称为转换构造函数。但需要注意的是:编译器只会自动地执行一步类型转换
    2. 我们可以通过将构造函数声明为explicit阻止隐式转换。此时该构造函数只能以直接初始化的形式使用,即圆括号的形式。
    3. 聚合类使得用户可以直接访问其成员,当一个类满足一下条件时,我们说它是聚合的:
    • 所有成员都是public
    • 没有定义任何构造函数
    • 没有类内初始值
    • 没有基类,也没有virtual函数
    struct Data {
      int ival;
      string s;
    };
    

    我们可以提供一个花括号括起来的成员初始值列表来初始化聚合类的数据成员

    Data vall={0,"Kevin"};
    
    1. 通过在成员声明之前加上static关键字使得其与类关联在一起,称为静态成员
    2. 我们既可以在类的内部也可以在类的外部定义静态成员函数,挡在外部定义时,不能重复static关键字,该关键字只出现在类内部的声明语句。
    3. 因为静态数据成员不属于类的任何一个对象,所以它们并不是在创建类的对象时被定义的。一般来说,我们不能在类的内部初始化静态成员。
    4. 通常情况下, 静态成员不应该在类的内部初始化。然而,我们可以为静态成员提供const整数类型的类内初始值,不过要求静态成员必须是constexpr
    5. 静态成员的类型可以就是它所属的类类型,而非静态成员不行。同时静态成员可以作为默认实参

    相关文章

      网友评论

        本文标题:[C++ Primer Note6] 类

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