【程序设计与思想Cpp】课本知识点整理

本文最后更新于:2023年5月7日 下午

C++程序设计——思想与办法

Tips:标注书页均为《C++程序设计思想与方法(慕课版第3版)》


上来就是第8章 这是为什么呢


第8章 数据封装——结构体

记录是一组无序的、异质的数据,在C++语言中被称为结构体

8.1记录的概念 P186

记录的组成部分通常被称作字段,或者被称为成员。

8.2记录的使用 P187

在C++中记录需要进行如下两个步骤:

  1. 定义一个新的结构体类型。这指明了其变量是由哪些字段组成的
  2. 定义结构体类型的变量。完成了结构体的定义后,可以定义该类型的变量,编译器会参照相应的结构体类型定义分配相应的空间。

这两步是必不可少的。

结构体类型的定义

格式如下。

1
2
3
struct 结构体类型名{
字段声明;
};

在定义结构体类型时,字段名可以与程序中的变量名相同。在不同的结构体中也可以有相同的字段名。
结构体成员的类型可以是任意类型。可以是整数、实型,也可以是数组,甚至是其他结构体类型

结构体类型的变量的定义

  • 例如,已经有了studentT这个类型,就可以通过下列代码定义该类型的变量、数组或指针。studentT student1, studentArray[10], *sp
    studentArray是具有10个 studentT类型的值的数组。
    sp是一个指向 studentT类型的指针,它可以指向 student1,可以指向studentArray中的某个元素。
  • 结构体类型变量也可以在定义结构体类型的同时定义,格式如下。
1
2
3
struct 结构体类型名{
字段声明;
} 结构体变量;
  • 定义结构体类型变量时也可以为它赋初值。c++用一对花括号将这一组值括起来,表示成一个整体,值与值之间用逗号分开。例如:
    studentT student = { "00001","Elysia",100,90,80 }

结构体类型的变量的使用

要表示结构体类型的变量中的某一变量,需要写下整个结构体类型的变量名称,后面跟运算符 .及该字段的名称。结构体中的字段可以单独使用,它相当于普通变量。若结构体类型的变量成员还是一个结构体,则可使用 .一级一级分开,逐级访问。

  • 结构体类型的变量输入通常是通过输入它的每一个成员来实现。
1
2
3
cin >> student1.no >> student1.name 
>> student1.score.chinese
>> student1.score.english
  • 也可以通过对每一个字段赋值来实现结构体的赋值,如 student1.score.english = 145
  • 当两个结构体变量属于不同结构体类型时,就可以互相赋值,这说明结构体是左值。
    注:数组不是左值。
  • 指针也可以通过指针间接访问。指向结构体的指针可以指向同一个类型的结构体变量,也可以指向一个通过动态内存申请到的一块用于储存同类型的结构体的空间。
1
2
studentT student1, *sp = &student1;
sp = new studentT;
  • 要引用sp指向的结构体对象的某个字段,可以表示为 (*sp).chinese注意,()是必要的,因为点运算符的优先级比 *高。如果不加括号,编译器会理解为 sp.chinese为一个指针,然后指向该指针指向的内容。
    但是这种方法过于笨拙,使用者在每一次选取的时候都会使用括号,故而C++提出了另外一个更加简明的运算符 ->,用法如 指针变量名->字段名表示指针变量指向的结构体的制定字段。

注:数组通常表示一组变量,而结构体通常表示的是一个变量。

8.3结构体作为函数的参数

结构体的传递和普通内置传递一样都是值传递,由于结构体的特点,这种做法一般既浪费时间也浪费空间,因此常使用引用传递
使用引用传递时,由于形式参数和实际参数共享了同一块空间,为了避免对实际参数的修改,可以使用 const的引用传递。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//文件名:8-1.cpp
//对平面上点的操作的函数及应用
struct pointT{
double x,y;
};
void setPoint(double x, double y, pointT &p)
double getX(const pointT &p)
double getY(const pointT &p)
void showPoint(const pointT &p)
double distancePoint(const pointT &p1, const pointT &p2)

int main()
{
pointT p1, p2;

setPoint(1,1,p1);
setPoint(2,2,p2);

cout << getX(p1) << " " << getY(p2) << endl;
showPoint(p1);
cout << " -> " ;
showPoint(p2);
cout << " = " << distancePoint(p1, p2) << endl;
return 0;
}

void setPoint(double x, double y, pointT &p)
{
p.x = x;
p.y = y;
}

double getX(const pointT &p)
{ return (p.x); }

double getY(const pointT &p)
{ return (p.y); }

