C++学习笔记(1)——初识C++

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/bjtuwayne/article/details/82820925

学习一门语言当然要从认识一门语言开始。

一、背景

世界上第一种计算机高级语言是诞生于1954年的FORTRAN语言。之后出现了多种计算机高级语言。1970年,AT&T的Bell实验室的D.Ritchie和K.Thompson共同发明了C语言。研制C语言的初衷是用它编写UNIX系统程序,因此,它实际上是UNIX的“副产品”。它充分结合了汇编语言和高级语言的优点,高效而灵活,又容易移植。

1979年,剑桥大学计算系的Bjarne Stroustrup到了Bell实验室,开始从事将C改良为带类的C(C with classes)的工作。1983年该语言被正式命名为C++。我们可以到http://www.stroustrup.com/index.html下去一睹老爷子风采:

“I designed and implemented the C++ programming language. To make C++ a stable and up-to-date base for real-world software development, I stuck with its ISO standards effort for 25 years (so far).”


二、发展历程

Bjarne Stroustrup把C++的发展历程分为了2个阶段:

第一阶段:(1979-1991)早期设计阶段:

1983年8月,第一个C++实现投入使用。

1983年12月,Rick Mascitti建议命名为CPlusPlus,即C++。1985年2月, 第一个C++ Release E发布。

10月,CFront的第一个商业发布,CFront Release 1.0。

10月,Bjarne博士完成了经典巨著The C++ Programming Language第一版

1986年11月,C++第一个商业移植CFront 1.1,Glockenspiel。

1987年2月,CFront Release 1.2发布。

11月,第一个USENIX C++会议在新墨西哥州举行。

1988年10月,第一次USENIX C++实现者工作会议在科罗拉多州举行。

1989年12月,ANSI X3J16在华盛顿组织会议。

1990年3月, 第一次ANSI X3J16技术会议在新泽西州召开.

5月, C++的又一个传世经典ARM诞生。

7月, 模板被加入。

11月,异常被加入。

第二阶段(1991-2006)进化成为实用语言阶段(标志性事件是1991年成立了ISO 标准组织,1998年通过了ISO 标准审定):

1991年6月, The C++ Programming Language第二版完成。

6月, 第一次ISO WG21会议在瑞典召开。

10月,CFront Release 3.0发布。

1993年3月,运行时类型识别在俄勒冈州被加入。

7月,名字空间在德国慕尼黑被加入。

1994年8月,ANSI/ISO委员会草案登记。

1997年7月,The C++ Programming Language第三版完成。

10月,ISO标准通过表决被接受

1998年11月,ISO标准被批准。形成第一个标准C++98.

2003年C++第二次标准化,这是一次技术性修订,修订错误,减少多义性,但没有改变语言特性。此版本被称为C++03.

2011年9月的C++第三次标准化(C++11)。


三、特性总结

Bjarne Stroustrup当年的编写C++的目的就是“为了我的朋友和我不再使用汇编语言、C语言和其他现代高级语言来编程来设计的,它的功能是可以更方便的编写出好的程序,让每个程序员更加快乐”。老爷子在个人主页的C++页面中如此介绍它的C++:

C++ is a general purpose programming language with a bias towards systems programming that

is a better C

supports data abstraction       //数据抽象

supports object-oriented programming      //oop:面向对象

supports generic programming.    //gp:泛型编程

所以有人说C++是带类的C;有人说C++是面向对象编程语言;有人说C++是面向过程与面向对象结合的语言。类似的评论网上有很多,虽然正确,却片面,是断章取义之言。

1983年C++诞生之时,由于兼容C语言而天生拥有了面向过程编程的能力;

1989年推出的2.0版,C++完善了对面向对象编程范式的支持;

1993年的3.0版,C++中引入了模板(template),有了泛型编程的雏形;

1998年的C++第一次标准化,为支持著名的C++标准库中的STL,对泛型编程有了更完善的支持,并且发现了模板元编程;

2011年9月的C++第三次标准化(C++11),进一步加强了泛型编程、模板元编程和并发编程的能力,也进一步加强了直接对硬件的操作能力,并且由于lambda的出现和早就出现在boost库中的bind/function等C++自扩展的组件,C++也拥有了一定程度上的函数式编程能力。

1.面向过程

面向过程(Process Oriented)这个词是在面向对象(Object Oriented)出现之后为与之相对而提出的。其实它在以前基本被叫做“结构化编程”。

面向过程开发方式是对计算机底层结构的一层抽象,它明显把程序的内容分为数据和操纵数据的操作两部分。这种编程方式的核心问题是数据结构和算法的开发和优化。C语言所提供的机制就是典型的结构化编程设施。简单来说面向过程是将问题分解成若干步骤(动作),每个步骤(动作)用一个函数来实现,在使用的时候,将数据传递给这些函数。

网上有一个典型的例子:把大象放入冰箱里面。

面向过程的C语言是这么做的:首先打开冰箱门,然后把大象放入进去,最后关闭冰箱门。

main()
{
    open();
    lay(elephant);
    close();
}

C++可以认为是C语言的超集,所以C++自然也具备C语言这种面向过程的特性。而且这在当前很多C++语言开发的项目中都有体现。

2.面向对象

面向对象是继结构化革命之后的又一次软件开发方式革命。面向对象的主要思想是基于抽象数据类型的(Abstract Data Type, ADT):在结构化编程过程中,人们发现把某种数据结构和用于操纵它的各种操作以某种模块化方式绑定到一起会非常方便,使用这种方式进行编程时数据结构的接口是固定的。

如果对抽象数据类型进一步抽象,就会发现把这种数据类型的实例当作一个具体的东西、事物、对象,就可以引发人们对编程过程中怎样看待所处理的问题的一次大的改变。抽象数据类型方法虽然也有一定的抽象能力,但其核心仍然是数据结构和算法。而面向对象方法直接把所有事物都当作独立的对象,处理问题过程中所思考的不再主要是怎样用数据结构来描述问题,而是直接考虑重现问题中各个对象之间的关系。可以说,面向对象革命的最重要价值就在于改变了人们看待和处理问题的方式。

同样是把大象放冰箱。C++面向对象是这么做的:涉及到两个对象:冰箱和大象。三个动作:打开冰箱,放置大象,关闭冰箱。首先定义一个冰箱类,他有打开的方法,放置的方法,关闭的方法。然后再定义一个大象类。接下来构建冰箱和大象的对象,然后冰箱对象调用打开门的方法,冰箱对象再调用放置大象对象的方法,最后冰箱对象关门。

Fridge{ open();lay();close();}//冰箱类有三个方法
main()
{
    Elephant   elephant;//构建一个大象对象
    Fridge  fridge;//构造一个冰箱对象
    fridge.open();
    fridge.lay(elephant);
    fridge.close();
}

所以面向对象首先需要从用户的角度考虑对象,描述对象所需的数据和用户与数据交互所需的操作,抽象出“类”的概念,进而设计这个类。

网上还有一个用五子棋描述面向过程和面向对象思想的例子:

面向过程的设计思路就是首先分析问题的步骤:

1、开始游戏,

2、黑子先走,

3、绘制画面,

4、判断输赢,

5、轮到白子,

6、绘制画面,

7、判断输赢,

8、返回步骤2,

9、输出最后结果。

把上面每个步骤用分别的函数来实现,问题就解决了。

面向对象的设计则是从另外的思路来解决问题。整个五子棋可以分为:

1、黑白双方,这两方的行为是一模一样的,

2、棋盘系统,负责绘制画面,

3、规则系统,负责判定诸如犯规、输赢等。

第一类对象(玩家对象)负责接受用户输入,并告知第二类对象(棋盘对象)棋子布局的变化,棋盘对象接收到了棋子的变化就要负责在屏幕上面显示出这种变化,同时利用第三类对象(规则系统)来对棋局进行判定。

可以明显地看出,面向对象是以功能来划分问题,而不是步骤。同样是绘制棋局,这样的行为在面向过程的设计中分散在了总多步骤中,很可能出现不同的绘制版本,因为通常设计人员会考虑到实际情况进行各种各样的简化。而面向对象的设计中,绘图只可能在棋盘对象中出现,从而保证了绘图的统一。

功能上的统一保证了面向对象设计的可扩展性。比如我要加入悔棋的功能,如果要改动面向过程的设计,那么从输入到判断到显示这一连串的步骤都要改动,甚至步骤之间的循序都要进行大规模调整。如果是面向对象的话,只用改动棋盘对象就行了,棋盘系统保存了黑白双方的棋谱,简单回溯就可以了,而显示和规则判断则不用顾及,同时整个对对象功能的调用顺序都没有变化,改动只是局部的。

3.泛型编程

泛型编程旨在编写出独立于数据类型的代码。泛型编程(Generic Programming)是一种语言机制,通过它可以实现一个标准的容器库(如Vector)。像类一样,泛型也是一种抽象数据类型,但是泛型不属于面向对象,它是面向对象的补充和发展。在面向对象编程中,当算法与数据类型有关时,面向对象在对算法的抽象描述方面存在一些缺陷。比如,C++中常见的重载现象,用不同类型的形参来区分函数功能:

int func(int a, int b){return a+b;}

double func(double a, double b) {return a+b;}

如果类型很多,势必导致函数过于繁琐。更重要的是函数重载是静态编译,运行时占用过多内存。泛型编程的思想即创建一种适应所有类型的函数,这样便大大减少了编码量。

泛型在C++中的主要实现为模板函数和模板类。

在此我们可以用C++的模板函数来表达通用型的函数,如下:

template<typename T> // 模板声明

T func(T a,T b) { return a+b; } // 注意形参和返回值的类型

这时C++编译器会根据add函数的参数类型来生成一个与之对应的带具体参数类型的函数并调用。

又如模板类:对栈的描述:

class stack
{
    push(参数类型)  //入栈算法
    pop(参数类型) //出栈算法
}

如果把上面的伪代码看作算法描述,没问题,因为算法与参数类型无关。但是如果把它写成可编译的源代码,

就必须指明是什么类型,否则是无法通过编译的。使用重载来解决这个问题,即对N种不同的参数类型写N个push和pop算法,这样是很麻烦的,代码也无法通用。

若对上面的描述进行改造如下:

首先指定一种通用类型T,不具体指明是哪一种类型。

class stack<参数模板 T>
{
    push(T)   //入栈算法
    pop(T)  //出栈算法
}

这里的参数模板T相当于一个占位符,当我们实例化类stack时,T会被具体的数据类型替换掉。

若定义对象S为statc类型,在实例化S时若我们将T指定int型则:

这时候类S就成为:

class S
{
    push(int)//入栈算法
    pop(int) //出栈算法
}

这时我可以称class stack<参数模板 T>是类的类,通过它可以生成具体参数类型不同的类。


至此,可能对上述某些术语理解的仍然不到位,不过没关系,我相信在后续的学习中会对以上特性有更深刻的理解。

猜你喜欢

转载自blog.csdn.net/bjtuwayne/article/details/82820925
今日推荐