类定义了数据成员和函数成员:数据成员用于存储与该类类型的对象相关联的状态;而函数成员则负责执行赋予数据意义的操作。

第12章 类

一个类可以包含若干公有的、私有的和受保护的部分:在public部分定义的成员可被使用该类型的所有代码访问;在private部分定义的成员可被其他类成员访问。

所有成员必须在类的内部声明。

const成员函数不能改变其所操作的对象的数据成员。const必须同时出现在声明和定义中,若只出现其中一处,就会出现一个编译时错误。

类背后蕴涵的基本思想是数据抽象和封装。

数据抽象是一种依赖于接口和实现分离的编程(和设计)技术。

封装是一项将底层次的元素组合起来形成新的、高层次实体的技术。

可以在任意的访问标号出现之前定义类成员。在类的左花括号之后、第一个访问标号之前定义成员的访问级别,其值依赖于类是如何定义的。如果类是用struct关键字定义的,则在第一个访问标号之前的成员是公有的;如果类是用class关键字定义的,则这些成员是私有的。

如果在多个文件中定义一个类,那么每个文件中的定义必须是完全相同的。

将类定义放在头文件中,可以保证在每个使用类的文件中以同样的方式定义类。

不完全类型只能用于定义指向该类型的指针及引用,或者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数。

类的前向声明一般用来编写相互依赖的类。

类的定义以分号结束。分号是必需的,因为在类定义之后可以接一个对象定义列表。例:

class Sales_item {/*...*/};
class Sales_item {/*...*/} accum,trans;

如果返回类型使用由类定义的类型,则必须使用完全限定名。例如:

class Screen
{
public:
typedef std::string::size_type index;
index get_cursor() const;
};
inline Screen::index Screen::get_cursor() const
{
return cursor;
}

构造函数是特殊的成员函数,只要创建类类型的新对象,都要执行构造函数。构造函数的工作是保证每个对象的数据成员具有合适的初始值。

构造函数的名字与类的名字相同,并且不能指定返回类型。

构造函数不能声明为const,例:

class Sales_item
{
public:
Sales_item() const; //error
}

构造函数初始化式只在构造函数的定义中而不是声明中指定。

没有默认构造函数的类类型的成员,以及const或引用类型的成员,不管是哪种类型,都必须在构造函数初始化列表中进行初始化。例:

class ConstRef
{
public:
ConstRef(int ii);
private:
int i;
const int ci;
int &ri;
};
// no explicit constructor initializer:error ri is uninitialized
ConstRef::ConstRef(int ii)
{ // assignment:
i=ii; // ok
ci=ii; //error:cannot assign to a const
ri=ii; //assigns to ri which was not bound to an object
}

记住,可以初始化const对象或引用类型的对象,但不能对它们赋值。在开始执行构造函数的函数体之前,要完成初始化。初始化const或引用类型数据成员的唯一机会是在构造函数初始化列表中。编写该构造函数的正确方式为:

//ok:explicitly initialize reference and const members
ConstRef::ConstRef(int ii): i(ii), ci(ii), ri(ii) {}

成员被初始化的次序就是定义成员的次序。

如果一个成员是根据其他成员而初始化,则成员初始化的次序是至关重要的。

内置和复合类型的成员,如指针和数组,只对定义在全局作用域中的对象才初始化。当对象定义在局部作用域中时,内置或复合类型的成员不进行初始化。

如果类包含内置或复合类型的成员,则该类不应该依赖于合成的默认构造函数。它应该定义自己的构造函数来初始化这些成员。

可以通过将构造函数声明为explicit ,来防止在需要隐式转换的上下文中使用构造函数。

explicit 关键字只能用于类内部的构造函数声明上。

友元(friend)机制允许一个类将其非公有成员的访问权授予指定的函数或类。友元的声明以关键字friend开始。它只能出现在类定义的内部。





每个static数据成员是与类关联的对象,并不与该类的对象相关联。

static成员是类的组成部分但不是任何对象的组成部分,因此,static成员函数没有this指针。

static数据成员必须在类定义体的外部定义(正好一次)。不像普通数据成员,static成员不是通过类构造函数进行初始化,而是应该在定义时进行初始化。

static关键字只能用于类定义体内部的声明中,定义不能标示为static。





小结:

类是C++中最基本的特征,允许定义新的类型以适应应用程序的需要,同时是程序更短且更易于修改。

数据抽象是指定义数据和函数成员的能力,而封装是指从常规访问中保护类成员的能力,它们都是类的基础。成员函数定义类的接口。通过将类的实现所用到的数据和函数设置为private来封装类。

类可以定义构造函数,它们是特殊的成员函数,控制如何初始化类的对象。可以重载构造函数。每个构造函数应初始化每个数据成员。初始化列表包含的是名-值对,其中的名是一个成员,而值则是该成员的初始值。

类可以将其非public成员的访问权授予其他类或函数,并通过将其他的类或函数设为友元来授予其访问权。

类也可以定义mutable或static成员。mutable成员永远都不能为const;它的值可以在const成员函数中修改。static成员可以是函数或数据,独立于类类型的对象而存在。

第13章 复制控制

复制构造函数是一种特殊构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。

析构函数是构造函数的互补:当对象超出作用域或动态分配的对象被删除时,将自动应用析构函数。

当用于类类型对象时,初始化的复制形式和直接形式有所不同:直接初始化直接调用与实参匹配的构造函数,复制初始化总是调用复制构造函数。

定义复制构造函数最困难的部分在于认识到需要复制构造函数。

为了防止复制,类必须显式声明其复制构造函数为private。然而,类的友元和成员仍可以进行复制。如果想要连友元和成员中的复制也禁止,就可以声明一个(private)复制构造函数但不对其定义。

构造函数的一个用途是自动获取资源。
析构函数完成资源回收,最为类构造函数的补充。

动态分配的对象只有在指向该对象的指针被删除时才撤销。如果没有删除指向动态对象的指针,则不会运行该对象的析构函数,对象就一直存在,从而导致内存泄露,而且,对象内部使用的任何资源也不会释放。

三法则(rule of three):如果类需要析构函数,则它也需要赋值操作符和复制操作符,这是一个有用的经验法则。这个规则常称为三法则(rule of three),指的是如果需要析构函数,则需要所有这三个复制控制成员。

包含指针的类需要特别注意复制控制,原因是复制指针时只复制指针中的地址,而不会复制指针指向的对象。

大多数C++类采用以下三种方法之一管理指针成员:
(1)指针成员采取常规指针型行为。这样的类具有指针的所有缺陷但无需特殊的复制控制。
(2)类可以实现所谓的“智能指针”行为。指针所指向的对象是共享的,但类能够防止悬垂指针。
(3)类采取值型行为。指针所指向的对象是唯一的,由每个类对象独立管理。

小结:
类除了定义该类型对象上的操作,还需要定义复制、赋值或撤销该类型对象的含义。特殊成员函数(复制构造函数、赋值构造函数和析构函数)可用于定义这些操作。这些操作统称为“复制控制”函数。
如果类没有定义这些操作中的一个或多个,编译器将自动定义它们。合成操作执行逐个成员成员初始化、赋值或撤销:合成操作依次取得每个成员,根据成员类型进行成员的复制、赋值或撤销。如果成员为类类型的,合成操作调用该类的相应操作(即,复制构造函数调用成员的复制构造函数,析构函数调用成员的析构函数,等等)。如果成员为内置类型或指针,则直接复制或赋值,析构函数对撤销内置类型或指针类型的成员没有影响。如果成员为数组,则根据元素类型以适当方式复制、赋值或撤销数组中的元素。
与复制构造函数和赋值操作符不同,无论类是否定义了自己的析构函数,都会创建和运行合成析构函数。如果类定义了析构函数,则在类定义的析构函数结束之后运行合成析构函数。
分配内存或其他资源的类几乎总是需要定义复制控制成员来管理所分配的资源。如果一个类需要析构函数,则它几乎也总是需要定义复制构造函数和赋值操作符。

第14章 重载操作符与转换

重载操作符是具有特殊名称的函数:保留字operator后接需定义的操作符符号。

用于内置类型的操作符,其含义不能改变。

操作符的优先级、结合性或操作数数目不能改变。

下面是一些指导原则,有助于决定将操作符设置为类成员还是普通非成员函数:
1、赋值(=)、下标([ ])、调用(( ))和成员访问箭头(->)等操作符必须定义为成员,将这些操作符定义为非成员函数将在编译时标记为错误。
2、像赋值一样,复合赋值操作符通常应定义为类的成员。与赋值不同的是,不一定非得这样做,如果定义非成员复合赋值操作符,不会出现编译错误。
3、改变对象状态或与给定类型紧密联系的其他一些操作符,如自增、自减和解引用,通常应定义为类成员。
4、对称的操作符,如算数操作符、相等操作符、关系操作符和位操作符,最好定义为普通非成员函数。

IO操作符必须为非成员函数。 类通常将IO操作符设为友元。

设计输入操作符时,如果可能,要确定错误恢复措施,这很重要。

一般而言,将算数和关系操作符定义为非成员函数。

既定义了算数操作符又定义了相关复合赋值操作符的类,一般应使用复合赋值实现算数操作符。

类定义下标操作符时,一般需要定义两个版本:一个为非const成员并返回引用,另一个为const成员并返回const引用。

转换操作符是一种特殊的类成员函数。它定义将类类型值转变为其他类型值的转换。转换操作符在类定义体内声明,在保留字operator之后跟着转换的目标类型,通用形式如下:

operator type();

这里,type表示内置类型名、类类型名或由类型别名所定义的名字。对任何可作为函数返回类型的类型(除了void之外)都可以定义转换函数。一般而言,不允许转换为数组或函数类型,转换为指针类型(数据和函数指针)以及引用类型是可以的。

转换函数必须是成员函数,不能指定返回类型,并且形参表必须为空。

语言只允许一次类类型转换。

<C++Primer>第四版 阅读笔记 第三部分 “类和数据抽象”的更多相关文章

  1. C++ Primer 第四版阅读笔记

    阅读笔记 初始化 变量定义指定了变量的类型和标识符,也可以为对象提供初始值.定义时指定了初始值的对象被称为是 已初始化的.C++ 支持两种初始化变量的形式:复制初始化和 直接初始化.复制初始化语法用等 ...

  2. <C++Primer>第四版 阅读笔记 第一部分 “基本语言”

    之前阅读时没有及时总结,现在慢慢补上. 第1章 快速入门 main 函数在很多方面都比较特别,其中最重要的是每个C++程序必须含有 main 函数,且 main 函数是(唯一)被操作系统显示调用的函数 ...

  3. <C++Primer>第四版 阅读笔记 第四部分 “面向对象编程与泛型编程”

    继承和动态绑定与数据抽象一起成为面向对象编程的基础. 模板使我们能够编写独立于具体类型的泛型类和泛型函数. 第15章 面向对象编程 面向对象编程基于三个基本概念:数据抽象.继承和动态绑定.在C++中, ...

  4. <C++Primer>第四版 阅读笔记 第二部分 “容器和算法”

    泛型算法中,所谓"泛型(generic)"指的是两个方面:这些算法可作用于各种不同的容器类型,而这些容器又可以容纳多种不同类型的元素. 第九章 顺序容器 顺序容器的元素排列次序与元 ...

  5. C++Primer第5版学习笔记(三)

    C++Primer第5版学习笔记(三) 第四/五章的重难点内容           你可以点击这里回顾第三章内容       因为第五章的内容比较少,因此和第四章的笔记内容合并.       第四章是 ...

  6. C++Primer第5版学习笔记(四)

    C++Primer第5版学习笔记(四) 第六章的重难点内容         你可以点击这里回顾第四/五章的内容       第六章是和函数有关的知识,函数就是命名了的代码块,可以处理不同的情况,本章内 ...

  7. C++Primer第5版学习笔记(一)

    C++Primer第5版学习笔记(一) 第一.二章的重难点内容        这个笔记本主要记录了我在学习C++Primer(第5版,中文版)的过程中遇到的重难点及其分析.因为第一.二章都比较简单,因 ...

  8. C++Primer第5版学习笔记(二)

    C++Primer第5版学习笔记(二) 第三章的重难点内容         这篇笔记记录了我在学习C++常用基本语法的学习过程,基本只记录一些重难点,对概念的描述不是一开始就详尽和准确的,而是层层深入 ...

  9. C++学习书籍推荐《C++ Primer 第四版》下载

    百度云及其他网盘下载地址:点我 编辑推荐 <C++ Primer中文版(第4版)>对C++基本概念和技术全面而且权威的阐述,对现代C++编程风格的强调,使<C++ Primer中文版 ...

随机推荐

  1. [Unity]SQLite-C#调用

    SQLite数据库-Unity操作 项目开发的时候,经常会遇到的一种需求,数据存储 离线缓存的数据类型很多,大致分成两类 字符串文本数据 多媒体数据 字符串数据的类型只有字符串,但是结构有很多: xm ...

  2. CI框架学习——基本的用法(一)

    一.必备知识 1.如何访问你的页面 我在控制器下面建立了admin文件夹,此文件夹下有login.php 文件,我们现在要访问这个文件 http://localhost/gradesystem/ind ...

  3. HUST 1372 marshmallow

    很简单的博弈题.....算几组能得到规律了. 某个状态先手要赢 等价于 之前有一种状态是后手赢,先手可以保证让现在这个状态到达那个状态 #include<cstdio> #include& ...

  4. Android Material各种颜色设置

    Blogpost about support appcompat v21 from Chris Banes

  5. 关于div宽度和高度的100%设定

    设置DIV大小的有两个属性width和height,以前在学习DIV每次给DIV设置100%宽度或高度时都很迷惑,不明白这个100%的宽度(高度)到底有多宽有多高?这个100%是从哪里得到的从哪里继承 ...

  6. css3动画-animation

    animation驱使一组css style变化到另外一组css style,它可以定义keyframes的集合,指定style的开始和结束状态,它是transition的增强. 配置animatio ...

  7. LPC2478时钟模块详解

    时钟框图如上图,系统时钟来源分别是内部4M的RC振荡器和外置晶振,RTC模块在某些情况下也可以作为主时钟,经过系统时钟选择s\de时钟需要经过PLL倍频(或者不倍频),处理过的PLL输出USB时钟分频 ...

  8. UVa 496 - Simply Subsets

    题目大意:给你两个集合,判断两个集合的关系(不相交.相等.真子集和其他).简单判断就可以了,不过STL的set没有交集.并集等操作有点让人觉得不方便... #include <cstdio> ...

  9. scrapy+Lucene搭建小型搜索引擎

    Reference: http://blog.csdn.net/napoay/article/details/51477586 一.选题 工程类搜索型: 定向采集 3-4 个新闻网站, 实现这些网站信 ...

  10. [Angular Tutorial] 10 -More Templating

    在这一步中,我们会实现电话细节的视图,这在用户点击列表中的一部电话时被展示. ·当您点击列表中的一部电话时,带有电话特定信息的电话细节页面将被展示. 我们打算使用$http来获取我们的数据,以此来实现 ...