void showPoint(const pointT &p)
{ cout << "(" << p.x << " , " << p.y << ")"; }

double distancePoint(const pointT &p1, const pointT &p2)
{ return sqrt((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y) * (p1.y-p2.y)); }

8.4链表 P192

概念

链表是一种可以动态的进行内存分配的结构。
变量head中存储着第一个元素结点的地址,head可以找到第一个结点,第一个可以找到第二个……一直到最后一个结点。最后一个结点的第二部分存放一个空指针。

形式

形式多样。除了单链表外,还有双链表、循环链表等。数据结构中做进一步学习。

单链表

存储

在一个单链表中,每个数据元素被存储在一个结点中。存储一个单链表只需要存储第一个结点的地址,因此只需要定义一个指向结点的指针
每个结点由两部分组成:数据元素本身和指向下一结点的指针。

1
2
3
4
struct linkNode{
datatype data; //任意一种数据类型
lnkeNode *next; //指向自身类型的一个指针
}; //注意在定义结构体时最后要有分号(;)

定义 linkNode *headhead就表示了这个单链表。

操作

创建一个单链表

包括以下步骤:定义一个单链表创建一个空的单链表,依次从键盘读入数据,链入单链表的表尾。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//文件名:8-2.cpp
//单链表的建立与访问
#include <iostream>
using namespace std;

struct linkRec {
int data;
linkRec *next;
};

int main()
{
int x; //存放输入的值
linkRec *head, *p, *rear; //head为表的头指针,rear指向创建链表时的表尾结点,
//p是创建和读链表时指向被操作结点的指针

head = rear = new linkRec; //创建空链表,头结点也是最后一个结点

//创建链表的其他结点
while (true) {
cin >> x;
if (x == 0) break;
p = new linkRec; //申请一个结点
p->data = x; //将x的值存入新结点
rear->next = p; //将p链到表尾
rear = p; //p作为新的表尾
}

rear->next = NULL; //设置rear为表尾,其后没有结点了

//读链表
cout << "链表的内容为:\n";
p = head->next; //p指向第一个结点
while (p != NULL) {
cout << p->data << '\t';
p = p->next; //使p指向下一个结点
}
cout << endl;

return 0;
}


单链表的插入

1
2
3
4
5
6
tmp = new ListNode; //创建一个新结点
tmp -> data = x; //把x放入新结点的数据成员中
tmp -> next = p -> next; //把新结点和p的下一结点相连
p -> next = tmp; //把p和新结点连接起来

// 注意第三、四句不可以互换,这样会导致p后面的节点丢失

单链表的删除

可以直接 p->next = p ->next ->nxet,来删除,但这会导致内存泄露,没有回收被删除节结点的空间。
完整的删除应该有两个工作:从链表中删去该结点,回收结点的空间,如下例。

1
2
3
delPtr = p -> next; //保存被删除结点的地址
p-> next = delPtr -> next ; //将此结点从链中删去
delete delPtr ; //回收被删除结点的空间

8.5编程规范及常见错误

  • 结构体是C++的一个类。定义时最好指定名字,便于程序的其他地方也可以定义该类型的变量
  • 结构体类型定义时在最后的 }后要加一个分号
  • 结构体类型一旦被定义,就可以将其当做变量使用
  • 结构体变量可以作为函数的参数,传递方式为值传递。但是结构体采取值传递既浪费时间又浪费空间,故而一般采用引用传递。为了防止引用传递修改了实际参数,可以用 const限定形式参数。

第10章 创建新的类型

10.1面向对象程序设计

10.2类的定义 P224

定义一个类就是定义一组属性和一组对属性进行操作的函数。属性称为类型的数据成员,而函数称为类的成员函数。类定义的一般形式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13

class 类名{

private

私有数据成员和成员函数;

public

公有数据成员和成员函数;

}

私有成员只能被自己类中的成员函数访问,不能被全局函数或其他类的成员函数访问。这些成员被封装在类的内部,不为外界所知。公有成员能通过 变量名.成员名被程序中其他类的成员函数访问,它是类的对外接口。

利用private可以封装类型的实现细节。封装是将低层次的元素组合起来形成新的、高层次实体的技术。函数就是封装的一种形式。函数所执行的细节封装在函数本身这个更大的实体中。被封装的元素隐藏了他们的实现细节。类也是封装的一种形式,通常将数据的存储和处理的细节隐藏起来。结构体和类都可以用来定义新的类型,格式也完全相同,唯一区别在于:结构体中,如果没有指明访问特性,则成员是公有的;在类的定义中,默认情况下,成员是私有的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

