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

c++:-3

2022-05-05 10:23 1561 查看

上一节学习了C的函数:c++:-2,本节学习C的数组、指针和字符串

##数组 ###定义和初始化 ####定义 例如:int a[10]; 表示a为整型数组,有10个元素:a[0]...a[9]

例如: int a[5][3]; 表示a为整型二维数组,其中第一维有5个下标(04),第二维有3个下标(02),数组的元素个数为15,可以用于存放5行3列的整型数据表格。

####使用

  • 必须先声明,后使用。
  • 只能逐个引用数组元素,而不能一次引用整个数组

例如:a[0]=a[5]+a[7]-a[2*3] 例如:b[1][2]=a[2][3]/2

####存储 (1)一维数组的存储 数组元素在内存中顺次存放,它们的地址是连续的。元素间物理地址上的相邻,对应着逻辑次序上的相邻。 (2)二维数组的存储 按行存放 例如: float a[3][4]; 可以理解为: 其中数组a的存储顺序为:

####初始化 (1)一维数组的初始化 * 在定义数组时给出数组元素的初始值。 * 列出全部元素的初始值 例如:static int a[10]={0,1,2,3,4,5,6,7,8,9}; * 可以只给一部分元素赋初值 例如:static int a[10]={0,1,2,3,4}; * 在对全部数组元素赋初值时,可以不指定数组长度 例如:static int a[]={0,1,2,3,4,5,6,7,8,9} (2)二维数组的初始化 * 将所有初值写在一个{}内,按顺序初始化 例如:static int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12 * 分行列出二维数组元素的初值 例如:static int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; * 可以只对部分元素初始化 例如:static int a[3][4]={{1},{0,6},{0,0,11}}; * 列出全部初始值时,第1维下标个数可以省略 例如:static int a[][4]={1,2,3,4,5,6,7,8,9,10,11,12}; 或:static int a[][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; * 注意: 如果不作任何初始化,内部auto型数组中会存在**垃圾数据**,static数组中的数据**默认初始化为0**; 如果只对部分元素初始化,剩下的未显式初始化的元素,将**自动被初始化为零**; ###传参 * 数组元素作实参,与单个变量一样。 * 数组名作参数,形、实参数都应是数组名(实质上是地址),类型要一样,传送的是数组首地址。 * **对形参数组的改变会直接影响到实参数组。** ###对象数组 ####定义和访问 (1)定义对象数组 类名 数组名[元素个数]; (2)访问对象数组元素 通过下标访问:数组名[下标].成员名 ####初始化 * 数组中每一个元素对象被创建时,系统都会调用类**构造函数初始化**该对象。 * 通过初始化列表赋值。 例:Point a[2]={Point(1,2),Point(3,4)}; * 如果没有为数组元素指定显式初始值,数组元素便使用默认值初始化(调用**默认构造函数**)。 * 元素所属的类不声明构造函数,则采用默认构造函数。 * 各元素对象的初值要求为相同的值时,可以声明具有默认形参值的构造函数。 * 各元素对象的初值要求为不同的值时,需要声明带形参的构造函数。 * 当数组中每一个对象被删除时,系统都要调用一次**析构函数**。 ###基于范围的for循环 ####原for循环 ``` #include "iostream" using namespace std; int main() { int array[3] = {1, 2, 3}; int *p; for (p = array; p < array + sizeof(array) / sizeof(int); ++p) { *p += 2; std::cout << *p << std::endl; } } ``` ####新for循环 ``` #include "iostream" using namespace std; int main() { int array[3] = {1, 2, 3}; for(int & e : array) { e += 2; std::cout<<e<<std::endl; } return 0; } ``` ##指针 (1)内存空间的访问方式 * 通过变量名访问 * 通过地址访问 (2)指针的概念 * 指针:内存地址,用于间接访问内存单元 * 指针变量:用于存放地址的变量 ###定义 (1)例: static int i; static int* ptr = &i; ![](https://img2022.cnblogs.com/blog/1928790/202205/1928790-20220503164147278-5013407.png) (2)例: *ptr = 3; ![](https://img2022.cnblogs.com/blog/1928790/202205/1928790-20220503164200658-412201085.png) (3)与地址相关的运算——“*”和“&” * 指针运算符:* * **地址运算符:&** ###初始化 (1)语法形式:存储类型 数据类型 *指针名=初始地址; 例:int *pa = &a; (2)注意事项 * 用变量地址作为初值时,该变量必须在指针初始化之前已声明过,且变量类型应与指针类型一致。 * 可以用一个已有合法值的指针去初始化另一个指针变量。 * 不要用一个内部非静态变量去初始化 static 指针。 ###赋值 (1)语法形式:指针名=地址 注意:“地址”中存放的数据类型与指针类型必须相符 (2)向指针变量赋的值必须是**地址常量或变量**,不能是普通整数。例如: * 通过地址运算“&”求得已定义的变量和对象的起始地址 * 动态内存分配成功时返回的地址 (3)例外:整数0可以赋给指针,表示**空指针**。 (4)允许定义或声明指向 **void 类型的指针**。该指针可以被赋予任何类型对象的地址。 例: void *general; ####指针空值nullptr (1)以往用0或者NULL去表达空指针的问题: C/C++的NULL宏是个被有很多潜在BUG的宏。因为有的库把其定义成整数0,有的定义成 (void*)0。在C的时代还好。但是在C++的时代,这就会引发很多问题。 (2)C++11使用`nullptr`关键字,是表达更准确,类型安全的空指针 ####指向常量的指针 > 不能通过指向常量的指针改变所指对象的值,但指针本身可以改变,可以指向另外的对象。 例: ``` int a; const int *p1 = &a; //p1是指向常量的指针 int b; p1 = &b; //正确,p1本身的值可以改变 *p1 = 1; //编译时出错,不能通过p1改变所指的对象(常量) ``` ####指针类型的常量 > 若声明指针常量,则指针本身的值不能被改变。 例: ``` int a; int * const p2 = &a; p2 = &b; //错误,p2是指针常量,不能改变 ``` ###运算 ####算术运算 * 指针p加上或减去n 其意义是指针当前指向位置的前方或后方第n个数据的起始位置。 * 指针的++、--运算 意义是指向下一个或前一个完整数据的起始。 * 运算的结果值取决于指针指向的数据类型,总是指向一个完整数据的起始位置。 * 当指针指向连续存储的同类型数据时,指针与整数的加减运和自增自减算才有意义。 (1)指针与整数相加的意义 ![](https://img2022.cnblogs.com/blog/1928790/202205/1928790-20220503165150976-1711539988.png) ####关系运算 * 指向相同类型数据的指针之间可以进行各种关系运算。 * 指向不同数据类型的指针,以及指针与一般整数变量之间的关系运算是无意义的。 * 指针可以和零之间进行等于或不等于的关系运算。 例如:p==0或p!=0 ###指针与数组 ####用指针访问数组 > 数组是一组连续存储的同类型数据,可以通过指针的算术运算,使指针依次指向数组的各个元素,进而可以遍历数组。 (1)定义与赋值: 例:int a[10], *pa; pa=&a[0]; 或 pa=a; 经过上述定义及赋值后: *pa就是a[0],*(pa+1)就是a[1],... ,*(pa+i)就是a[i]。a[i], *(pa+i), *(a+i), pa[i]都是等效的。 (2)注意: 不能写 a++,因为a是数组首地址、是常量。 (3)举例: 设有一个int型数组a,有10个元素。用三种方法输出各元素: 方法1:使用数组名和下标 ``` #include "iostream" using namespace std; int main() { int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; for (int i = 0; i < 10; i++) cout << a[i] << " "; cout << endl; return 0; } ``` 方法2:使用数组名和指针运算 ``` #include "iostream" using namespace std; int main() { int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; for (int i = 0; i < 10; i++) cout << *(a+i) << " "; cout << endl; return 0; } ``` 方法3:使用指针变量 ``` #include "iostream" using namespace std; int main() { int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; for (int *p = a; p < (a + 10); p++) cout << *p << " "; cout << endl; return 0; } ``` ###指针与函数 ####以指针作为函数参数 > 为什么需要用指针做参数? * 需要**数据双向传递**时(引用也可以达到此效果) * 用指针作为函数的参数,可以使被调函数通过形参指针存取主调函数中实参指针指向的数据,实现数据的双向传递 * 需要传递一组数据,只传首地址运行效率比较高 * 实参是数组名时形参可以是指针 ####指针类型的函数 ![](https://img2022.cnblogs.com/blog/1928790/202205/1928790-20220504163149878-1599706988.png) (1)不要将非静态局部地址用作函数的返回值 ![](https://img2022.cnblogs.com/blog/1928790/202205/1928790-20220504163256952-1577017581.png) (2)例子 ``` #include "iostream" using namespace std; int *search(int *a,int num) { for(int i=0;i<10;i++) { if(a[i]==0) return &a[i]; } } int main() { int arr[10]; int *search(int *a,int num); for(int i=0;i<10;i++){ cin >> arr[i]; } int *zeroptr= search(arr,10); return 0; } ``` ####指向函数的指针 (1)定义形式 存储类型 数据类型 (*函数指针名)(); (2)含义 函数指针指向的是程序代码存储区。 (3)用途 > 实现函数回调 * 通过函数指针调用的函数 * 例如将函数的指针作为参数传递给一个函数,使得在处理相似事件的时候可以灵活的使用不同的方法。 调用者不关心谁是被调用者 * 需知道存在一个具有特定原型和限制条件的被调用函数。 ###对象指针 (1)定义 类名 *对象指针名; (2)例 ``` Point a(5,10); Piont *ptr; ptr=&a; ``` (3)通过指针访问对象成员 对象指针名->成员名 例:**ptr->getx()** 相当于** (*ptr).getx();** (4)举例 ``` #include <iostream> using namespace std; class Point { public: Point(int x = 0, int y = 0) : x(x), y(y) { } int getX() const { return x; } int getY() const { return y; } private: int x, y; }; int main() { Point a(4, 5); Point *p1 = &a; //定义对象指针,用a的地址初始化 cout << p1->getX() << endl;//用指针访问对象成员 cout << a.getX() << endl; //用对象名访问对象成员 return 0; } 输出: 4 4 ``` ####this指针 * 指向当前对象自己 * **隐含于类的每一个非静态成员函数中** * 指出成员函数所操作的对象 当通过一个对象调用成员函数时,系统先将该对象的地址赋给this指针,然后调用成员函数,成员函数对对象的数据成员进行操作时,就隐含使用了this指针。 * 例如:Point类的getX函数中的语句:return x; 相当于: return this->x; * 例 ``` class Fred; //前向引用声明 class Barney { Fred *x; }; class Fred { Barney y; }; ``` ###动态内存分配 ####分配与释放 (1)动态申请内存操作符 new new 类型名T(初始化参数列表) 功能:在程序执行期间,申请用于存放T类型对象的内存空间,并依初值列表赋以初值。 结果值:成功:T类型的指针,指向新分配的内存;失败:抛出异常。 (2)释放内存操作符delete delete 指针p 功能:释放指针p所指向的内存。p必须是new操作的返回值。 (3)举例 ``` #include <iostream> using namespace std; class Point { public: Point() : x(0), y(0) { cout<<"Default Constructor called."<<endl; } Point(int x, int y) : x(x), y(y) { cout<< "Constructor called."<<endl; } ~Point() { cout<<"Destructor called."<<endl; } int getX() const { return x; } int getY() const { return y; } void move(int newX, int newY) { x = newX; y = newY; } private: int x, y; }; int main() { cout << "Step one: " << endl; Point *ptr1 = new Point; //调用默认构造函数 delete ptr1; //删除对象,自动调用析构函数 cout << "Step two: " << endl; ptr1 = new Point(1,2); delete ptr1; return 0; } 输出: Step one: Default Constructor called. Destructor called. Step two: Constructor called. Destructor called. ``` ####动态数组 ![](https://img2022.cnblogs.com/blog/1928790/202205/1928790-20220504220541907-455179195.png) ![](https://img2022.cnblogs.com/blog/1928790/202205/1928790-20220504220627064-2098691231.png) ####动态多维数组 ![](https://img2022.cnblogs.com/blog/1928790/202205/1928790-20220504220659381-905364011.png) ![](https://img2022.cnblogs.com/blog/1928790/202205/1928790-20220504220725010-222090018.png) ![](https://img2022.cnblogs.com/blog/1928790/202205/1928790-20220504220809896-1190495794.png) ###智能指针 * 显式管理内存在是能上有优势,但容易出错。 * C++11提供智能指针的数据类型,对垃圾回收技术提供了一些支持,实现一定程度的内存管理 * 三类: unique_ptr :不允许多个指针共享资源,可以用标准库中的move函数转移指针 shared_ptr :多个指针共享资源 weak_ptr :可复制shared_ptr,但其构造或者释放对资源不产生影响 ###vector对象 (1)为什么需要vector? 封装任何类型的动态数组,自动创建和删除。 数组下标越界检查。 (2)vector对象的定义 vector<元素类型> 数组对象名(数组长度); (3)例: ``` vector<int> arr(5)//建立大小为5的int数组 ``` ####vector对象的使用 (1)对数组元素的引用 与普通数组具有相同形式:vector对象名 [ 下标表达式 ] (2)vector数组对象名不表示数组首地址 获得数组长度用`size`函数:数组对象名.size() (3)举例: ``` #include <iostream> #include <vector> using namespace std; //计算数组arr中元素的平均值 double average(const vector<double> &arr) //vector的引用 { double sum = 0; for (unsigned i = 0; i<arr.size(); i++) sum += arr[i]; return sum / arr.size(); } int main() { unsigned n; cout << "n = "; cin >> n; vector<double> arr(n); //创建数组对象 cout << "Please input " << n << " real numbers:" << endl; for (unsigned i = 0; i < n; i++) cin >> arr[i]; cout << "Average = " << average(arr) << endl; return 0; } 输出: n = 5 Please input 5 real numbers: 1 2 3 4 6 Average = 3.2 ``` ###深拷贝 ####浅层复制 实现对象间数据元素的一一对应复制。 举例: ``` #include <iostream> #include <cassert> using namespace std; class Point { //类的声明同例6-16 //…… }; class ArrayOfPoints { //类的声明同例6-18 //…… }; int main() { int count; cout << "Please enter the count of points: "; cin >> count; ArrayOfPoints pointsArray1(count); //创建对象数组 pointsArray1.element(0).move(5,10); pointsArray1.element(1).move(15,20); ArrayOfPoints pointsArray2(pointsArray1); //创建副本 cout << "Copy of pointsArray1:" << endl; cout << "Point_0 of array2: " << pointsArray2.element(0).getX() << ", " << pointsArray2.element(0).getY() << endl; cout << "Point_1 of array2: " << pointsArray2.element(1).getX() << ", " << pointsArray2.element(1).getY() << endl; pointsArray1.element(0).move(25, 30); pointsArray1.element(1).move(35, 40); cout<<"After the moving of pointsArray1:"<<endl; cout << "Point_0 of array2: " << pointsArray2.element(0).getX() << ", " << pointsArray2.element(0).getY() << endl; cout << "Point_1 of array2: " << pointsArray2.element(1).getX() << ", " << pointsArray2.element(1).getY() << endl; return 0; } 运行结果如下: Please enter the number of points:2 Default Constructor called. Default Constructor called. Copy of pointsArray1: Point_0 of array2: 5, 10 Point_1 of array2: 15, 20 After the moving of pointsArray1: Point_0 of array2: 25, 30 Point_1 of array2: 35, 40 Deleting... Destructor called. Destructor called. Deleting... 接下来程序出现运行错误。 ``` ![](https://img2022.cnblogs.com/blog/1928790/202205/1928790-20220504233639472-791096818.png) ####深层复制 当被复制的对象数据成员是指针类型时,不是复制该指针成员本身,而是**将指针所指对象进行复制**。 > 当返回的对象含有动态创建的空间时,需要用深层复制 ``` #include <iostream> #include <cassert> using namespace std; class Point { //类的声明同例6-16 }; class ArrayOfPoints { public: ArrayOfPoints(const ArrayOfPoints& pointsArray); //其他成员同例6-18 }; ArrayOfPoints::ArrayOfPoints(const ArrayOfPoints& v) { size = v.size; points = new Point[size];//重新创建空间 for (int i = 0; i < size; i++) points[i] = v.points[i]; } int main() { //同例6-20 } 程序的运行结果如下: Please enter the number of points:2 Default Constructor called. Default Constructor called. Default Constructor called. Default Constructor called. Copy of pointsArray1: Point_0 of array2: 5, 10 Point_1 of array2: 15, 20 After the moving of pointsArray1: Point_0 of array2: 5, 10 Point_1 of array2: 15, 20 Deleting... Destructor called. Destructor called. Deleting... Destructor called. Destructor called. ``` ![](https://img2022.cnblogs.com/blog/1928790/202205/1928790-20220504233907326-988601576.png) ###移动构造函数 > 在现实中有很多这样的例子,我们将钱从一个账号转移到另一个账号,将手机SIM卡转移到另一台手机,将文件从一个位置剪切到另一个位置……移动构造可以减少不必要的复制,带来性能上的提升。 * C++11标准中提供了一种新的构造方法——移动构造。 * C++11之前,如果要将源对象的状态转移到目标对象只能通过复制。在某些情况下,我们没有必要复制对象——只需要移动它们。 * C++11引入移动语义:源对象资源的控制权全部交给目标对象 ####问题 当临时对象在被复制后,就不再被利用了。我们完全可以把临时对象的资源直接移动,这样就避免了多余的复制操作。 ![](https://img2022.cnblogs.com/blog/1928790/202205/1928790-20220504234051030-1875685104.png) ####移动构造 > 什么时候该触发移动构造? 有可被利用的临时对象 移动构造函数:class_name ( class_name && ) ##字符串 ###字符数组 * 字符串常量 例:"program" * 各字符连续、顺序存放,每个字符占一个字节,以‘\0’结尾,相当于一个隐含创建的字符常量数组 * “program”出现在表达式中,表示这一char数组的首地址 * 首地址可以赋给char常量指针: const char *STRING1 = "program"; ![](https://img2022.cnblogs.com/blog/1928790/202205/1928790-20220505001926424-1234556356.png) ####缺点 * 执行连接、拷贝、比较等操作,都需要显式调用库函数,很麻烦 * 当字符串长度很不确定时,需要用new动态创建字符数组,最后要用delete释放,很繁琐 * 字符串实际长度大于为它分配的空间时,会产生数组下标越界的错误 ###string 使用字符串类string表示字符串 string实际上是对字符数组操作的封装 ####构造函数 (1)string(); //默认构造函数,建立一个长度为0的串 例: string s1; (2)string(const char *s); //用指针s所指向的字符串常量初始化string对象 例: string s2 = “abc”; (3)string(const string& rhs); //复制构造函数 例: string s3 = s2; ####常用操作 * s + t 将串s和t连接成一个新串 * s = t 用t更新s * s == t 判断s与t是否相等 * s != t 判断s与t是否不等 * s < t 判断s是否小于t(按字典顺序比较) * s <= t 判断s是否小于或等于t (按字典顺序比较) * s > t 判断s是否大于t (按字典顺序比较) * s >= t 判断s是否大于或等于t (按字典顺序比较) * s[i] 访问串中下标为i的字符 例: ``` string s1 = "abc", s2 = "def"; string s3 = s1 + s2; //结果是"abcdef" bool s4 = (s1 < s2); //结果是true char s5 = s2[1]; //结果是'e' ``` ####getline > 如何输入整行字符串? 用cin的>>操作符输入字符串,会以空格作为分隔符,空格后的内容会在下一回输入时被读取 * getline可以输入整行字符串(要包string头文件),例如:getline(cin, s2); * 输入字符串时,可以使用其它分隔符作为字符串结束的标志(例如逗号、分号),将分隔符作为getline的第3个参数即可,例如:getline(cin, s2, ','); ##程序 ###数组 ####存放数组 > 求Fibonacci数列的前20项 ``` #include <iostream> using namespace std; int main() { int i; int f[20] = {1,1}; //初始化第0、1个数 for (i = 2; i < 20; i++) //求第2~19个数 f[i] = f[i - 2] + f[i - 1]; for (i=0;i<20;i++) { //输出,每行5个数 if (i % 5 == 0) cout << endl; cout.width(12); //设置输出宽度为12 cout << f[i]; } return 0; } 输出: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 ``` ####统计正确率 > 循环从键盘读入若干组选择题答案,计算并输出每组答案的正确率,直到输入ctrl+z为止。每组连续输入5个答案,每个答案可以是'a','b','c','d'。 ``` #include <iostream> using namespace std; int main() { const char key[ ] = {'a','c','b','a','d'}; const int NUM_QUES = 5; char c; int ques = 0, numCorrect = 0; cout << "Enter the " << NUM_QUES << " question tests:" << endl; while(cin.get(c)) //cin.get(c)得到的是输出流,所以只需要一次输入 { if(c != '\n') { if(c == key[ques]) { numCorrect++; cout << " "; } else cout<<"*"; ques++; } else { cout << " Score " << static_cast<float>(numCorrect)/NUM_QUES*100 << "%"; ques = 0; numCorrect = 0; cout << endl; } } return 0; } 输出: Enter the 5 question tests: abcda **** Score 20% abcad ** Score 60% cabcd ** * Score 40% ddaca ***** Score 0% ``` ####使用数组名作为函数参数 > 主函数中初始化一个二维数组,表示一个矩阵,矩阵,并将每个元素都输出,然后调用子函数,分别计算每一行的元素之和,将和直接存放在每行的第一个元素中,返回主函数之后输出各行元素的和。 ``` #include <iostream> using namespace std; void rowSum(int a[][4], int nRow) { for (int i = 0; i < nRow; i++) { for(int j = 1; j < 4; j++) a[i][0] += a[i][j]; } } int main() { //主函数 //定义并初始化数组 int table[3][4] = {{1, 2, 3, 4}, {2, 3, 4, 5}, {3, 4, 5, 6}}; //输出数组元素 for (int i = 0; i < 3; i++) { for (int j = 0; j < 4; j++) cout << table[i][j] << " "; cout << endl; } rowSum(table, 3); //调用子函数,传入的是数组名,计算各行和 //输出计算结果 for (int i = 0; i < 3; i++) cout << "Sum of row " << i << " is " << table[i][0] << endl; return 0; } 输出: 1 2 3 4 2 3 4 5 3 4 5 6 Sum of row 0 is 10 Sum of row 1 is 14 Sum of row 2 is 18 ``` ####对象数组应用 ``` //Point.h #ifndef _POINT_H #define _POINT_H class Point { //类的定义 public: //外部接口 Point(); Point(int x, int y); ~Point(); void move(int newX,int newY); int getX() const { return x; } int getY() const { return y; } static void showCount(); //静态函数成员 private: //私有数据成员 int x, y; }; #endif //_POINT_H //Point.cpp #include <iostream> #include "Point.h" using namespace std; Point::Point() : x(0), y(0) { cout << "Default Constructor called." << endl; } Point::Point(int x, int y) : x(x), y(y) { cout << "Constructor called." << endl; } Point::~Point() { cout << "Destructor called." << endl; } void Point::move(int newX,int newY) { cout << "Moving the point to (" << newX << ", " << newY << ")" << endl; x = newX; y = newY; } #include "Point.h" #include <iostream> using namespace std; int main() { cout << "Entering main..." << endl; Point a[2]; for(int i = 0; i < 2; i++) a[i].move(i + 10, i + 20); cout << "Exiting main..." << endl; return 0; } 输出: Entering main... Default Constructor called. Default Constructor called. Moving the point to (10, 20) Moving the point to (11, 21) Exiting main... Destructor called. Destructor called. ``` ###指针 ####void指针 ``` #include <iostream> using namespace std; int main() { //!void voidObject; 错,不能声明void类型的变量 void *pv; //对,可以声明void类型的指针 int i = 5; pv = &i; //void类型指针指向整型变量 int *pint = static_cast<int *>(pv); //void指针转换为int指针 cout << "*pint = " << *pint << endl; return 0; } 输出: *pint = 5 ``` ####指针表示矩阵 ``` #include "iostream" using namespace std; int main() { int line1[]={1,0,1}; int line2[]={1,0,1}; int line3[]={1,0,1}; //定义整型指针数组并初始化 int *pline[3]={line1,line2,line3}; cout <<"矩阵:"<< endl; //输出 for(int i=0;i<3;i++) { for(int j=0;j<3;j++) { cout <<pline[i][j]<<" "; } cout << endl; } return 0; } 输出: 矩阵: 1 0 1 1 0 1 1 0 1 ``` ####指针做形参 > 读入三个浮点数,将整数部分和小数部分分别输出 ``` #include "iostream" using namespace std; void splitFloat(float x, int *intPart, float *fracPart) { *intPart = static_cast<int>(x); //取x的整数部分 *fracPart = x - *intPart; //取x的小数部分 } int main() { cout << "Enter 3 float point numbers:" << endl; for(int i = 0; i < 3; i++) { float x, f; int n; cin >> x; splitFloat(x, &n, &f); //变量地址作为实参 cout << "Integer Part = " << n <<endl<< "Fraction Part = " << f << endl; } return 0; } ``` > 指向常量的指针做形参 ``` #include "iostream" using namespace std; const int N = 6; void print(const int *p, int n); int main() { int array
; for (int i = 0; i < N; i++) cin>>array[i]; print(array, N); return 0; } void print(const int *p, int n) { cout << "{ " << *p; for (int i = 1; i < n; i++) cout << ", " << *(p+i); cout << " }" << endl; } 输出: 1 2 3 4 5 6 { 1, 2, 3, 4, 5, 6 } ``` ####函数指针 > 编写一个计算函数compute,对两个整数进行各种计算。有一个形参为指向具体算法函数的指针,根据不同的实参函数,用不同的算法进行计算。编写三个函数:求两个整数的最大值、最小值、和。分别用这三个函数作为实参,测试compute函数 ``` #include <iostream> using namespace std; int compute(int a, int b, int(*func)(int, int)) { return func(a, b);} int max(int a, int b) // 求最大值 { return ((a > b) ? a: b);} int min(int a, int b) // 求最小值 { return ((a < b) ? a: b);} int sum(int a, int b) // 求和 { return a + b;} int main() { int a, b, res; cout << "请输入整数a:"; cin >> a; cout << "请输入整数b:"; cin >> b; res = compute(a, b, & max); cout << "Max of " << a << " and " << b << " is " << res << endl; res = compute(a, b, & min); cout << "Min of " << a << " and " << b << " is " << res << endl; res = compute(a, b, & sum); cout << "Sum of " << a << " and " << b << " is " << res << endl; } 输出: 请输入整数a:12 请输入整数b:32 Max of 12 and 32 is 32 Min of 12 and 32 is 12 Sum of 12 and 32 is 44 ``` ####动态数组类 ``` #include <iostream> #include <cassert> using namespace std; class Point { //类的声明同例6-16 … }; class ArrayOfPoints { //动态数组类 public: ArrayOfPoints(int size) : size(size) { points = new Point[size]; } ~ArrayOfPoints() { cout << "Deleting..." << endl; delete[] points; } Point& element(int index) { assert(index >= 0 && index < size); return points[index]; } private: Point *points; //指向动态数组首地址 int size; //数组大小 }; int main() { int count; cout << "Please enter the count of points: "; cin >> count; ArrayOfPoints points(count); //创建数组对象 points.element(0).move(5, 0); //访问数组元素的成员 points.element(1).move(15, 20); //访问数组元素的成员 return 0; } 运行结果: Please enter the number of points:2 Default Constructor called. Default Constructor called. Deleting... Destructor called. Destructor called. ``` ####vector数组 ``` #include <vector> #include <iostream> int main() { std::vector<int> v = {1,2,3}; for(auto i = v.begin(); i != v.end(); ++i) std::cout << *i << std::endl; for(auto e : v)//带范围的for循环 std::cout << e << std::endl; } ``` ####移动构造 > 函数返回含有指针成员的对象 (1)先给出使用深层复制构造函数,返回时构造临时对象,动态分配将临时对象返回到主调函数,然后删除临时对象。 ``` #include<iostream> using namespace std; class IntNum { public: IntNum(int x = 0) : xptr(new int(x)){ //构造函数 cout << "Calling constructor..." << endl; } //n是参数对象的引用;*n.xptr取值;xptr(new int(*n.xptr)是深拷贝 IntNum(const IntNum & n) : xptr(new int(*n.xptr)){//复制构造函数 cout << "Calling copy constructor..." << endl; }; ~IntNum(){ //析构函数 delete xptr; cout << "Destructing..." << endl; } int getInt() { return *xptr; } private: int *xptr; }; //返回值为IntNum类对象 IntNum getNum() { IntNum a;//新建一个局部对象,调用构造函数 return a; //返回一个对象,调用复制构造函数 } int main() { IntNum p=getNum(); cout<<p.getInt()<<endl; return 0; } 输出: Calling constructor... Calling copy constructor... Destructing... 0 Destructing... ``` (2)使用移动构造函数,将要返回的局部对象转移到主调函数,省去了构造和删除临时对象的过程。 ``` #include<iostream> using namespace std; class IntNum { public: IntNum(int x = 0) : xptr(new int(x)){ //构造函数 cout << "Calling constructor..." << endl; } IntNum(const IntNum & n) : xptr(new int(*n.xptr)){//复制构造函数 cout << "Calling copy constructor..." << endl; //注:•&&是右值引用;•函数返回的临时变量是右值 } IntNum(IntNum && n): xptr( n.xptr){ //移动构造函数 n.xptr = nullptr; cout << "Calling move constructor..." << endl; } ~IntNum(){ //析构函数 delete xptr; cout << "Destructing..." << endl; } int getInt() { return *xptr; } private: int *xptr; }; //返回值为IntNum类对象 IntNum getNum() { IntNum a; return a; } int main() { cout << getNum().getInt() << endl; return 0; } 输出: Calling constructor... Calling move constructor... Destructing... 0 Destructing... ``` >上面两个程序输出是有问题的,待解决! ###字符串 ####string ``` #include <string> #include <iostream> using namespace std; //根据value的值输出true或false //title为提示文字 inline void test(const char *title, bool value) { cout << title << " returns " << (value ? "true" : "false") << endl; } int main() { string s1 = "DEF"; cout << "s1 is " << s1 << endl; string s2; cout << "Please enter s2: "; cin >> s2; cout << "length of s2: " << s2.length() << endl; //比较运算符的测试 test("s1 <= \"ABC\"", s1 <= "ABC"); test("\"DEF\" <= s1", "DEF" <= s1); //连接运算符的测试 s2 += s1; cout << "s2 = s2 + s1: " << s2 << endl; cout << "length of s2: " << s2.length() << endl; return 0; } 输出: s1 is DEF Please enter s2: eqwe length of s2: 4 s1 <= "ABC" returns false "DEF" <= s1 returns true s2 = s2 + s1: eqweDEF length of s2: 7 ``` ####getline ``` #include <iostream> #include <string> using namespace std; int main() { for (int i = 0; i < 2; i++){ string city, state; getline(cin, city, ','); getline(cin, state); cout << "City:" << city << ",State:" << state << endl; } return 0; } 输出: San Francisco,the United States City:San Francisco,State:the United States Beijing,China City:Beijing,State:China ``` ##习题 (1)以下关于地址和指针的叙述中正确的是 * 可以取变量的地址赋值给同类型的指针变量(对) * 可以取常量的地址赋值给同类型的指针变量 * 可以取一个指针变量的地址赋给本指针变量,这样就使得指针变量指向自身 * 所有指针变量如果未赋初值,则自动赋空值NULL 解析:A选项正确。常量存储在编译文件中,不能取地址。B选项错误。一个指针变量的地址只能赋给指向这种类型的指针变量,与其本身类型不同,不能赋值,C选项错误。未赋初值的指针变量自动赋任意地址值,D选项错误。 (2)要定义一个引用变量p使之引用类MyClass的一个对象,正确的定义语句是 * MyClass p=MyClass; * MyClass p=new MyClass; * MyClass &p=new MyClass; * MyClass a, &p=a;(对),别名 (3)在C++的动态存储分配,下列说法正确的是? * new和delete是C++语言中专门用于动态内存分配和释放的函数 * 动态分配的内存空间也可以被初始化(对) * 当系统内存不够时,会自动回收不再使用的内存单元,因此程序中不必用delete释放内存空间 * 当动态分配内存失败时,系统会立刻崩溃,因此一定要慎用new 分析:选项A,错,**new 与delete是用于动态平衡分配与释放空间的运算符**,不是函数;选项C,错,要使用delete释放空间,系统会统一管理,而不用delete释放的空间,会造成内存泄漏,这种程序用的次数多,会造成内存耗尽;选项D,错,不成功,会返回0。 (4)C++11中,&表示左值引用;**&&表示右值引用**——下列关于左值和右值的说法正确的是 * 表达式等号左边的是左值,等号右边的是右值 * 左值是指表达式结束后依然存在的持久对象(对) * 右值是指表达式结束后依然存在的持久对象 * 可以对右值取地址 分析:左值和右值都是针对表达式而言的,**左值是指表达式结束后依然存在的持久对象**,**右值指表达式结束时就不再存在的临时对象**——显然右值不可以被取地址。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: