C++程序设计基础知识及基本数据和复合数据

内容

一、预备知识

程序是如何在计算机上运行的

  • 人与计算机的交流语言:当双方进行交流的时候,我们的语言成为了交流的主体模式。中国人与中国人交流,自然用中文就能让双方听得懂,但是中国人与外国人交流,则双方都讲自己国家的语言自然是没办法交流的,因此要么中国人学习外语,要么外国人学习中文。然而我们还可以引入第三方,找一位翻译。因此,人与计算机沟通,计算机是没办法理解人的语言的,但另外一个问题是人去学习**计算机语言(机器语言,0和1组成的语言,二进制文本)**也是很困难的,那么最简单的方法就是找“一位翻译”,这样的话更多的人不用花费时间与精力去学习机器语言,而是学习高级语言,然后被翻译成机器语言,故而学习计算机就更为大众化,让更多人能够理解。

  • 因此程序员设计出了计算机高级语言,方便大家与计算机打交道。而这设计出的语言,是怎么与计算机沟通的呢?因此需要通过以下流程实现转换。

  • 人类—>高级语言(接近于人类的自然语言)—>汇编语言(用英文字母和数字表示指令)—>机器语言(0和1组成的指令)—>计算机

  • 虽然我们可以通过学习汇编语言来实现对机器语言的转换,但是学习汇编还是必须要理解计算机的基本实现方式,简而言之汇编语言也有一定的难度,所以设计者又设计出了一种语言叫计算机的高级语言,这种语言的目的是让人能更方便去表达想让计算机执行的操作,更容易去编写。

  • 而对于高级语言来说,又分为面向过程(POP-(Procedure-Oriented Programming))与面向对象(OOP-(Object-Oriented Programming)。面向过程:是以计算机的实现方式为基础来设计的语言,加工的方式是以过程化来解决问题的,以过程为中心的编程思想;面向对象:以人为本的一种计算机语言,以人的思维方式,人理解的方法来编写的语言,以类和对象为中心的编程思想。从概念上来说,过程性语言,它强调的是编程的算法设计,首先要确定计算机应采取什么操作,然后使用编程语言来实现这些操作。程序命令计算机按照一系列流程生成特定的结果,就像菜谱指定了厨师做蛋糕时应遵循的一系列操作一样,每一步都需要自己去完成,对对象进行的操作程序需要自己用算法去编写;而对象性语言,它强调的是处理的对象也就是数据,确定了具体要处理的对象,只需要将这个具体实际对象用对应的对象方法去解决即可,这个对应的算法就是这种面向对象语言本身所提供的,因此这种面向对象(OOP)试图让语言来满足问题的要求。

  • 刚刚所说的能将高级语言翻译成机器语言的“翻译”,它就是编译程序。因此我们将高级语言所写的代码叫源代码,程序叫源程序。

  • 现在我们将上面所说的高级语言转化为机器语言的整个步骤划分如下:

    1. 使用文本编辑器编写程序,并将其保存到文件中,这个文件就是程序的源代码。
    1. 编译源代码这意味着运行一个程序,将源代码翻译成计算机使用的内部语言——机器语言。(大多数编译程序能直接产生机器语言目标程序,形成可执行的目标文件。但也有一些编译程序是先产生汇编语言级的符号代码文件,再调用汇编程序进行翻译加工,最后形成可执行的机器语言目标程序。)包含了翻译后的程序的文件就是程序的目标代码(object code)。
    1. 将目标代码与其他代码链接起来C++库包含一系列计算机例程(被称为函数)的目标代码。链接(Link)指的是将各源文件目标代码同使用的函数的目标代码以及一些标准的启动代码(startup code)组合起来,生成程序的运行阶段版本。包含该最终产品的文件被称为可执行代码。
  • 经过上述三个步骤也就形成了目标程序(可执行代码),即实现了高级语言到机器语言,源代码到可执行代码的转化。以C语言为例来说就是源代码经过翻译过程中的编译这个步骤最终形成了可执行代码。其中翻译环境的操作如下:翻译分为两个步骤:编译(compile)链接(link)。 而编译过程也分为两个步骤:预编译(预处理 preprocessor)解析(parse)编译预处理:是在源代码上执行一些文本操作,通常是带#号的代码,包括读入include指令包含的文件的内容(该文件包含了指定的函数的声明)以及define宏定义的宏替换,#define编译指令的工作方式与文本编辑器中的全局搜索并替换命令相似,预处理器查找独立的标记,跳过嵌入的单词。修改后的程序进入 编译(解析) 模式:即判断它的语句的意思。 编译后便形成了目标代码。 然后各个目标文件由链接器捆绑在一起,形成一个单一而完整的可执行程序。

以下是来自知乎上的回答截图,详细地解释了整个编译过程:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以上来自于知乎:https://www.zhihu.com/question/389126944/answer/1169709964


二、开始学习C++

C++程序的框架和组成要素

#include<iostream>
int main(void)
{
    
    
   using namespace std;
   cout<< "hello world";
   cout<< endl;
   cout<< "Come up and c++ me some time."<< endl;
   return 0;
}

首先介绍一下显示消息的简单C++程序,其中C++对大小写敏感,也就是说区分大写字符和小写字符。

从这个简单的小程序可以看出,一个C++程序的基本组成,是由基本字符集和执行字符集组成,基本字符集包含了ASCII字符集,执行字符集与基本字符集大同小异,只是将不能从键盘输入的字符换成了转义字符序列。而这些基本字符,如字母,下划线又组成了C++预定义的保留字(关键字),你也可以用这些字母,数字,下划线组成自己定义的标识符,词构成词组,词组通过关键字,标识符由操作符,控制符等等衔接形成了语句,语句形成段落构成了函数,最后所有函数模块形成一个程序。

2.1 main函数

  • 函数头:int main() :函数头对函数与程序其他部分之间的接口进行了总结;
  • 作为接口的函数头:
    1. C++句法要求main()函数的定义以函数头int main()开始。main()是程序预定义的函数,编译器默认从main()函数名作为程序入口进入,作为程序出口结束。
    1. 函数可以被其他函数激活或调用,函数头描述了函数与调用它的函数之间的接口。位于函数名前面的部分叫作函数的返回类型,它描述的是从函数返回给调用它的函数的信息;函数名后括号中的部分叫作形参列表或参数列表,它描述的是从调用函数传递给被调用函数的信息。这种通用的格式用于main()时让人感到迷惑,因为程序不允许内部调用main函数,通常main()被启动代码调用,而启动代码是由编译器链接到程序中的,是程序和操作系统(UNIX,Windows或其他操作系统)之间的桥梁。简而言之,该函数头描述的是main()和操作系统之间的接口。其返回值为int型用于说明程序的退出状态,意思是如果程序正常运行到main函数结束,则返回0给操作系统,告诉系统程序正常运行完毕,如果返回非零整数则代表程序异常退出;其括号内实际上是有参数的,称为命令行参数。
    1. 在函数头的括号中若使用了关键字void,则明确指出该函数不接受任何参数。在C++中,让括号空着与在括号中使用void等效,都是明确指出该函数不接受任何参数;但与C不同,C中让括号空着意思是对是否接受参数保持沉默。。
    1. 若返回值的位置为void,则意味着函数不返回任何值。通常这类函数被调用以后,所有该完成的工作和显示都在该函数内部完成。(在其他编程语言中有的称这样的函数为过程。)
    1. 最后,ANSI/ISO C++标准对哪些抱怨必须在main()函数最后包含一条返回语句过于繁琐的人做出了让步:如果编译器到达main()函数末尾没有遇到返回语句,则认为main函数以如下语句结尾:return 0; 这条隐含返回值的语句只适用于main()函数,而不适用于其他函数!
    1. C++程序必须包含一个main()函数,程序从调用main()开始,从main()调用结束。
  • 函数体:花括号中的部分叫作函数体 ,指出函数应该作什么计算机指令。

2.2 语句

  • 语句每条完整的指令都称为语句。语句是要执行的操作。为理解源代码,编译器需要知道一条语句何时结束,另一条语句何时开始,C++同C的处理方式一样,都使用终止符来实现语句的分隔。终止符是一个分号(;),它是语句的结束标记,是语句的组成部分。
  • 常见的两种语句:声明语句和赋值语句
  • 声明语句
    1. 计算机是一种精确的,有条理的集器。**要将信息项存储在计算机中,必须指出信息的存储位置和所需的内存空间。在C++中,完成这种任务的一种相对简便的方法,是使用声明语句来指出存储类型并提供位置标签。**如:*int carrots;*这句定义声明语句提供了两项信息:需要的内存以及该内存单元的名称。具体来说,这条语句指出程序需要足够的存储空间来存储一个整数。编译器第一项任务负责分配和标记内存的细节(即这里编译成机器语言以后告诉操作系统在这个地方需要分配内存了,并且需要分配四个字节大小),以及第二项任务是给存储单元指定名称。(此后程序将使用名称carrots来标识存储在该内存单元中的值。)
    1. 在这个例子中,声明语句是定义,但通常声明语句不一定是定义,可以是函数原型的声明等等。函数原型的声明,它们告诉编译器这些以后将在源文件中定义的函数的特征。这样,当这些函数被调用时,编译器就能对它进行准确性的检查。
  • 赋值语句
    1. 赋值语句将值赋给存储单元。 如:carrots=25; 符号=叫做赋值运算符。并且在C++中可以连续使用赋值运算符,如:a=b=c=10; 赋值将从右向左进行。这也是继承了C语言的风格。
  • 通常我们将声明语句与赋值语句同时使用,如:int carrots=25; 这样的话在声明变量的内存分配,存储空间细节的同时将值赋给了它的存储单元中,这种语句叫作变量的初始化。

2.3 使用cout进行C++输出

  • 我们先来看一下如何显示消息:
  • cout << “Come up and C++ me some time.”;
  • 双引号括起来的部分是要打印的消息。在C++中,用双引号括起来的一系列字符叫作字符串(由若干个字符组合而成,包括一段文字,数字,英文,符号等等。)<<符号表示该语句把这个字符串发送给cout;该符号指出了信息流动的路径。因此<<,>>也称为流运算符。 cout是什么呢?它是一个预定义的对象,知道如何显示字符串、数字和单个字符等等(对象是类的实例,而类定义了数据的存储方式和使用方式)cout有一个简单的接口,它可以将<<该符号右侧的信息插入到输出流中。简单来说我们可以将cout看作屏幕,而<<是流运算符,也就是将<<右侧信息流向了屏幕输出
  • 控制符endl
  • cout << endl;
  • endl是一个特殊的C++符号,表示一个重要概念:重启一行。==在输入流中插入endl将告诉编译器重启一行,导致屏幕光标移到下一行开头。==诸如endl等对于cout来说有特殊含义的特殊符号被称为控制符。
  • 换行符\n
  • C++还提供了另一种在输出中指示换行的旧方法:C语言中的转义字符\n:
  • cout << “what’s next?\n”;
  • \n被视为一个字符,名为换行符。
  • 通常,我们写程序的时候在输出一串字符的情况下用\n更快捷,在输出数值或者留空白行等其他情况下用endl更方便。
  • cout语句
    1. cout语句除了上述可以输出一串字符以外(类似于C语言中的printf),还能直接输出数值。如:cout << carrots; 程序没有打印carrots而是打印存储在carrots存储单元中的值,即25。我们知道,根据ASCII码,数值的25和符号的’25’实质上存储的二进制值不是同一个值,可是为什么cout依然输出了25呢?实际上,cout将两个操作合二为一了。首先,cout先判断<<右侧输入流中的信息,判断出没有双引号则表明该数据不是字符类型,而是已经定义并赋值好的标识符carrots,并以此标准把carrots替换成其内存单元的值25;随后将数值25转换为合适的字符’25’插入到输出流中输出。(cout很聪明,知道carrots是一个需要转换的整数,cout也很智能,将数值25转换成了字符25输出,这种只能行为来自于C++的OOP特性。实际上,C++插入运算符<<将根据其后的数据类型相应地调整其行为,这是一个运算符重载的例子。这种运算符重载,使得cout能够识别和显示所开发得新数据类型。)
    1. cout语句的拼接:
#include<iostream>
using namespace std;
void main()
{
    
    
    /*****第一种*****/
    cout << "Now you have" << carrots << "carrots."<<endl; //打印:Now you have 25 carrots.
    //这样能将字符串输出和整数输出合并为一条语句。

    /*****第二种*****/
    cout << "Now you have"
         << carrots
         << "carrots"
         << endl;
    //注意区别:前面三句话后面都没有分号,而分号表示该cout的输入流对象结束。因此前面三句都没有结束,因此和最后一句都属于同一个cout的输入流,只有遇到;才会结束输入流。
    
    /*****第三种*****/
    cout << "Now you have";
    cout << carrots;
    cout << "carrots";
    cout << endl;
    //注意这里每句话都带有分号,因此这四个cout输出语句都是独立的,C++与C语言不同的是,允许使用第二种,第三种这样的拼接语句来输出。
}
    1. 另外,cout默认将输入的整型字面常量(如十进制 28,二进制0101,八进制012,十六进制0x73等)流,转换成十进制整型输出到屏幕上,如果要输出其他进制类型的整型值,则需要对其输出模式进行修改,修改输入输出对象的头文件名为iomanip(In/Out Manipulate)。

2.4 源代码中的标记和空白

  • 一行代码中不可分割的元素叫标记(token)。通常,必须用空格、制表符、或者回车将两个标记分开,空格,制表符和回车统称为空白。这些空白将关键字以及用户标识符等标记分开,使编译器能读懂你的语言,这里是声明一个int变量,接着这个变量原来叫作carrots。

2.5 使用cin进行C++输入

  • 使用cin:
  • cin >> carrots; 该语句的意思是:从键盘输入的值从cin流向carrots 显然,对这以过程有更为正式的描述:就像C++将输出看作流出程序的字符流一样,它也将键盘的输入看作是流入程序的字符流。输入时,cin使用>>运算符从输入流种抽取字符,右侧的变量用以接收抽取的信息。
  • 与cout一样,cin也是一个智能对象。它可以将通过键盘输入的一系列字符转换为接收信息的变量能够接收的形式。具体操作如下:cin可以看作是键盘,将键盘输入的值通过>>流运算符,流运算符在操作系统中给系统下达指令,去根据地址和变量名寻找到变量的存储单元,然后再将缓冲区的数据流入到变量的存储单元中。除此之外,在程序执行终端进行enter的键击操作,被执行器认为是换行,因此实际我们操作的过程中,键盘输入了值加回车,值被赋予给了变量,流入了其存储单元,cin检测到缓冲区的回车则结束读取,回车依然留在缓冲区,同时回车的键击操作被终端执行器默认指令换行。
  • cin支持>>的连续使用,>>每遇到后面第一个对象,则将其对象所能接受的单个数据流入其内存单元,想要一次性将键盘输入的内容流入多个变量对象,则需要多个>>连接起来,将信息依次对应流入所连接的变量中。 例如键盘输入一个算术表达式如3+5,10/5等等,则需要定义三个变量int a,b;char op; 然后利用该性质直接输入到三个变量存储单元中:cin >> a >> op >> b; 则此时a=3,op=’+’,b=5。

2.6 类简介

  • 类是用户定义的一种数据类型。 要定义类,需要描述它能够表示什么信息和可对数据执行哪些操作。类定义描述的是数据格式及其用法,而对象则是根据数据格式规范创建的实体。 C++类对应某些语言中的对象的数据类型,而C++对象则对应对象实例或实例变量。
  • 在这里插入图片描述
  • 在这里插入图片描述
  • 在这里插入图片描述
  • 在这里插入图片描述
  • 在这里插入图片描述
#include <iostream>
 
using namespace std;
 
class Box
{
    
    
   public:
      double length;   // 长度
      double breadth;  // 宽度
      double height;   // 高度
      // 成员函数声明
      double get(void);
      void set( double len, double bre, double hei );
};
// 成员函数定义
double Box::get(void)
{
    
    
    return length * breadth * height;
}
 
void Box::set( double len, double bre, double hei)
{
    
    
    length = len;
    breadth = bre;
    height = hei;
}
int main( )
{
    
    
   Box Box1;        // 声明 Box1,类型为 Box
   Box Box2;        // 声明 Box2,类型为 Box
   Box Box3;        // 声明 Box3,类型为 Box
   double volume = 0.0;     // 用于存储体积
 
   // box 1 详述
   Box1.height = 5.0; 
   Box1.length = 6.0; 
   Box1.breadth = 7.0;
 
   // box 2 详述
   Box2.height = 10.0;
   Box2.length = 12.0;
   Box2.breadth = 13.0;
 
   // box 1 的体积
   volume = Box1.height * Box1.length * Box1.breadth;
   cout << "Box1 的体积:" << volume <<endl;
 
   // box 2 的体积
   volume = Box2.height * Box2.length * Box2.breadth;
   cout << "Box2 的体积:" << volume <<endl;
 
 
   // box 3 详述
   Box3.set(16.0, 8.0, 12.0); 
   volume = Box3.get(); 
   cout << "Box3 的体积:" << volume <<endl;
   return 0;
}
  • 类描述了一种数据类型的全部属性(包括可使用它执行的操作),对象是根据这些描述创建的实体。类描述指定了可对类对象执行的所有操作。要对特定对象执行这些允许的操作,需要给该对象发送一条消息。C++提供了两种发送消息的方式:第一种方式是使用类方法(本质上就是函数的调用);第二种就是重新定义运算符,cin和cout采用的就是这种方法,将<<运算符重新定义成流运算符,也就是将“显示消息”发送给cout。
  • 知道类是用户定义的类型,但作为用户,并没有设计ostream类和istream类。就像函数可以来自函数库一样,类可以来自类库。ostream和istream类就属于这种情况。从技术上说,它们没有被内置到C++语言中,而是语言标准指定的类。(拿C语言举例,输入输出并没有内置到C语言中像定义变量那样直接使用,而是要调用I/O函数库中的printf和scanf函数)这些类定义位于iostream文件中,没有被内置到编译器中。

2.7 函数简单介绍

  • 由于函数用于创建C++程序的模块,对C++的OOP定义至关重要,因此在这里先简单介绍一下。
  • C++函数分两种:有返回值的和没有返回值的
  • 有返回值的函数
    1. 有返回值的函数将生成一个值,而这个值可赋给变量或在其他表达式中使用。
    1. 没有返回值的函数在被称为过程,也就是所有需要执行的操作过程都由自己内部完成。
  • 函数变体
  • **还有一种函数不接受任何参数。例如,有一个C库(位于stdlib.h头文件中)包含一个rand()函数,该函数不接受任何参数,并返回一个随机整数。其原型如下:int rand(void); **

2.8 关键字

  • 关键字是计算机语言中的词汇,把高级语言的基本结构和语句与低级语言的实用性结合起来。关键字是电脑语言里事先定义的,有特别意义的标识符,有时又叫保留字。

2.9 位与字节

  • 计算机内存由一些叫作位(bit)的单元组成。因此位是计算机内存的基本单元。可以将位看作电子开关,可以开,也可以关。关表示值0,开表示值1。字节(byte)通常指的是8位的内存单元,也就是说以8位二进位为一个内存单元,是描述计算机内存量的度量单位,1KB等于1024字节,1MB等于1024KB。计算机中的CPU位数指的是CPU一次能处理的最大位数。例如32位计算机的CPU一个机器周期内可以处理32位二进制数据的计算。
  • 一般来说,字节需要至少能够容纳基本字符集,在美国,基本字符集通常是ASCII和EBCDIC字符集,它们都可以用8位来容纳,所以在使用这两种字符集的系统中,C++字节为8位,因为ASCII码中基本字符集所容纳的数值为0127,而带符号的8位能容纳的数值范围是-128127(2的7次方,最高位0和1分别代表正负),不带符号则能容纳0~255(2的8次方)。因此如果声明一个字符变量,char ch;则编译器会翻译成为其分配一块1字节的内存单元并给该1字节的内存以ch标记,同理int carrots;则编译器会翻译称为其分配一块4字节的内存单元,并将这4字节一整块的内存以carrots标记。
  • 要想知道某个系统中某数据类型的最大长度,可以用C++内置语言来检测类型长度。sizeof运算符返回类型或变量的长度,单位为字节。(运算符是内置的语言元素,对一个或多个数据进行运算,并生成一个值。)

三、处理基本数据

  • 面向对象编程(OOP)的本质就是设计并扩展自己的数据类型。设计自己的数据类型就是让类型与数据匹配,方便对应的数据对象直接使用,如类库中有box类,则当我们需要处理关于box的变量时,直接调用box类库,这个类库里包含了box想要的基本数据例如长宽高,以及对box进行的操作例如计算面积体积等函数,而不需要重新编写相应算法及程序;这就是与面向过程编程(POP)最大的不同,面向过程只能使用C语言内置的数据类型,通过不同的算法来实现用这些基本数据类型完成相应的操作。还是例如我们此时需要算一个盒子的体积,我们则需要定义三个变量分别代表盒子的长宽高,以及设计一个函数来被调用算体积,这些所有的操作都需要你亲自编写代码来实现,而面向对象则只需要实体变量调用成员函数,你要做的只是输入数据就行,函数早已经在类中帮你设计好了。
  • 然而,在创建自己的类型之前,必须了解并理解C++内置的类型,因为这些类型是创建自己类型的基本组件。C++内置类型分为两组:基本类型和复合类型。
  • 基本数据类型分为两种:整型和浮点型。(因为字符型也属于一种小整型)

3.1 整型

  • 整型就是没有小数部分的数字。
    1. 宽度(width)用于描述存储整数时使用的内存量。不同的整型类型,所使用的内存量也不同,也就是可以表示的数值范围不同。
    1. char类型有一些特殊属性(它最常用来表示字符,而不是数字)。
    1. 16位(2个字节)的int取值范围为-32768~+32767.
    1. 无符号类型(关键字unsigned修饰,如果省略符号类型,则默认为有符号signed),下面介绍的4种整型都有一种不能存储负数值得无符号变体,其优点久是可以增大变量能够存储的正向范围。例如,如果short表示的范围为-32768到+32767,则无符号版本unsigned short表示的范围为0~65535。当然,仅到数值不会为负时才应用无符号类型,如人口等。因此在隐式声明时,编译器默认数据都是有符号类型)(signed)这样更自然,更贴近真实。
    1. 选择整型类型:C++提供了大量的整型,应该使用哪种类型呢?通常,int被设置为对目标计算机而言最为“自然”的长度。自然长度(natural size)指的是计算机处理起来效率最高的长度。如果没有非常有说服力的理由选择其他类型,则应使用int。

整型 short、int、long和long long

如果在所有的系统中,每种类型的宽度都相同,那么使用起来会非常方便。但生活并非那么简单,没有一种选择能够满足所有的计算机设计要求。因此C++提供了一种灵活的标准,它确保了最小长度,如下所示:

    • short至少16位
    • int至少和short一样长;(在16位系统中int跟short一样为2字节,而32位和64位系统中,int为4字节)
    • long至少32位,且至少与int一样长;
    • long long至少64位,且至少与long一样长。

3.2 字符型

  • char类型是专为存储字符(如字母和数字)而设计的。编程语言通过使用字母的数值编码解决了存储字母的问题。因此,char类型实质上是另一种整型。它足够长,定义char为1个字节宽度,能够表示目标计算机系统中的所有基本符号——所有的字母,数字,标点符号等。实际上,很多系统支持的字符都不超过128个,因此用一个字节久可以表示所有的符号,很节省内存空间。在计算机内部都以二进制形式存储,字符当然也不例外,因此C++字符集都对应一个统一的数值编码来对应,即ASCII字符集,故char实质是小整型,通过数值编码对应存储到计算机中,再通过翻译数值编码进行格式转换输出对应的字符。
    • 例如:cin >> ch; cout << ch; 有趣的是,程序中用键盘输入的是字符M,而不是对应的字符编码77,另外,程序将打印M,而不是77。通过查看内存可以知道,77是存储在变量ch中的值。而这个转换工作来自cin和cout:输入时,cin将键盘输入的M转换为77;输出时,cout将值77转换为所显示的字符M;因此cin和cout的行为都是由变量类型引导的。
    • C++对字符用单引号,对字符串用双引号。

cout.put()函数——成员函数

    • cout.put()函数用来显示一个字符。如:cout.put(‘M’);==由于类定义了如何表示和控制数据。而成员函数归类所有,描述了操纵类数据的方法。cout.put()成员函数提供了另一种显示字符的方法,可以代替<<运算符。==在重载运算符<<如此方便的情况下,为何需要cout.put()函数呢?答案与历史有关。在C++的Release 2.0之前,cout将字符变量显示为字符,而将字符常量(如’M’和’N’)显示为对应的数值编码。问题是,C++的早期版本和C一样,也把字符常量存储为int类型。因此在Release 2.0之后,C++将字符常量存储为char类型,而不是int类型。这意味着cout现在可以正确处理字符常量。(因此由此可见为什么cout可以准确将carrots输出其值,因为<<会将数据类型信息也传送给cout,cout判断完信息数据类型以后再作相应操作处理。)

char字面值

      1. 在C++中,书写字符常量的方式有多种。**对于常规字符(如字母,标点符号和数字),最简单的方法是将字符用单引号括起来。**这种表示法代表的是字符的数值编码;但有些字符不能直接通过键盘输入到程序中,例如:在写代码时,按回车键并不能使字符串包含一个换行符;相反,程序编辑器将这种键击解释为源代码中开始新的一行。其他一些字符也无法从键盘输入,因为C++赋予了它们特殊的含义,当你从键盘输入的时候会被编辑器理解为默认的特殊含义。例如,双引号字符用来分隔字符串字面值,因此不能把双引号放在字符串字面值中。对于这些字符,C++提供了一种特殊的表示方法——转义序列。例如:cout << “Enter your agent code:________ \b\b\b\b\b\b”;打印完下划线以后,程序使用退格字符将光标退到第一个下划线处。读者可以输入自己的密码,并继续。
  • 通用字符名
      1. C++实现支持一个基本的源字符集,即可用来编写源代码的字符集,它由标准美国键盘上的字符(大写和小写)和数字,C语言中使用的符号(如{和=)以及其他一些字符(如换行符和空格)组成。还有一个基本的执行字符集,它包括在程序执行期间可以处理的字符,因此执行字符集包含了基本的源字符集,除此之外还包含了转义字符。例如退格和振铃。

wchar_t字符数据类型

    • 程序需要处理的字符集可能无法用一个8位的字节表示。如日文汉字系统。对于这种情况,C++的处理方式有两种。首先,如果大型字符集是实现的基本字符集,则编译器厂商可以将char定义为16位的字节或更长的字节。其次,一种实现可以同时支持一个小型基本字符集数据类型char和一个较大的扩展字符集,而这一个较大的扩展字符集可以用另一种类型wchar_t(wide char 宽字符类型)可以表示扩展字符集。而cin和cout在其被创建的时候,就没有设计识别wchar_t类型的相应数据类型,它们只能将输入和输出看作是char流,而不能是wchar_t流。因此不适用于处理wchar_t类型。因此iostream头文件的最新版本提供了作用相似的工具——wcin和wcout,可用于处理wchar_t流。
    • 另外,可以通过加上前缀L来指示宽字符常量和宽字符串。wchar_t宽度为2个字节。
    • C+ 11新增类型:char16_t和char32_t:随着编程人员日益熟悉Unicode,类型wchar_t显然不再能够满足需求。事实上,在计算机系统上进行字符和字符串编码时,仅使用Unicode码点并不够。具体地说,进行字符串编码时,如果有特定长度和符号特征的类型,将很有帮助。因此新增了这两个无符号的长度更大的类型,C++使用前缀u表示char16_t字符常量和字符串常量,用前缀U表示char32_t常量。

bool(布尔)类型

      1. ANSI/ISO C++标准添加了一种名叫bool的新类型(对C++来说是新类型)。它的名称来源于英国数学家George Boole,是他开发了逻辑律的数学表示法。
      1. 在计算中,布尔变量的值可以是ture或者false。C++将非零值解释为ture,将零解释为false,然而,现在可以使用bool类型表示真和假了,它们分别用预定义的符号常量ture和false表示
      1. bool类型所占内存大小为1个字节,因为1个字节为内存单元最小的度量,而1个字节对于bool类型来说已经足够(ture=1,false=0),因此不需要更多内存了,sizeof(bool)=1;

C++如何确定常量的类型

      1. 程序的声明将特定的整型变量的类型告诉了编译器,但是编译器是如何判断一个常量的类型呢?如:cout<<“year=”<<1492<<endl; 程序将1492存储为int,long还是其他类型呢?答案是:除非有理由存储为其他类型(如使用了特殊的后缀来告诉编译器它为其他类型,或者值太大超过了int自然长度),否则C++将整型常量存储为int类型。
      1. 后缀是放在数字常量后面的字母,与放在字符常量前面的前缀意义相同,都是告诉编译器常量的具体类型。整数后面的l和L后缀表示该整数为long常量,u或U后缀表示unsigned int常量(注意,u和U前缀分别表示char16_t和char32_t),ul(可以采用任意一种顺序,大小写均可)表示unsigned long常量。
      1. 十六进制常用来表示内存地址,而内存地址是没有符号的。

3.3 浮点型

  • 浮点数是C++第二组基本类型。浮点数可以表示带小数部分的数字,诸如:2.5、3.1415926等等。计算机将这样的值分成两部分存储,一部分表示值,另一部分用于表示对值进行放大或缩小。例如:34.1245和34124.5,它们除了小数点的位置不同外,其他都是相同的,因此可以把第一个数表示为0.341245(基准值)和100(缩放因子),而第二个数表示为0.341245(基准值)和10000(缩放因子)。缩放因子的作用是移动小数点的位置,术语浮点因此而得名。
      1. C++内部表示浮点数的方法与此相同,只不过它基于的是二进制数,因此缩放因子是2的幂,不是10的幂。浮点数内部表示方式与整数有天壤之别。
      1. 浮点数的书写两种形式:第一种是直接以小数形式书写,标准小数点表示法,如3.1415926;第二种叫作E表示法,如3.45E6,3.45代表尾数(基准值),E表示10的幂的形式,6表示指数。**注意E或者e都可以,然而数字中不能有空格,这是一个整体,尾数部分必须有数字,指数部分只能为整数。**其次,指数既可以是正数也可以是负数,如3.45E-6。因此E表示法适合特别大或者特别小的数字。

浮点类型 float、double和long double

      1. C++有三种浮点类型:float、double和long double。这些类型是按它们可以表示的有效数位和允许的指数最小范围来描述的。
      1. 有效位(significant figure)是数字中有意义的位。例如加利福尼亚的Shata山脉的高度为14179英尺,该数字使用了5个有效位,然而将Shata山脉的高度写成约为14000英尺的时候,有效位就成了2位,因为结果经过四舍五入精确到了千位。在这种情况下,其余三位只不过是占位符而已。因此有效位指的是精确以后的位数,从该数的第一个非零数字开始数起,数到末尾,如0.618,有效数字为(6,1,8)三个。如cout << 12345.67; 最后输出到屏幕上是:12345.6 因为cout默认有效位数是6,注意,小数点不算一个有效位,有效位指的是数字。另外,float精度是7位,有些编译器是6位有效数字,double精度是15位,有些编译器是16位。
      1. C和C++对于有效位数的要求是,float至少32位(4字节),double至少48位(6字节),然而通常float为4字节,double为8字节,long double为80位,96位或者128位。另外,这三种类型的指数范围至少是-37~+37。
      1. 使用cout输出浮点型数字的时候,通常cout会删除结尾的无效零。例如:3.141500,cout会输出3.1415。**但调用cout.setf()将会修改这种行为,这种调用迫使输出使用定点表示法,以便更好地了解精度,它防止程序把较大地值切换为E表示法,并使程序显示小数点后6位。**如:cout.setf(ios_base::fixed,ios_base::floatfield); 参数ios_base::fixed和ios_base::floatfield是通过包含iostream来提供的常量。

浮点常量

    • 在程序中书写浮点常量时,程序将它存储位哪种浮点类型呢?在默认情况下,像8.24和2.4e8这样的浮点常量都属于double类型。如果希望浮点常量告诉编译器是float型,请加上f或者F后缀,对于long double类型,可以使用l或者L后缀。
  • 浮点数的优缺点
      1. 优点: 首先,它们可以表示整数之间的值。(因为大概率情况下浮点型的内存空间长度都大于整型,因此可以表示整数之间的值,不会出现缩窄现象,也就是不会出现数据截断而丢失数据的情况)其次,由于有缩放因子,它们可以表示的范围大得多。
      1. 缺点: 浮点运算的速度通常比整数运算慢,且精度将降低。

四、复合数据类型

  • 复合类型:这种类型是基于整型和浮点型类型创建的。影响最为深远的复合类型是类(class),它是将学习的OOP的堡垒。 然而,C++还支持几种更普通的复合类型,它们都来自于C语言,例如数组,结构,指针,枚举等等。

4.1 数组

  • 数组(array)是一种数据结构(数据结构是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。),向系统申请一块连续的内存空间,存储多个同类型数据的值。每个值存储在一个独立的数组元素中,计算机在内存中依次存储数组的各个元素。
  • C++标准模板库(STL)提供了一种数组代替品——模板类(vector),而C++新增了模板类array。这些代替品比内置复合类型数组更复杂,更灵活。

数组相关知识

    1. 数组声明定义的通用格式:typeName arrayName[arraySize]; 表达式arraySize指定元素数目,它必须是整型常数(字面常量)或const值,也可以是常量表达式,即在数组在编译阶段就确定内存空间大小,由系统自动分配指定内存单元,因此需要显式声明其长度。
    1. C++使用带索引的方括号表示法来指定数组元素。
    1. 有效下标值的重要性:编译器不会检查使用的下标是否有效。如果将一个值赋给不存在的元素,也就是索引值超过数组的长度,这样的话就会触碰到其他内存单元(因为数组名实际上是该数组的首地址,是一个指针常量,不可修改,而带索引的方括号实际上与*解引用的操作一样,因此会访问到一个不属于该数组的其他内存单元的地址),它可能破坏数据或代码,也可能导致程序异常终止。
    1. 数组初始化:typeName arrayName[arraySize]={参数列表}; 参数列表的每个单独的值需要用一个逗号来分隔(编译器识别逗号token来告诉系统进入下一个存储单元),并将它们用花括号括起来。
    1. 数组初始化的相关问题:(1). 只有在定义数组时候才能使用初始化赋值,以后就不能使用了,并且也不能将一个数组赋给另一个数组,因为数组名是地址,不是变量,或者说数组只是声明了一块内存空间,其本身并不是变量。(2). 如果只对数组的一部分进行初始化,则编译器会把其他元素设置为0。(3). 如果不进行初始化,则auto型数组的话编译器不会给它每个元素赋予0初值,而是赋给随机值,如果是static型数组则依然会每个元素都赋值0(字符数组同理,数值的0换成空字符)。因此有一个简单的方法将auto数组全部赋初值为0,就是typeName arrayName[arraySize]={0}; 赋一个元素为0即可,剩下的元素会被编译器默认赋为0。 (4). 如果初始化数组方括号内长度为空,则C++编译器将根据参数列表有多少个参数,就默认该数组长度为多大(因此这两者不能同时为空)。(5). 数组列表初始化也同样禁止缩窄转换(即跨类型赋初值)。
    1. 数组与sizeof运算符:sizeof运算符让编译器翻译告诉系统统计类型或者数据对象所占内存空间的长度并返回。注意,将sizeof运算符用于数组名,得到的将是整个数组中的字符数。但如果将sizeof用于数组元素,则得到的将是元素的长度。因为数组名代表了数组这个数据类型,而元素名只代表该元素这个数据对象。

4.2 字符串

  • 字符串是存储在内存的连续字节中的一系列字符,并用双引号括起来声明(双引号被赋予了特殊的意义与作用,被编译器识别为字符串的定义)。 C++处理字符串的方式有两种:第一种来自C语言,将一系列字符存储在数组的连续单元中,每个内存单元存储一个字符,通过数组数据类型的连续性来实现一串字符的处理,C-风格字符串具有一种特殊的性质:以空字符结尾,空字符因为无法从键盘上输入,因此被写入转义字符’\0’,其ASCII码为0,因为其有无法打印的特点,因此被用来标记字符串结尾。第二种是C++特有,基于string类库的方法,C++中string为string类对象,也就是可以存放字符串的数据类型。

字符串相关知识

    1. 如果以字符串为单位输出,则该字符串一定要有’\0’,否则系统会出错。如果没有’\0’,则单个字符的形式逐个输出,但也要注意不要超过了长度。
    1. 各种C++输入工具通过键盘输入,将字符串读入char数组中时,将自动加上结尾的空字符。因为例如cin输入遇到字符后面的空白符会默认为结束(这与cin输入流类机制相关),并且在结尾加上空字符。
    1. 与C语言相近的输入方式:cin的相关问题:cin是如何确定已完成字符串输入的呢?由于不能通过键盘输入空字符,因此cin需要用别的方法来确定字符串的结尾位置。cin使用空白(空格,制表符和换行符)来确定字符串的结束位置。这意味着cin在获取字符数组输入时一次只读取一个单词。读取一个单词以后,cin将该单词字符串放到该数组中,并自动在结尾添加空字符,结束读取。 因此,cin是面向单词的输入类,cin会跳过首个字母前面所有空白符,寻找首个字符,从首个字符开始,以字符序列后的首个空白为结束。故要用cin输入一行完整的英语句子时,则需要将这个句子拆成n个独立单词,分别由n个cin输入存放在n个独立数组中,以此来构成一个完整的英文句子。为了解决这个麻烦事情,c++ istream中的类(如cin)提供了一些面向行的类成员函数:getline()和get()。
    1. 对于未被初始化的数组,第一个空字符的出现位置是随机的。

处理字符串数组的I/O函数

面向行输入:getline()
    1. getline()函数读取整行,它使用通过回车键输入的换行来确定结尾。要调用这种方法,可以使用cin.getline()。
    1. cin.getline()函数接受两个参数,第一个参数用来输入存放输入行的数组的名称,第二个参数是要读取的字符数。一般来说第二个参数都会输入该数组指定的有效长度,如:cin.getline(arrayName,sizeof(arrayName)-1); 这样保证了不会超过数组长度,余下的空间用于自动在结尾处添加的空字符。
    1. getline()成员函数在读取指定数目的字符或者遇到换行符时停止读取。
    1. getline()成员函数每次读取一行。它通过换行符来确定行尾,但不保存换行符,相反,在存储字符串时,它用空字符来替换换行符。也就是说getline()一样会吸收缓冲区中行尾的换行符,并且会将换行符换成空字符存入字符串末尾处。
    1. getline可以类比于C语言中的gets()函数,但是两者调用格式不同,接受参数不同。
面向行输入:get()
    1. get()函数读取整行,它使用通过回车键输入的换行来确定结尾。要调用这种方法,可以使用cin.get()。
    1. cin.get()函数接受两个参数,第一个参数用来输入存放输入行的数组的名称,第二个参数是要读取的字符数,与cin.getline()的两个参数完全相同。如:cin.get(arrayName,sizeof(arrayName)-1);
    1. get()成员函数不像getline()并不会读取并且丢弃换行符,而是将换行符留在了输入队列的缓冲区内,它也不像cin那样会跳过开头的无效空白,它只要识别到换行符就认为结束,没有跳过功能。因此如果由于第一次调用后,换行符留在了输入队列中,因此二次调用时看到的第一个字符便是换行符,get()便认为已经到达了行尾,而没有发现任何可读取的内容。如果不借助于帮助,get()将无法跨过该换行符,也就是说以后每个get()都是直接结束。
    1. 由于C++存在函数重载,解决了get()无法跨国行尾换行符的问题,get()函数有一个变体,其名称也是get(),但区别是接受参数不同,该get()不接受任何参数。编译器会根据参数不同来识别get()函数。
    1. 不接受任何参数的cin.get(void)调用可读取下一个字符(即便是换行符),因此可以使用它来处理换行符,为读取下一行输入做好准备。cin.get(void)类比于C语言中的getchar()吸收空行,一般C语言中如果连用scanf+gets,则在scanf后面需要紧接着一个getchar()来吸收scanf输入的时候遗留在缓冲区的换行符,避免gets首个读取字符为换行就结束,但这里gets()性质由于cin.get(参数列表)不同,因为gets()是类似于getline()那样会吸收换行符变成空字符的,因此只会影响scanf()函数后面紧跟着的首个gets()函数。同理,我们在输入一堆信息中存在先输入整型数值,再输入字符串的情况,我们可以这样解决:cin>>name;cin.get();或者(cin >> name).get();来解决换行符问题,在cin后续的cin.get(参数列表)就不会被跳过了。
    1. 因此可以使用以下格式来完成一行字符的输入:cin.get(arrayName,sizeof(arrayName)-1);cin.get();或者:因为cin.get(name,ArSize)返回一个cin对象,该对象随后会被用来调用get函数,因此可以将两者放在一个语句中:cin.get(name,Arsize).get(); 同样的,对于getline函数也可以在一个语句中同时输入两句话放在两个数组中,cin.getline(name1,ArSize).getline(name2,ArSize);这种拼接方式。

String类

    1. ISO/ANSI C++98 标准通过添加string类扩展了C++库,因此现在可以string类型的变量而不是字符数组来存储字符串。string提供了字符串作为一种数据类型的表示方法。
    1. 要使用string类,必须在程序中包含头文件stringstring类位于名称空间std中,string类定义隐藏了字符串的数组性质,让您能够像处理变量那样处理字符串。但同时,可以用数组的表示法来访问存储在string对象中的字符(下标索引法)。
    1. 类设计让程序能够自动处理string的大小。
    1. string类变量是一个表示字符串的实体。
    1. 赋值,拼接和附加:使用string类时,某些操作比使用数组时更简单。例如:(1). 可以用赋值表达式将一个string对象赋给另一个string变量(strcpy);(2). 可以使用运算符+将两个string对象合并起来(strcat);(3). 可以使用运算符+=将字符串附加到string对象的末尾。
    1. string类成员函数size()取代strlen():arrayName.size();返回值为该string对象的有效长度。与strlen对比来看,由于strlen是单独的函数,因此用法为strlen(arrayName); 这里数组名是参数,而size是成员函数,因此数组名是对象,对象对成员函数的调用,用句点连接,因此用法为arrayName.size()。

String类I/O

  • 正如您知道的,可以使用cin和运算符>>来将输入存储到string对象中,使用cout和运算符<<来显示string对象。可是有没有专门的函数来处理string对象呢?就像cin.getline()和cin.get()处理字符串数组那样。自然,string对象可以使用string类的一个友元函数getline()来处理。
    1. 下面是将一行输入读取到数组中的代码:cin.getline(arrayName,arraySize); 将一行输入读取到string对象中的代码:getline(cin.stringName);
    1. 由上述可以发现,这个getline()并不是类方法。它将cin作为参数,指出到哪里去查找输入。另外也没有指出到字符串长度的参数,因为string对象将根据字符串长度自动调整自己的大小。
    1. 由于istream类的设计考虑到了诸如double和int等基本C++数据类型,但没有考虑string类型,所以istream类中,有处理double,int和其他基本类型的类方法,但没有处理string对象的类方法。(这就好比string是自定义的一种新数据类型,而在之前创造出的输出类中并没有设计专门用来处理string对象的成员函数的方法)。而你可能会问既然istream类中没有处理string对象的类方法,那么cin和cout如何处理string对象的呢?这个就与cin/cout其内部工作原理有关了,我们需要知道cin和cout能够处理string对象。

4.3 结构

  • 结构是一种比数组更灵活的数据格式,因为同一个结构可以存储多种类型的数据,而数组只能存储同一类型数据。当我们要跟踪的信息存在多种类型的数据时,我们就可以用结构数据类型,甚至我们可以使用结构数组。结构是C++ OOP堡垒的基石。简单来说类与结构的区别就是:C++中类除了成员变量之外,还可以定义成员函数。而结构体内部是不能定义函数的。
      1. 结构是用户定义的类型,而结构声明定义了这种类型的数据属性。关键字struct表明,这些代码定义的是一个结构的布局。结构体的声明只是告诉编译器其结构的存储模式,并不会申请占用内存空间,只有当声明了结构体变量时,才会为该变量分配其所声明归属的某个结构体模式下的存储空间,也就是它的内存单元大小根据这个模式来分配。
      1. 结构体声明中每一个列表项都是一条声明语句,仅仅用来声明变量,告诉编译器该结构体完整的存储模式,不能初始化,赋值等等操作。
      1. 结构体定义的格式: struct struct_typeName { statements }; 结构体变量定义声明的格式: struct_typeName Variable_Name;
      1. 通过成员名能够访问结构的成员,格式:Variable_Name.Member_Name; 用句点连接。
      1. 结构体变量的初始化同样不允许缩窄转换。参数列表中的不同参数用逗号分隔,参数列表用花括号括起来。
      1. 与数组不同的是,结构体变量初始化允许花括号内为空,结构体变量初始化时花括号内为空,意味着各个成员都将被设置为零。数值都被设置为0,字符都被设置为空字符。
      1. 结构体实质上是一种变量,因此可以将结构体作为参数传递给函数,也可以将一个结构体变量直接赋值给另一个同结构类型的结构体变量,即便成员项中有数组,数组也同样会被赋值一样给另一个该结构类型的结构体变量的对应成员。
      1. 结构数组的定义声明格式:struct_typeName arrayName[LEN];

4.4 联合体(共用体)

  • 联合体(union)是一种数据结构,它能够存储不同的数据类型,但只能同时存储其中的一种类型。
      1. 联合体一次(同一时间内)只能存储一个值,对其中任意一个成员赋值一次。因此它必须有足够的空间来存储最大的成员,所以,联合体的长度为其最大成员的长度。
      1. 联合体用途之一是,当数据项使用两种或者更多种格式(但不会同时使用)时,可节省空间。例如:假设管理一个小商品目录,其中有一些商品的ID为数字,而另一些ID为字符串,这种情况下,就可以使用联合体。
      1. 联合体不仅是用来节省内存的,在C++中还用于嵌入式系统编程,如控制烤箱,MP3播放器或者火星漫步者的处理器。对这些应用程序来说,内存可能非常宝贵。另外,联合体常用于操作系统数据结构或者硬件数据结构。

4.5 指针和自由存储空间

  • 计算机程序在存储数据的时候必须跟踪的3种基本属性:(1).信息存储的位置(2).存储的值(3).存储的信息是什么类型。我们对指针来分析来看,信息存储的位置与其他数据类型没有什么差别,但存储的值是其他数据的存储地址,这也就让我们明白了指针是用来追踪定位其他内存数据的一个变量,而不是对自身研究的一种数据类型。
      1. 如何找到常规变量的地址。只需对变量应用地址运算符(&),就可以获得它的地址。
      1. 显示地址时,该实现的cout使用十六进制表示法,因为这是常用于描述内存的表示法。
      1. 有些系统中,可能不会将两个同时声明的同类型变量存储在相邻的内存单元中。使用常规变量时,值是指定的量,而地址是派生量。而指针刚好相反,将地址视为指定的量,而将值视为该地址下的派生量。
      1. 不同的数据类型,它们存储值时使用的内部格式也不同,因此对于一个指针变量声明,必须指定指针所指向的数据的类型,这也决定了指针的操作数
      1. 当我们要将一个数值告诉编译器它是代表地址值的时候,我们需要用强制转换,格式如下:(所指向的数据类型*)数值。这样一来,该数值就会被编译器翻译成告诉系统这是一个某数据类型的具体地址,这个地址等于这个数值。但我们通常是不知道一个数值的地址的,这些在计算机中是不可见的。
      1. sizeof(pointerName);返回值都是4,因为指针的值是地址,而地址也是整型量因此占4个字节的存储空间。

指针与C++基本原理

  • 面向对象编程与传统的过程性编程的区别在于,OOP强调的是在运行阶段(而不是编译阶段)进行决策。运行阶段指的是程序正在运行时,编译阶段指的是编译器将程序组合起来时。总之,使用OOP时,可以在运行阶段确定数组的长度。C++采用的方法是,使用关键字new请求正确数量的内存以及使用指针来跟踪新分配的内存的位置。(在C语言中,可以使用库函数malloc()来分配内存,而C++中则是new运算符)
  • 变量是在编译时分配的有名称的内存,而指针只是为可以通过名称直接访问的内存提供了一个别名,指针访问该有名存储区的地址,使该变量可以通过内存单元的名称访问,也可以通过指针解引用其地址访问。指针真正的用武之地在于,在运行阶段分配未命名的内存以存储值。在未命名的情况下,只能通过指针来访问内存。
      1. 为一个数据对象获得并指定分配内存的通用格式如下:typeName* Pointer_name=new typeName;
      1. 程序员要告诉new,需要为哪种数据类型分配内存;new将找到一个长度正确的内存块,并返回该内存块的地址。程序员的责任是将该地址赋给一个指针。
      1. 使用delete释放内存,与(malloc-free函数一样,用free()函数来释放malloc()函数申请的内存),delete运算符可以在new运算符使用完内存以后,将其归还给内存池。
      1. 一定要配对使用new和delete,否则将会发生内存泄漏(memory leak),也就是说,被分配的内存再也无法使用了。如果内存泄漏严重,则程序将由于不断寻找更多内存而终止。
      1. 只能用delete来释放使用new分配的内存。然而,对于空指针使用delete是安全的。

使用new来创建动态数组

  • 如果通过声明来创建数组,则在程序被编译的时候将为它分配内存空间。不管程序最终是否使用数组,数组都在那里,它占用了内存。在编译时给数组分配内存被称为静态联编(static binding)。但使用new时,如果在运行阶段需要数组,则创建它;如果不需要则不创建(例如菜单中选择creat_array)。还可以在程序运行时选择数组的长度,这被称为动态联编(dynamic binding),这种数组叫作动态数组(dynamic array)。
      1. 使用new创建动态数组的通用格式:typeName* pointer_Name=new typeName [Size];
      1. new运算符返回第一个元素的地址。
      1. 对应的new运算符创建的数组,用相应的另一种格式的delete来释放:delete [] pointer_Name; (方括号告诉程序,应该释放整个数组,而不仅仅是指针指向的元素)
      1. 程序员的责任是跟踪内存块的元素个数,也就是说编写程序的时候,必须告诉编译器用new申请多少个该数据类型长度的连续存储单元。与静态联编不同的是,我们可以在程序运行过程中告诉系统需要的元素个数,也就是说方括号内可以是变量。
      1. 不能使用sizeof运算符来确定动态分配的数组包含的字节数,因为只是向系统申请了一块内存,但并没有给内存命名,所以这种内存信息是不共用的,sizeof运算符只能确定信息公用的内存单元大小。

使用new来创建动态结构

  • 在程序运行时为结构分配所需的空间,也可以通过new运算符来完成。这种结构为动态结构。
      1. 使用new创建动态结构的通用格式:structType_Name* pointer_Name=new structType_Name;
      1. 创建动态结构时,不能将成员运算符句点用于结构名,因为这种结构没有名称,只是知道它的地址(注意new出来的内存空间只是一块内存,不像系统给变量分配内存那样会为内存署上变量名)。C++专门为这种情况提供了一个运算符:箭头成员运算符(->),该运算符用于结构指针,格式为:struct_pointerName->memberName; 或者(*struct_pointerName).memberName;

4.6 数组的替代品

模板类 vector

  • 模板类vector类似于string类,也是一种动态数组。您可以在运行阶段设置vector对象的长度,可在末尾附加新数据,还可以在中间插入新数据。基本上,它是使用new创建动态数组的替代品。实际上,vector类确实使用new和delete来管理内存,但这种工作是自动完成的。
      1. 首先,要使用vector对象,必须包含头文件vector。其次,vector包含在名称空间std中。
      1. 使用格式:vector vi; vector vd; …… , vector objectName(n_elem)。 上述格式中,前两个为实例,vi是一个vector的对象,vd同理。由于vector对象在您插入或添加值时自动调整长度,因此可以将vi的初始长度设置为零。但要调整长度,需要使用vector包中的各种方法。
      1. 其中参数n_elem可以是整型常量,也可以是整型变量。

模板类 array

  • vector类功能比数组强大,但付出的代价就是效率稍低。如果您需要的是长度固定的数组,可以使用模板类array来代替数组,和数组一样,array对象的长度也是固定的,由系统自动分配内存。
      1. 首先,要使用array对象,必须包含头文件array。其次,array也位于名称空间std中。
      1. 使用通用格式:array<typeName,n_elem> arrName; 与vector不同的是,n_elem不能是变量,与数组一样必须是常量。
      1. 与数组不同的是,array对象之间可以赋值。

猜你喜欢

转载自blog.csdn.net/Arbitrary007/article/details/115028854