您的位置:首页 > 编程语言 > C语言/C++

C++的数据抽象及类的由来

2016-02-27 22:00 627 查看

《c++编程思想》阅读笔记(2)

与c相比,为什么会有一堆类的概念呢 ?

第2章 数据抽象

1)c++是一个能提高效率的工具。为什么我们还要努力使我们从已经熟悉且效率高的语言(c)转到另一种新的语言上?而且使用这种新语言,我们会在确实掌握它之前的一段时间内降低效率。这归因于我们确信通过使用新工具将会得到更大的好处。

2)

极大提高效率的唯一办法是使用其他人的代码,即使用库。库,简单地说就是一些人已经写的代码,按照某种方式包装在一起。所以,库大概是改进效率的最重要的方法,c++的主要设计目标之一就是使库容易使用。这意味着,在c中使用库有困难

3)声明与定义

“声明”向计算机介绍名字,它说,“这个名字什么意思”。而“定义”为这个名字分配内存空间。无论涉及到变量时还是函数时都一样。无论在哪种情况下,编译器都在“定义”处分配储存空间。对于变量,编译器确定这个变量占多少储存单元,并在内存中产生存放这它们的空间。对于函数,编译器产生代码,并为之分配储存空间。函数的储存空间中有一个由使用不带参数表活带地址操作符的函数名产生的指针

定义也可以是声明。如果该编译器还没有看到过名字A,程序员定义int A。则编译器马上为这个名字分配储存地址。

声明常常使用extern关键字。如果我们只是声明变量而不是定义它,则要求用extern。对于函数声明,extern是可选的,不带函数体的函数名连同参数表或返回值,自动地作为一个声明

函数原型包括关于参数类型和返回值的全部信息, int f(float, char);是一个函数原型。c++要求必须写出函数原型,因为它增加了一个重要的安全层。举个例子

extern int i; //Declaration without definition
extern float f(float); // function declaration

float b; // Declaration & definition
float f(float a) { // Definition
return a + 1;
}

int i; //definition
int h(int x) { // Declaration &definition
return x + 1;
}


在函数声明是,参数名可给出也可不给出。而在定义时,它们是必须的。这在c语言中确实如此,但在c++中却不一定

4)在c中,使用库的最大的障碍是名字冲突的问题,无论哪种情况,都不允许使用包含具有相同函数的两个库,所以c++开始把struct中的变量名不会与全局变量名冲突的这一优点扩展到函数名上,也就是,让函数是struct的成员。这样以后,函数不再需要传入结构体的指针进行操作,编译器已经帮你做好了这个问题,这些函数现在仅有的参数与这些函数所做的事情有关,而不是与这些函数的运算的机制有关

5)在这个struct里的init()函数完全不用担心与 其他struct的init()函数相抵触,于是乎,定义这个函数是,需要完全指定它是哪一个,为了完成这个指定任务,c++有一个新的运算符::,即范围分解运算符,例如,如果希望指定init()属于stash,就写stash::init(int size, int quantity)

6)结构的声明必须要在它们定义和使用之前,而放置结构定义的最习惯的为之是在头文件中,除非我们有意把它藏在代码中。

7)这里有一个关键字,称为this,它产生这个struct的地址

8)c++的编译比c的编译更加严格,在c中,我们常常发现能使程序通过编译,然后我们必须再花力气使它工作。在c++中,程序编译正确了,它就能工作了。这是因为该与眼堆类型要求更加严格的缘故。

9)什么是对象

把函数放进结构是c++中的根本改变,并且这引起我们将结构作为新概念去思考。在c中,结构是数据的凝聚,它将数据捆绑在一起,使得我们可以将它们看作一个包。但这除了能使程序设计方便之外,别无其他好处。这些结构上的运算可以用在别处。然而将函数也放在这个包内,结构就变成了新的创造物,它既能描述属性(就像c中的结构体能做的一样),又能描述行为,这就形成了对象的概念。对象时一个独立的有约束的试题,投自己的记忆和活动

在c++中,对象只是一个变量,最纯的定义是“存储的一个区域”。它是能存放数据的空间,并隐含着还有在这些数据上的运算。将数据连同函数捆绑在一起,这一点就允许创建新的类型。这常常被称作封装。

