(笔记—C++ Primer) 第七章 类

1. 定义抽象数据类型

  • 成员函数:定义为类的一部分的函数,有时称作方法;成员函数声明必须在类的内部,定义可以在内部或外部;
  • 非成员接口函数:形参或返回值包含类对象;
  • 定义在类内部的函数是隐式的inline函数;
  • 结构体struct也可以定义类;
  • 引入this:成员函数的调用:
    total.isbn()
       成员函数实际上是通过一个名为this的额外的隐式参数(常量指针)来访问调用它的那个对象(相当于python中的self);调用一个成员函数时,该对象的地址用于初始化this,因此实际执行过程为:
    Sales_data::isbn(&total)
    注: 任何自定义为 this的变量或参数都是非法的;
  • 引入const成员函数:常量成员函数
    string isbn()const {...}
      在形参列表后添加const声明,用于修改this指针的类型;默认情况下,this时指向非常量对象的常量指针,即Sales_data *const this,但是这样就不能为常量对象所调用;(常量对象只能调用常量成员函数)
      因此将this指针变为指向常量对象的常量指针:const Sales_data *const this
  • 类作用域及和成员函数:类本身是一个作用域;成员函数可以随意使用类中的成员而不必在意顺序;
  • 类外部成员函数:其名字定义需要包含所属类,从而限定类作用域,访问其他类成员;
    double Sales_data::
  • 定义返回this对象的函数return *this, 函数返回的引用类型,是左值
  • 定义类相关的非成员函数:其操作属于类接口的组成部分,但是不属于类本身(为啥?),其声明应该与类在同一个头文件中;

1.4 构造函数(constructors)

- 构造函数用于初始化类对象的数据成员;
- 名字和类名相同,没有返回类型;
- 类可以有多个构造函数,类似于重载;
- 构造函数不可以是`const`
- 类似于python中的 init() ?
  • 合成的默认构造函数:在没有显式构造函数的情况下;无需任何实参;
  • 多数的类是不能依靠默认构造函数的,还是需要自己构建;

1. Saes_data() = default;
默认构造函数;若在类内部,则是内联的,反之则不然;
2. 构造函数初始值列表
Sales_data(const string &s, unsigned n, double p):
    bookNo(s),units_sold(n),revenue(p*n){}
注意其函数体为空;
3. 外部定义构造函数
Sales_data::Sales_data(istream &is)
{
    read(is, *this);
}

1.5 拷贝、赋值和析构(销毁)

  • 拷贝:初始化变量、值传递或返回
  • 赋值:
  • 销毁:局部对象在块结束时被销毁;
    一般编译器可以合成这些操作,但是当类用于动态内存管理时,合成版本会失效;不过可以通过使用vector来避免分配和释放内存带来的复杂性;

2. 访问控制与封装

  • 访问说明符:
    • public:其后的成员可以被整个程序访问,用于定义类的接口(包括构造函数);
    • private:其后的成员可以被类的成员函数访问,但不可以被外部接口函数访问;(可以被类对象直接访问吗?)
  • struct和class定义类的唯一区别是默认访问权限:即在第一个访问说明符之前的成员,对于struct而言是public的,对于class而言是private的;
  • 若类的所有成员是public,则用struct,否则用class
  • 封装encapsulation: is the separation of implementation from interface. It hides the implementation details of a type. (In C++, encapsulation is enforced by putting the implementation in the private part of a class)
    1. 确保用户代码不会无意间破坏封装对象的状态;
    2. 可以随时改变具体实现细节,而不必更改用户级代码;

2.1友元

  • 普通的接口函数是无法访问私有数据成员的,通过使其他类或函数成为友元,就可以访问私有成员;
  • 声明方式:在类内添加friend Sales_data add(const Sales_data&, const Sales_data&);,
  • 在类外还需要对友元函数再次声明;

3. 类的其他特性

类型成员、类成员的类内初始值、可变数据成员、内联成员函数、从成员函数返回*this、定义类类型及友元类

3.1 类成员再探

  • type member(类型成员) using pos = string::size_type
    与其他成员不同,类型成员必须定义在使用之前;
  • 显示说明成员函数内联时,可以仅在外部定义时进行内联声明
  • 成员函数也可以被重载;
  • 可变数据成员:即使时const成员函数也可以修改一个可变成员的值;
    在变量声明时加mutable关键字;
  • 类内初始值,必须等号或花括号;

3.2 返回*this的成员函数

inline Screen &Screen::set(char ch)
{
    contents[cursor] = ch;
    return *this;
}
  • 返回引用的函数是左值的,即返回的是对象本身而非其副本;
  • 从const成员函数返回*this,:则函数返回类型为const Screen &;且常量对象只能调用常量成员函数;

3.3 类类型

  • 每个类定义了唯一的类型;
  • 只有类被完全定义之后,数据成员才可以被声明称这种类型,故一个类的成员类型不能是他自己,但是允许包含指向他自己的指针或引用;

3.4 友元再探

  • 友元可以是普通的非成员函数,其他类,或者其他类的成员函数;
  • 一个类的友元类的成员函数可以访问该类的私有成员;
  • 友元不存在传递性;
  • 如果一个类要把一组重载函数作为友元,则需要对每个函数进行友元声明;
  • 即使友元函数在类内定义,也必须在类外声明,使其可见;

4. 类的作用域

4.1 名字查找

  • 编译器在处理完类的全部声明之后,才处理成员函数的定义,因此成员函数可以使用类中出现的所有名字;
  • 如果成员使用了外部定义的类型名,则不要再去重定义它;
  • 类型名定义通常在类的开始处;

5. 构造函数再探

5.1 初始值列表

  • 注意初始化与赋值的差异,可能造成影响;
Sales_data::Sales_data(const string &s, unsigned cnt, double price)
{
    bookNo = s;
    unit_sold = cnt;
    revenue = cnt * price;
}
//上述代码虽然也是构造函数,但实际上其执行的是赋值操作;
  • 如果成员是const, 引用或者未提供默认构造函数的类类型,则其初始化只能通过构造函数初始值列表来实现;
  • 尽量使初始值的顺序与成员声明顺序一致,避免用一个成员初始化另一个成员;
  • 默认实参

5.2 委托构造函数

  • 部分或全部地将职责委托给其他构造函数;
Sales_data_class(const string &s, unsigned n, double p) :
    bookNo(s), units_sold(n), revenue(p*n) {}
Sales_data_class(const string &s) :Sales_data_class(s,0,0) {}

5.3 默认构造函数

  • 如果提供了其他构造函数,最好也提供一个默认构造函数;

5.4 隐式的类类型转换

  • 单个实参的构造函数才能用于隐式类类型转换;
  • 类类型转换只能执行一次
  • explicit阻止隐式类型转换的使用,只针对单个实参的构造函数有效;

5.5 聚合类

所有成员都是public的
没有定义任何构造函数
没有类内初始值
没有基类,也没有virtual函数;
  • 用户可以直接访问其成员

5.6 字面值常量类

  • constexpr的参数和返回类型都需是字面值常量;
  • 数据成员都是字面值类型的聚合类是字面值常量类;

    数据成员都必须是字面值类型。
    类必须至少含有一个constexpr构造函数
    如果一个数据成员含有类内初始值,则内置类型成员的初始值必须是一条常量表达式;或者如果成员属于某种类类型,则初始值必须使用成员自己的constexpr构造函数。
    类必须使用析构函数的默认定义,该成员负责销毁类的对象。
    
  • 尽管构造函数不能是const的,但是字面值常量类的构造函数可以是constexpr函数

6. 类的静态成员

  • 有时需要类的成员与类本身有关,而非与类对象有关;
  • 类的静态成员存在于任何对象之外,
  • 静态成员函数即不予对象绑定,也不包含this指针,不能被声明为const
  • 使用作用域运算符直接访问静态成员:
r = Account::rate();
  • 类的对象、引用和指针仍然可以访问静态成员
  • 静态数据成员最好定义在与其他非内联函数同一文件内;
  • 常量静态数据成员即使被类内初始化了,也应该在类外定义一下;
  • 静态成员可以是不完全类型,可以做默认实参;

静态数据成员初始化的格式如下:
 <数据类型><类名>::<静态数据成员名>=<值>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值