代码清单10-7 用class定义的DoubleArray类型

// 文件名:DoubleArray.h

// DoubleArray类的定义

class DoubleArray{

private:

int low;

int high;

double *storage;


public:

//根据low和high为数组分配空间。分配成功,返回值为true,否则返回值为false

bool initialize(int lh, int rh);


//设置数组元素的值

//返回值为true表示操作正常,返回值为false表示下标越界

bool insert(int index, double value);


//取数组元素的值

//返回值为true表示操作正常,返回值为false表示下标越界

bool fatch(int index, double &value);


//回收数组空间

void cleanup();

};

完善类的定义还必须包括所有成员函数的实现。成员函数的实现通常有两种实现表示方法。

  1. 在类的定义时只给出函数原型,而函数的定义时写在一个实现文件(.cpp)中;
  2. 将成员函数的定义直接写在类定义中。直接定义在类中的函数默认为内联函数。因此,直接定义在类中的函数都是比较简单的函数。当然,内联函数也可以直接写在实现文件中,用保留字 inline说明。

良好的程序设计习惯是将类定义和成员函数的实现分开,这样可以更好的达到实现隐藏的目的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

代码清单10-8 将成员函数定义为内联函数

//文件名:10-8.h

//将成员函数定义为内联函数

#ifndef¬_DoubleArray

#define_DoubleArray


class DoubleArray{

private:

int low;

int high;

double *storage;


public:

//根据low和high为数组分配空间。分配成功,返回值为true,否则返回值为false

bool initialize(int lh, int rh)

{

low = lh;

high = rh;

storage = new double [high - low + 1];

if (storage == NULL) return false; else return true;

}

//设置数组元素的值

//返回值为true表示操作正常,返回值为false表示下标越界

bool insert(int index, double value)

{

if (index < low || index > high) return false;

storage[index - low] = value;


return true;

}


//取数组元素的值

//返回值为true表示操作正常,返回值为false表示下标越界

bool fatch(int index, double &value)

{

if (index < low || index > high) return false;

value = storage[index - low] ;


return true;

}


//回收数组空间

void cleanup()

{ if (storage) delete [] storage; }

};

#endif

有些类内部的函数不需要用户调用,这类函数被称为工具函数。工具函数通常设计为 private的。

10.3对象的使用 P228

对象的定义

一旦定义了一个类,就可以定义这种类型的变量了。在面向对象的程序设计中,这类变量称为对象。对象有两种定义方法:

1. 在程序中直接定义类型为某个类的对象

格式如下:

1
2
3

储存类别 类名 对象列表

例如定义一个静态的 Rational类的对象 r1,可写成:

1
2
3

static Rational r1

2. 动态对象

要定义一个动态对象必须要有指向对象的指针,然后 new申请一块储存对象的空间,通过 delete释放动态对象占用的空间。例如:

1
2
3
4
5
6
7

Rational *rp;

rp = nwe Rational;

delete [] rp;

对象的操作

结构体中默认所有的成员都是公有的,因此都可以用 结构体变量名.成员名来访问,但是在类的对象中,全局函数或其他类的成员函数只能访问公有成员而不能访问私有成员,因此只有公有的成员才能用点运算符 对象名.数据 成员名来访问。

与结构体类型的变量一样,除了能用对象名访问对象外,还可以用指向对象的指针访问对象。此事可以用 ->运算符;同类对象也可以相互赋值,当把一个对象赋值给另一个对象时,所有的数据成员都会逐位复制。

this指针

一个对象包括数据成员和成员函数。成员函数操作的数据就是本对象的数据成员。由于所有该类对象中的成员函数的代码是完全相同的,C++采取了一个优化手段:无论创建了多少这种类型的对象,成员函数在内存中只有一个副本。所有对象共享这一个副本。

定义一个对象时,系统只知道为数据层成员分配空间,而C++为成员函数设置了一个隐藏的真相本类型的指针形式参数 this,它指向当前调用成员函数的对象。成员函数中对对象成员的访问是通过this指针实现的。 当通过对象调用成员函数时,编译器会把相应对象的地址传给形参 this

通常,编译器会自动加上 this,但是如果在成员函数中要把对象作为整体来访问时,必须显式地使用 this指针,即 *this。这种情况在函数中返回一个调用函数的对象的引用时时常出现,11.3节中将给出用法。

10.4对象的构造与析构 P231


【程序设计与思想Cpp】课本知识点整理
http://yiliu1412.github.io/2023/01/11/cppLearn/
作者
逸流Mercurio
发布于
2023年1月11日
更新于
2023年5月7日
许可协议