C++ Primer Plus “阅读”笔记(五)

本篇内容包括:

  • 过程性编程和面向对象编程
  • 类概念
  • 如何定义和实现类
  • 公有类访问和私有类访问
  • 类的数据成员
  • 类方法(类函数成员)
  • 创建和使用类对象
  • 类的构造函数和析构函数
  • const成员函数
  • this指针
  • 创建对象数组
  • 类作用域
  • 抽象数据类型

第10章 对象和类

下面是最重要的OOP特性:

  • 抽象
  • 封装和数据隐藏
  • 多态
  • 继承
  • 代码的可重用性
  • 为了实现这些特性并将它们组合在一起,C++ 所做的最重要的改进是提供了类。

10.1 过程性编程和面向对象编程

  • 采用 OOP 方法时,首先从用户的角度考虑对象——描述对象所需的数据以及描述用户与数据交互所需的操作。完成对接口的描述后,需要确定如何实现接口和数据存储。最后,使用新的设计方案创建出程序。

10.2 抽象和类

10.2.1 类型是什么

  • 指定基本类型完成了三项工作
    • 决定数据对象需要的内存数量
    • 决定如何解释内存中的位
    • 决定可使用数据对象执行的操作或方法

10.2.2 C++ 中的类

  • 通常,C++ 程序员将接口(类定义)放在头文件中,并将实现(类方法的代码)放在源代码文件中。
  • 防止程序直接访问数据被称为数据隐藏
  • 数据项通常放在私有部分
  • 不必在类声明中使用关键字private,因为这是类对象的默认访问控制

10.2.3 实现类成员函数

  • 类方法的完整名称中包括类名。形如 Stock::update() 的是函数的限定名(qualified name),而 update() 是函数的缩写(非限定名,unqualified name),只能在类作用域中使用
  • 定义位于类声明中的函数都将自动成为内联函数;当然,也可以在外部定义并加上 inline 限定符
    • 内联函数要求在每个使用它们的文件中都对其定义,所以最好将内联定义放在定义类的头文件中

10.3 类的构造函数和析构函数

  • struct 不同,下面的列表初始化是不被允许的,因为这相当于访问了私有变量。构造函数默认好像只有拷贝和无参两种。
    1
    2
    class ABC { int a, b, c; };
    ABC x{1, 2, 3};

10.3.2 使用构造函数

  • 构造函数可以和 new 连用:
    1
    Foo *bar = new Foo(2, 3, 3);
  • 下面两条语句有根本性的差别
    1
    2
    Foo bar1 = Foo(2, 3, 3);
    bar2 = Foo(2, 3, 3);
    第一行是初始化,可能会创建临时对象也可能不会;第二行是赋值,一定会创建临时对象
  • 只要与某个构造函数的参数列表匹配,就可以使用列表初始化
  • 接受一个参数的构造函数允许使用赋值语法将对象初始化为一个值(当然,如果有一个接受一个 int 的构造函数和一个接受一个 long 的构造函数,这种语法是不可用的)
    1
    Classname object = value;
    1
    2
    void Foo(Classname object);
    Foo(value);

10.3.3 默认构造函数

  • 默认构造函数不初始化成员
  • 当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数

10.3.4 析构函数

  • 有些编译器可能会过一段时间才删除临时对象,因此析构函数的调用有可能会延迟

10.3.5 const 成员函数

  • 下面第一段代码不一定能够过编,因为 show() 方法无法确保调用对象不被修改。第二段是解决方案
    1
    2
    const Foo bar = Foo(2, 3, 3);
    bar.show();
    1
    2
    3
    void Foo::show() const {
    /* details omitted */
    }
  • 只要类方法不修改调用对象,就应将其声明为 const

10.5 对象数组

  • 可以用构造函数来初始化数组元素。在这种情况下,必须为每个元素调用构造函数
    1
    2
    3
    4
    5
    Foo bar[3] {
    Foo(2, 3, 3),
    Foo(2, 3, 3),
    Foo(2, 3, 3)
    };

10.6 类作用域

  • 类作用域意味着不能从外部直接访问类的成员。所以,要调用公有成员函数,必须通过对象

10.6.1 作用域为类的常量

  • const 常量不能出现在类中。因为声明类只是描述的对象的形式,并没有创建对象。因此,在创建对象前,将没有用于储存值的空间。
  • 有三种方法可以解决这个问题
    • 在类中声明一个枚举,但这种方式对类型有限制
      1
      class Foo { enum { bar = 12 }; };
    • 使用 static。这个常量与别的静态变量储存在一起,被所有对象共享。注意,初始化只能在类声明外(一般写在方法文件中而不是类声明文件中,防止出现多个初始化副本)(如果常量类型是整形则可以在类声明内初始化)
      1
      2
      class Foo { static const double bar; };
      const double Foo::bar = 12;
    • 在构造函数中初始化常量(详见 12.7.1

10.6.2 作用域内枚举

  • 为避免不同的枚举定义中的枚举量重名,C++11 提供一种作用域为类的新枚举(这里面,classstruct 关键字效果相同)(不仅在类中可以定义,别的地方也可以)
    1
    2
    3
    4
    enum class egg { small, medium, large, jumbo };
    enum struct t_shirt { small, medium, large, xlarge };
    egg choice = egg::large;
    t_shirt Floyd = t_shirt::large;
  • 为提供作用域内枚举的类型安全,这种特殊的枚举形式不支持隐式地转换为整型
  • 对于作用域枚举,默认情况下底层类型为 int。当然,也可以手动调整,但必须是一个整型
    1
    enum class alphabet : char { a = 'a', b, c, d };

10.7 抽象数据类型

  • 抽象数据类型(abstract data typeADT)以通用的方式描述数据类型,而没有引入语言或实现细节。比如栈。

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×