stash(书中举例的一个类)也是一个新的数据类型,可以add()、fetch()和inflate()。由说明stash S创建一个stash就像由说明float f创建一个float一样。一个stash也有属性和行为,甚至它的活动就像一个实数——一个内建的数据类型。我们成stash为抽象数据类型,也许这是因为它能允许我们从问题空间吧概念抽象到解空间。另外,c++编译器把它看做一个新的数据类型,并且如果说一个函数需要一个stash,编译器就确保传递了一个stash给这个函数。对抽象数据类型的类型检查就像对内建类型的类型检查一样严格。

然而,我们会看到在对象上完成运算的方法有所不同。object.member_function(arglist)是对一个对象“调用一个成员函数”。而在面向对象的语法中,也称之为“想一个对象发送消息”。这样,对于stash S,语句S.add(&i) “发送消息给S”,也就是说,“对自己add()”实际上,面向对象程序设计可以终结为一句话“向对象发送消息”。需要做的所有事情就是创建一束对象并且给它们发送消息。当然,问题是勾画出我们的对象和消息是什么,但如果完成了这些,c++的实现就直接了当了。

10)在c++中的结构长度和c中等价版本的长度相同。c++尽力不增加任何花费。

11)头文件的形式

当我第一次学习用c语言时,头文件对我是神秘的。许多有关c语言的数似乎不强调它,并且编译器也不强调函数声明,所以它在大部分时间内似乎是可要可不要的,除非要声明结构式。在c++中,头文件的使用变得非常清楚。它们对于每个程序开发是强制的,在它们中放入非常特殊的信息:声明。头文件告诉编译器在我们的库中那些事可用的。因为对于cpp文件能够不要源代码而使用库(只需要对象文件或库文件),所以头文件是存放借口规范的唯一地方

头文件是库的开发者与它的用户之间的合同。它说:“这里描述的是库能做什么”,它不说如何做,因为如何做存放在cpp文件中,开发者不需要分发这些描述“如何做”的源代码给用户。

该合同描述数据结构,并说明函数调用的参数和返回值。用户需要这些信息来开发应用程序,编译器需要它们来产生相应的代码。(联系自己学c的时候的stdio.h头文件,就可以知道我们知道如何用printf函数,却不知道内部是如何实现的,就是上述过程的一个具体体现)

编译器强迫执行这一合同,也就是要求所有的结构和函数在它们使用之前被声明,当它们是成员函数时,在它们被定义之前被声明。这样,就强制把声明放在头文件中并把这个头文件包含在定义成员函数的文件(date.cpp)和使用它们的头文件(client.cpp)中。 因为描述库的单个头文件被包括在整个系统中,所以编译器能保证一致和避免错误。

为了恰当地组织代码和写有效的头文件,有一些问题必须知道。第一个问题是将什么放进头文件中,基本规则是“只声明”,也就是说,对于编译器只需要一些信息以产生代码或创建变量分配内存。这样可以防止头文件包含在几个处理单元中时,如果内存分配不止一个地方,避免连接器产生多重定义错误。

第二个问题是重复声明,c和c++都允许对函数重复声明,只要这些重复声明匹配,但绝不允许对结构重复声明。

12)嵌套结构

在全局名字空间之外为数据和函数取名字是有好处的。在类里面再加一个类就是一个所谓的嵌套结构,举个例子

class stack {
class link {
void* data;
link* next;
void init(void* Data, link* Next);
} *head;


我们可以这样来定义嵌套结构的成员,简单地两次使用范围分解运算符,以指明这个嵌套class的名字,即

void stack::link::init (void* Data, link* Next) {
blabla ....
}


13) 希望用范围分解指定一个全局名字(而不是成员与之同名的变量时),应当使用前面不带任何东西的运算符。 for example :

void S::f () {
::A++;  // select the global A
A--;   //  the A at the class
}


小结:在这一章中,我们已经学会了使用c++的基本方法,也就是在结构的内部放入函数。这种新类型被称为抽象数据类型,用这种结构创建的变量被称为这个类型的对象或实例。想对象调用成员函数被称为想这个对象发消息。面向对象的程序设计中的主要活动就是向对象发消息。

(不定期更新:))
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: