此专栏为移动机器人知识体系下的编程语言中的 C {\rm C} C++从入门到深入的专栏,参考书籍:《深入浅出 C {\rm C} C++》(马晓锐)和《从 C {\rm C} C到 C {\rm C} C++精通面向对象编程》(曾凡锋等)。
16.C++快速回顾
16.1 两个简单的C++实例
项目 1 1 1:打印字符串到屏幕上 ( e x a m p l e 16 _ 1. c p p ) ({\rm example16\_1.cpp}) (example16_1.cpp)。
/** * 作者:罗思维 * 时间:2024/04/09 * 描述:打印字符串到屏幕上。 */ #include <iostream> using namespace std; int main() { cout << "Hello World." << endl; cout << "Welcome to FUXI AI Technology." << endl; return 0; }
项目 2 2 2:使用冒泡算法对一组整数从小到大进行排序 ( e x a m p l e 16 _ 2. c p p ) ({\rm example16\_2.cpp}) (example16_2.cpp)。
/** * 作者:罗思维 * 时间:2024/04/09 * 描述:使用冒泡算法对一组整数从小到大进行排序。 */ #include <iostream> using namespace std; // 类定义; class IntSort { private: int* m_dat; int m_num; public: IntSort(int dat[], int num); // 构造函数声明; ~IntSort(); // 析构函数声明; void sort(); // 成员函数声明; int GetNum() { // 成员函数定义; return m_num; }; int* GetData() { return m_dat; }; }; // 构造函数定义,使用初始化成员列表初始化数据成员; IntSort::IntSort(int dat[], int num): m_num(num) { m_dat = new int[num]; for (int i = 0; i < num; i++) { m_dat[i] = dat[i]; } } // 析构函数定义; IntSort::~IntSort() { delete[] m_dat; } // 成员函数定义; void IntSort::sort() { for (int i = 0; i < m_num - 1; i++) { for (int j = 0; j < m_num - 1 - i; j++) { if (m_dat[j] > m_dat[j + 1]) { int temp = m_dat[j]; m_dat[j] = m_dat[j + 1]; m_dat[j + 1] = temp; } } } } int main() { int dat[100]; int num; cin >> num; // 输入数据个数; for (int i = 0; i < num; i++) { cin >> dat[i]; // 输入数组数据; } IntSort intdat(dat, num); // 定义类对象; intdat.sort(); // 调用类成员函数; int n = intdat.GetNum(); int* d = intdat.GetData(); for (int i = 0; i < n; i++) { cout << *(d + i) << " "; } return 0; }
16.2 面向对象程序设计
- 几个面向对象概念:
- 对象:客观世界中任何一个事物都是对象。
- 任何一个对象都包含属性 ( a t t r i b u t e ) ({\rm attribute}) (attribute)和行为 ( b e h a v i o r ) ({\rm behavior}) (behavior),一个对象一般由一组属性和一组行为组成,对象通过接收来自外界的消息进行相应的操作,对象也可以给其他对象发送消息;
- 在一个系统中包含很多对象,对象之间通过发送和接收消息相互联系,从而推动系统运行;
- C {\rm C} C++中每个对象都是由"数据"和"函数"组成,对象是面向对象程序设计的基本组成单位,"数据"是"属性"的体现,"函数"是"行为"的体现,函数通过对数据进行操作来实现功能;
- 抽象:从众多的事物中抽取出共同的、本质性的特征,舍弃非本质特征的过程。
- 比如:苹果、葡萄、香蕉、雪梨等,其共同特征都属于水果类,得出水果类概念的过程是一个抽象的过程;
- 抽象的作用:找出同一类事物的本质, C / C {\rm C/C} C/C++的基本数据类型就是对一批具体数据的抽象;
- 面向对象通过抽象得到类,类是对象的抽象,对象是类的实例,即是类的具体表现,类是抽象的,对象是具体的;
- 封装与信息隐蔽
- 封装:将对象的部分或全部属性和部分功能对外界屏蔽,外界对这些被屏蔽的部分不可见、不可知;
- 封装的好处:降低人们操作对象的难度,使用对象的人可以完全不需要知道对象内部的细节,只需要知道对象对外开放的功能即可操作对象;
- 封装的含义:一是将相关的数据和函数封装在一个对象中,形成一个基本单位;而是将对象中某些部分进行隐蔽,隐蔽内部的细节,只保留一些接口和外部联系,对外隐蔽的做法称为信息隐蔽 ( I n f o r m a t i o n H i d i n g ) ({\rm Information\ Hiding}) (Information Hiding);
- 信息隐蔽有利于数据安全,防止无关的人了解和修改数据;
- C {\rm C} C++的对象中的公有函数名就是对象的对外接口,外界通过这些函数名来调用这些函数完成某些功能;
- 消息:对象之间发出的行为请求。
- 封装使对象成为一个相对独立的实体,消息机制为它们提供一个相互间动态联系的途径,使对象的行为能相互配合,进而推动系统有机地运行;
- 当系统中的其他对象或系统命令请求这个对象去执行某个行为时,就向这个对象发送一个消息,这个对象响应这个消息请求,完成指定的行为;
- 继承与重用
- 继承使得某类对象可以继承另外一类对象的属性和行为,即一类(子类)对象具有另一类(父类)对象的特性;
- 类通过继承可以形成类的继承层次结构,位于上层的类被称为父类或基类,位于下层从父类继承而来的类称为子类或派生类;
- 多态性
- 多态本质:一名多用,如多条同名的消息传递给不同的对象,消息引发的行为可能不同;
- C {\rm C} C++中,多态性指由继承而产生的相关不同类,它们的对象对同一消息会做出不同的响应;
- 对象:客观世界中任何一个事物都是对象。
- 面向对象程序设计特点:抽象性、封装性、继承性、多态性。
- 抽象性:将同一类事物的共同特征概括出来,得到类并用类创建对象,抽象机制对操作数据的代码局部化,如果需要改变处理数据的代码,只需在一个地方进行更改即可,不必改动程序中所有访问数据的地方;
- 封装性:保持抽象机制实现细节的独立性,抽象机制要求认真考虑对象的接口,确定用户可以看到的东西;
- 继承性:可以让一个类重用另一个类的接口和实现,以已有代码为基础方便地扩充出新的功能和特性,从而达到代码扩充和代码重用的目的;
- 多态性:在继承的基础上,派生出的不同种类的对象都具有相同名称的行为,行为具体实现方式有所不同,从而可以设计和实现一个易于扩展的软件系统;
- 面向对象程序设计和面向过程程序设计比较
- 面向过程程序设计特点:
- 面向过程程序设计强调功能抽象和模块性,围绕功能进行设计,用一个函数实现一个功能;
- 面向过程程序设计的数据是公用的,一个函数可以使用任何一组数据,一组数据可能被多个函数使用;
- 对于大型复杂软件系统开发,面向过程程序设计的不足:程序设计困难、代码重用程序低、数据不安全、代码升级和维护困难;
- 面向对象程序设计特点:
- 面向对象程序设计综合功能抽象和数据抽象,将解决问题看成一个分类演绎过程,抽象得到类,类包含数据和操作数据的功能;
- 面向对象程序设计分析和设计面对的是一个个对象,数据和操作数据的功能封装在对象里,每个对象基本独立,对象间耦合性比较低,使得程序设计难度降低,同时保护了数据的安全;
- 面向对象程序设计通过继承可以非常方便地从已有类派生出新的类,实现代码重用,多态性和继承性可以让程序易于扩展,方便系统增加新功能;
- 面向对象程序设计优点:软件设计开发方式更符合人们对现实世界的认知,提高了数据的安全性,有助于软件的升级维护和重用;
- 面向过程程序设计特点:
- 面向对象的软件工程 5 5 5个阶段
- 面向对象分析 ( O b j e c t − O r i e n t e d A n a l y s i s , O O A ) ({\rm Object-Oriented\ Analysis,OOA}) (Object−Oriented Analysis,OOA):面向对象分析要按照面向对象的概念和方法,对要开发的软件系统的问题域和系统功能进行分析和理解,归纳出描述问题域和系统任务需要的对象,包括对象的属性、行为及它们之间的联系,并将具有相同属性和操作的对象用一个类来描述,建立一个能够真实反映问题域、满足用户需求的系统分析模型( O O A {\rm OOA} OOA模型);
- 面向对象设计 ( O b j e c t − O r i e n t e d D e s i g n , O O D ) ({\rm Object-Oriented\ Design,OOD}) (Object−Oriented Design,OOD):在面向对象分析阶段形成的系统分析模型基础上,运用面向对象方法,解决与实现有关的问题,产生一个符合具体实现条件且可以实现的系统设计模型( O O D {\rm OOD} OOD模型);具体设计包括:先进行类的设计,类设计包括类层次设计(利用继承),后以这些类为基础提出程序设计的思路、方法及算法的设计;
- 面向对象编程 ( O b j e c t − O r i e n t e d P r o g r a m m i n g , O O P ) ({\rm Object-Oriented\ Programming,OOP}) (Object−Oriented Programming,OOP):根据面向对象设计的结果,选择一种支持面向对象程序设计的计算机语言进行编写程序;
- 面向对象测试 ( O b j e c t − O r i e n t e d T e s t , O O T ) ({\rm Object-Oriented\ Test,OOT}) (Object−Oriented Test,OOT):将程序写好后交给用户使用前,必须对程序进行严格的测试,测试的目的是尽可能发现程序中的错误并修改好这些错误,面向对象测试方法以类为测试的基本单元;
- 面向对象维护 ( O b j e c t − O r i e n t e d S o f t M a i n t e n a n c e , O O S M ) ({\rm Object-Oriented\ Soft\ Maintenance,OOSM}) (Object−Oriented Soft Maintenance,OOSM):软件在交付使用后需要进行维护,软件使用时可能出现测试时没发现的错误,需要对软件进行纠错性维护;若软件开发商想改进软件的功能和性能,需要对软件进行完善性维护;为了让软件适应新的运行环境,需要对软件进行适应性维护等;
16.3 C++扩充
16.3.1 C++标准库头文件
标准头文件 {标准头文件} 标准头文件 | 文件说明 文件说明 文件说明 |
---|---|
< i o s t r e a m {\rm iostream} iostream> | 包含 C 包含{\rm C} 包含C++ 标准输入和输出函数的函数原型 标准输入和输出函数的函数原型 标准输入和输出函数的函数原型 |
< f s t r e a m {\rm fstream} fstream> | 包含由磁盘文件输入和向磁盘文件输出的函数的函数原型 包含由磁盘文件输入和向磁盘文件输出的函数的函数原型 包含由磁盘文件输入和向磁盘文件输出的函数的函数原型 |
< i o m a n i p {\rm iomanip} iomanip> | 包含格式化数据流的流运算符的函数原型 包含格式化数据流的流运算符的函数原型 包含格式化数据流的流运算符的函数原型 |
< c m a t h {\rm cmath} cmath> | 包含数学库函数的函数原型 包含数学库函数的函数原型 包含数学库函数的函数原型 |
< c s t d l i b {\rm cstdlib} cstdlib> | 包含了字符串和数的相互转换、内存分配、随机数和其他工具函数的函数原型 包含了字符串和数的相互转换、内存分配、随机数和其他工具函数的函数原型 包含了字符串和数的相互转换、内存分配、随机数和其他工具函数的函数原型 |
< c s t d i o {\rm cstdio} cstdio> | 包含 C 标准输入和输出库函数的函数原型及这些函数所用的信息 包含{\rm C}标准输入和输出库函数的函数原型及这些函数所用的信息 包含C标准输入和输出库函数的函数原型及这些函数所用的信息 |
< c t i m e {\rm ctime} ctime> | 包含维护时间和日期的函数原型和类型 包含维护时间和日期的函数原型和类型 包含维护时间和日期的函数原型和类型 |
< c s t r i n g {\rm cstring} cstring> | 包含 C 风格字符串处理函数的函数原型 包含{\rm C}风格字符串处理函数的函数原型 包含C风格字符串处理函数的函数原型 |
< s t r i n g {\rm string} string> | 包含 C 包含{\rm C} 包含C++ 处理字符串的 s t r i n g 类的定义 处理字符串的{\rm string}类的定义 处理字符串的string类的定义 |
< m e m o r y {\rm memory} memory> | 包含 C 包含{\rm C} 包含C++ 用来分配内存的类和函数 用来分配内存的类和函数 用来分配内存的类和函数 |
< c a s s e r t {\rm cassert} cassert> | 包含辅助程序调试的添加诊断的宏 包含辅助程序调试的添加诊断的宏 包含辅助程序调试的添加诊断的宏 |
< e x c e p t i o n {\rm exception} exception>,< s t d e x c e p t {\rm stdexcept} stdexcept> | 包含用于异常处理的类 包含用于异常处理的类 包含用于异常处理的类 |
< l o c a l e {\rm locale} locale> | 包含一般流处理所用的类和函数 , 用来处理不同语言自然形成的数据 ( 本地化 ) 包含一般流处理所用的类和函数,用来处理不同语言自然形成的数据(本地化) 包含一般流处理所用的类和函数,用来处理不同语言自然形成的数据(本地化) |
< u t i l i t y {\rm utility} utility> | 包含被许多 C 包含被许多{\rm C} 包含被许多C++ 标准库头文件所用的类和函数 标准库头文件所用的类和函数 标准库头文件所用的类和函数 |
16.3.2 字符串类
定义字符串变量:
// 1.使用string类的默认构造函数创建string对象; string s1; // 2.使用string类的带参数构造函数创建string对象; string s2("FUXI AI.");
字符串的赋值和连接:
// 1.字符串变量赋值; string s1; string s2("FUXI AI."); s1 = "FUXI Technology."; s1 = s2; // 2.字符串变量使用运算符"+"连接; string s1("FUXI "); string s2("Technology"); string s3 = s1 + s2; string s4 = "FUXI " + s2; string s5 = s1 + "Technology.";
字符串的比较:
string s1("FUXI"); string s2("fuxi"); if(s1 == s2) { cout << "字符串s1,s2相同." << endl; } if(s1 > s2) { cout << "字符串s1大于s2." << endl; } if(s1 < s2) { cout << "字符串s1小于s2." << endl; }
字符串替换:
// 1.替换字符串中单个字符; string s1("FUXI Technology."); s1[0] = 'f'; // 2.replace()函数替换字符串; // replace()参数说明: // 参数1:被替换的起始位置; // 参数2:被替换的长度; // 参数3:替换后的字符串; string s2("FUXI Technology."); s1.replace(0, 4, "fuxi");
s t r i n g {\rm string} string类的特性:
int length() const; // 返回当前字符串的长度; bool empty() const; // 判断当前字符串是否为空; int capacity() const; // 返回当前变量的容量; // 实例: string s1("FUXI Technology."); cout << "s1的长度是:" << s1.length() << endl; if(s1.empty() == true) { cout << "s1字符串为空." << endl; } cout << "s1变量容量为:" << s1.capacity() << endl;
字符串类实例 ( e x a m p l e 16 _ 3. c p p ) ({\rm example16\_3.cpp}) (example16_3.cpp):实现简单的登录功能。
/** * 作者:罗思维 * 时间:2024/04/11 * 描述:实现简单的登录功能。 */ #include <string> #include <iostream> using namespace std; bool login(string name) { // 存储已经注册的用户名; string users[] = {"Willard", "Chen", "Ling"}; for (int i = 0; i < 3; i++) { if (name == users[i]) { return true; } } return false; } int main() { string name; cout << "Please input user's name:"; cin >> name; if (login(name) == true) { cout << "Login succeeded!" << endl; } else { cout << "Login Failed!" << endl; } return 0; }
// 测试用例1:登录成功用例; Please input user's name:Willard Login succeeded! // 测试用例2:登录失败用例; Please input user's name:Luo Login Failed!
16.3.3 常量和引用
常量在程序执行期间不能被改变,使用 c o n s t {\rm const} const定义, c o n s t {\rm const} const定义的常量具有变量的属性,有数据类型,占用内存空间,可以使用指针,编译时进行类型检查;
定义 c o n s t {\rm const} const常量时需要对其初始化,如果没有初始化,则编译报错;
引用变量是给已存在的变量取一个别名,其目的是在需要时能方便地间接使用原变量,引用变量必须要和一个已存在的变量绑定,且在定义引用变量的同时进行绑定,且引用变量的类型和绑定变量的类型必须相同,被绑定的变量称为"引用物";
引用变量绑定已存在的变量后就不能改变,且引用变量在定义时不需要分配内存空间,和绑定的变量拥有同一个内存空间,即定义引用变量不是创建新变量,只是给一个已存在的变量声明一个别名;
定义引用变量需要使用引用声明符: & {\rm \&} &;
引用变量的使用实例:
// 1.引用变量和取地址符区别; int iNumber; int &riNumber = iNumber; // 定义引用riNumber绑定变量iNumber,即iNumber别名为riNumber; int* piNumber = &iNumber; // 定义一个指针变量piNumber,piNumber指向iNumber的地址; // 2.引用使用错误反例; int ℞ // 错误,没有同时绑定变量,即没有进行初始化; int &rc = 100; // 错误,不能绑定常量; int iNumber; float &riNumber = iNumber; // 错误,绑定的变量类型不同;
引用实例 ( e x a m p l e 16 _ 4. c p p ) ({\rm example16\_4.cpp}) (example16_4.cpp):
/** * 作者:罗思维 * 时间:2024/04/11 * 描述:引用的使用实例。 */ #include <iostream> using namespace std; int main() { int iNumber = 10; int &riNumber = iNumber; cout << "iNumber = " << iNumber << endl; cout << "riNumber = " << riNumber << endl; riNumber = 20; cout << "riNumber改为20,iNumber = " << iNumber << endl; iNumber = 30; cout << "iNumber改为30,riNumber = " << riNumber << endl; cout << "iNumber地址为:" << &iNumber << endl; cout << "riNumber地址为:" << &riNumber << endl; return 0; }
// 结果展示: iNumber = 10 riNumber = 10 riNumber改为20,iNumber = 20 iNumber改为30,riNumber = 30 iNumber地址为:0x71feb8 riNumber地址为:0x71feb8
三种函数参数传递方式:按值传递、按地址传递、按引用传递 ( e x a m p l e 16 _ 5. c p p ) ({\rm example16\_5.cpp}) (example16_5.cpp)。
/** * 作者:罗思维 * 时间:2024/04/12 * 描述:按值传递、按地址传递、按引用传递实例。 * 实现交换两个整型变量数据的函数。 */ #include <iostream> using namespace std; // 参数按值传递; void swap1(int iNumber1, int iNumber2) { int temp = iNumber1; iNumber1 = iNumber2; iNumber2 = temp; } // 参数按地址传递; void swap2(int *piNumber1, int *piNumber2) { int temp = *piNumber1; *piNumber1 = *piNumber2; *piNumber2 = temp; } // 参数按引用传递; void swap3(int &iNumber1, int &iNumber2) { int temp = iNumber1; iNumber1 = iNumber2; iNumber2 = temp; } int main() { int iNumber1 = 100; int iNumber2 = 200; cout << "iNumber1、iNumber2初始值:" << endl; cout << "iNumber1 = " << iNumber1 << endl; cout << "iNumber2 = " << iNumber2 << endl; // 不起交换值的作用; cout << "参数按值传递,交换后结果:" << endl; swap1(iNumber1, iNumber2); cout << "iNumber1 = " << iNumber1 << endl; cout << "iNumber2 = " << iNumber2 << endl; // 交换两个数; cout << "参数按地址传递,交换后结果:" << endl; swap2(&iNumber1, &iNumber2); cout << "iNumber1 = " << iNumber1 << endl; cout << "iNumber2 = " << iNumber2 << endl; // 交换两个数,注:按地址传递已经交换过一次; cout << "参数按引用传递,交换后结果:" << endl; swap3(iNumber1, iNumber2); cout << "iNumber1 = " << iNumber1 << endl; cout << "iNumber2 = " << iNumber2 << endl; return 0; }
// 运行结果: iNumber1、iNumber2初始值: iNumber1 = 100 iNumber2 = 200 参数按值传递,交换后结果: iNumber1 = 100 iNumber2 = 200 参数按地址传递,交换后结果: iNumber1 = 200 iNumber2 = 100 参数按引用传递,交换后结果: iNumber1 = 100 iNumber2 = 200
引用的几点说明:
- 引用不是单独类型,需要和其他类型结合形成的组成类型;
- 引用在定义时,必须绑定同类型的变量;
- 引用变量不能修改,即引用变量只能绑定一个变量;
- 引用变量和绑定的变量共享同一个内存单元,不需要给引用变量分配内存;
- 函数如果需要修改实参数据,在实现同样功能情况下,优先选择按引用传递参数;
- 函数如果不修改实参数据,在实现同样功能的情况下,优先按 c o n s t {\rm const} const引用传递参数;
16.3.4 函数声明和实参类型转换以及默认实参
- 函数声明(函数原型)告诉编译器函数名称、函数返回数据的类型、函数预期接收的参数个数和参数的类型及顺序;
- 如果函数在调用前已经定义,则函数声明不是必须的,如果在函数定义前调用函数,则需要进行函数声明;
- 函数声明会把实参类型强制转换为函数声明中的形参类型,从低类型可以转换为高类型,高类型转换为低类型会造成数据丢失或产生不正确值的问题;
- 默认实参是给某些形参提供一个默认值,当一个程序在函数调用时,对于有默认实参的形参省略了其对应的实参时,编译器重写此函数调用并插入实参默认值作为函数调用所传递的实参;
- 默认实参必须是函数参数列表中最靠右边(尾部)的参数,当调用具有两个或更多默认实参的函数时,如果省略的实参不是参数列表中最靠右的参数,则该实参右边的所有也必须被省略;
- 默认参数应该在函数名第一次出现时指定,通常在函数声明中,如果将函数定义作为函数声明,则默认实参应该在函数首部中指定,默认值可以是任何表达式,包括常量、全局变量等;
16.3.5 作用域和作用域运算符
程序中标识符(变量、函数、类等)可以使用的范围称为标识符的作用域,分为:文件作用域(全局作用域)、语句块作用域(局部作用域)、类作用域、命名空间作用域;
文件作用域:声明于任何函数或类之外的标识符具有文件作用域,亦称为全局作用域;此类标识符从其声明处开始直到文件结尾处为止出现的所有代码都是"可见的",即可以访问的,位于函数之外的全局变量、函数定义、函数声明都具有文件作用域;
语句块作用域:在一个语句块中声明的标识符具有语句块作用域,亦称为局部作用域;语句块作用域开始于标识符的声明处,结束于标识符声明所在的语句的结束右花括号;
隐藏机制:如果语句块是嵌套的,当外层语句块中的一个标识符和内层语句块中的一个标识符具有相同的名字时,外层语句块的标识符处于隐藏状态,直到内层语句块执行结束为止;
作用域运算符:如果语句块作用域中需要使用文件作用域中的同名标识符,则需要使用作用域运算符,可以在某作用域中使用其他作用域中的标识符,即在同名标识符前加其他作用域名称和"::";
作用域运算符实例 ( e x a m p l e 16 _ 6. c p p ) ({\rm example16\_6.cpp}) (example16_6.cpp):
/** * 作者:罗思维 * 时间:2024/04/12 * 描述:作用域及作用域运算符实例。 */ #include <iostream> #include <string> using namespace std; string str = "全局变量"; int main() { string str = "局部变量"; cout << "字符串内容为:" <<::str << endl; // 访问全局作用域变量; { string str = "语句块内变量"; cout << "字符串内容为:" << str << endl; // 访问语句块作用域变量; } cout << "字符串内容为:" << str << endl; // 访问局部作用域变量; return 0; }
16.3.6 函数重载
C {\rm C} C++允许定义多个具有相同名字的函数,只要这些函数的参数不同,包括:参数类型或参数数量或参数类型的顺序不同,称为函数重载;
当调用一个重载函数时, C {\rm C} C++编译器通过检查函数调用中的实参数目、类型和顺序来选择恰当的函数,函数重载通常用于创建执行相似任务,但作用于不同的数据类型的具有相同名字的多个函数;
函数重载指的是同一作用域下的同名函数,函数重载定义的规则:
- 函数必须是在同一作用域下;
- 函数间的参数列表不同:参数数量不同、参数类型不同;
- 判断参数数量是否相同时不计算默认参数;
- 不考虑函数返回类型;
C {\rm C} C++是通过实参类型和形参类型的匹配完成的,实参的数量必须和形参数量一致,后逐一匹配实参和形参的类型,找到完全匹配的函数就调用此函数,如果没有找到实参类型和形参类型匹配的函数,则报错;
函数重载实例 ( e x a m p l e 16 _ 7. c p p ) ({\rm example16\_7.cpp}) (example16_7.cpp):
/** * 作者:罗思维 * 时间:2024/04/12 * 描述:实现比较整型、浮点型和字符型数据的功能。 */ #include <iostream> #include <string> using namespace std; int compare(int iNumber1, int iNumber2) { cout << "整型数据比较:"; if (iNumber1 == iNumber2) { return 0; } return (iNumber1 > iNumber2) ? 1 : -1; } int compare(float fNumber1, float fNumber2) { cout << "浮点型数据比较:"; if (fNumber1 == fNumber2) { return 0; } return (fNumber1 > fNumber2) ? 1 : -1; } int compare(char cChar1, char cChar2) { cout << "字符型数据比较:"; if (cChar1 == cChar2) { return 0; } return (cChar1 > cChar2) ? 1 : -1; } int main() { int iRet, fRet, cRet; float fNumber1 = 1.314, fNumber2 = 5.20; iRet = compare(100, 200); cout << "iRet的值为:" << iRet << endl; fRet = compare(fNumber1, fNumber2); cout << "fRet的值为:" << fRet << endl; cRet = compare('W', 'C'); cout << "cRet的值为:" << cRet << endl; return 0; }
16.3.7 使用new和delete运算符动态管理内存
C {\rm C} C语言中常用的与动态管理内存相关的库函数:
// 1.函数1:在堆中分配内存空间,size是分配的字节数; void *malloc(size_t size); // 2.函数2:在堆中分配内存空间,并用0初始化每个元素,num是元素数量,size是每个元素的字节数; void *calloc(size_t num,size_t size); // 3.函数3:在堆中重新分配块,memblock指向已分配的内存块,size是新分配的字节数; void *realloc(void *memblock,size_t size); // 4.函数4:释放已分配的内存块,memblock指向已分配的内存块; void free(void *memblock); // 5.函数5:在栈中分配存储空间,使用完毕后自动释放,size是分配的字节数; void *alloca(size_t size);
为单个变量分配内存和释放单个变量的内存:
类型* p = new 类型 (初值); delete p;
为多个变量分配内存和释放多个变量的内存:
类型* p = new 类型[size]; delete [] p;
动态内存分配实例 ( e x a m p l e 16 _ 8. c p p ) ({\rm example16\_8.cpp}) (example16_8.cpp):
/** * 作者:罗思维 * 时间:2024/04/12 * 描述:动态内存分配实例。 */ #include <iostream> #include <string> using namespace std; int main() { int iNum; int* p; // C语言风格; printf("C风格动态内存分配\n"); printf("输入数据个数:"); scanf("%d", &iNum); p = (int*) malloc(iNum * sizeof(int)); for (int i = 0; i < iNum; i++) { scanf("%d", &p[i]); } for (int i = 0; i < iNum; i++) { printf("%d", p[i]); } free(p); cout << endl; // C++风格; cout << "C++风格动态内存分配" << endl; cout << "输入数据个数:"; cin >> iNum; p = new int[iNum]; for (int i = 0; i < iNum; i++) { cin >> p[i]; } for (int i = 0; i < iNum; i++) { cout << p[i] << " "; } delete [] p; return 0; }