C++实例——第六章

  • Post author:
  • Post category:其他




数组的定义与使用

类型说明符 数组名[常量表达式][常量表达式]…

int a[5][3]

从0开始数的

#include <iostream>
using namespace std;
int main(){
	int a[10],b[10];
	for(int i = 0;i<10;i++){
		a[i]=i*2-1;
		b[10-i-1]=a[i];
	}
	for(int i = 0;i < 10;i++){
		cout<<"a["<<i<<"] = "<<a[i]<<" ";
		cout<<"b["<<I<<"] = "<<b[i]<<endl;
	}
	return 0;
}



数组的储存与初始化

一维数据的存储

数组名字是数组的首元素的内存地址

数组名是一个常量,不能被赋值

一维数组初始化

在这里插入图片描述

二维数据的存储

按行存放

a00 a01 a02 a03 a10…

二维数据的初始化

在这里插入图片描述
在这里插入图片描述

例:求Fibonacci数列的前20项,将结果存放于数组中

#include <iostream>
using namespace std;
int main(){
	int f[20] = {1,1}; //初始化第0、1个数
	for (int 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;
}



一维数组应用举例

统计用户答题的结果,给出每一组答案的正确率

在这里插入图片描述

#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)){
		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;
}



数组作为函数的参数

数组元素作实参,与单个变量一样。

数组名作参数,形、实参数都应该是数组名,类型要一样,传送的是数组首地址。对形参数组的改变会直接影响到实参数组。

例6-2 使用数组名作为函数参数

主函数中初始化一个二维数组,表示一个矩阵,矩阵,并将每个元素输出,然后调用子函数,分别计算每一行的元素之和,将和直接存放在每行的第一个元素中,返回主函数之后输出各行元素的和。

//6_2.cpp
#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;
}



对象数组

对象:类名 数组名[元素个数]

访问: 数组名[下标].成员名

对象数组初始化:Point a[2] = {Point(1,2),Point(3,4)};

例6-3 对象数组应用举例

//6_3.cpp
#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;
}



基于范围的for循环

在这里插入图片描述



指针的概念、定义与指针运算

内存空间的访问方式:通过变量名访问/通过地址访问

在这里插入图片描述

不仅要用*说明它是指针,还要说明它指向的是什么类型的变量

指针运算:寻指的过程

在这里插入图片描述

与地址相关的运算——”*“和”&”

指针运算符:*

地址运算符:&

互为逆运算



指针的初始化和赋值

指针变量的初始化

语法形式

储存类型 数据类型 *指针名 = 初始地址;

例: int *pa = &a;

注意事项

用变量地址作为初值时,该变量必须在指针初始化之前已声明过,且变量类型应与指针类型一致。

可以用一个已有合法值的指针去初始化另一个指针变量。

指针变量的赋值运算

语法形式 指针名=地址

注意:“地址”中存放的数据类型与指针类型必须相符,向指针变量赋的值必须是地址常量或变量,不能是普通整数。

例如:通过

地址运算“&”求得

已定义的变量和对象的起始地址、

动态内存分配成功时返回的地址。


整数0可以赋给指针,表示空指针

允许定义或声明指向void类型的指针。该指针可以被赋予任何类型对象的地址

指针空值nullptr

C++11使用nullptr关键字,是表达更准确,类型安全的空指针

例6-5 指针的定义、赋值与使用

//6_5.cpp
#include <iostream>
using namespace std;
int main(){
	int i;        //定义int类型数i
	int *ptr = &i;//取i的地址赋给ptr
	i = 10;       //int型数赋初值
	cout<<"i="<<i<<endl;//输出int型数的值
	cout<<"*ptr = "<<*ptr<<endl;//输出int型指针所指地址的内容
	return 0;
}

例6-6 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;
}

指向常量的指针

const指针

不能通过指向常量的指针改变所指对象的值,但指针本身可以改变,可以指向另外的对象。

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个数据的起始位置。

指针类型的关系运算

  • 指向相同类型数据的指针之间可以进行各种关系运算
  • 指向不同数据类型的指针,以及指针与一般整数变量之间的关系运算是无意义的。
  • 例外:指针可以和零之间进行等于或不等于的关系运算,例如p==0或p!=0

在这里插入图片描述

综合实例单独写



用指针访问数组元素

定义指向数组元素的指针

定义与赋值

int a[10], *pa;

pa=&a[0];或pa=a;

等效形式

*pa就是a[0]

*(pa+1)就是a[1]

a[i],

*(pa+i),

*(a+i),

pa[i]都是等效的



指针数组

数组的元素是指针类型

Point *pa[2];
//由pa[0]\pa[1]两个指针组成

在这里插入图片描述



以指针作为函数参数

为什么需要用指针做参数?

需要数据双向传递时(引用也可以达到此效果)

需要传递一组数据,只传首地址运行效率比较高

例6-10

读入三个浮点数

将整数部分和小数部分分别输出。

//6_10.cpp
#include <iostream>
using namespace std;

//将实数x分成整数部分和小数部分,形参intpart、fracpart是指针
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 << " Fraction Part = " << f << endl;
	}
	return 0;
}

如果实参传送变量的地址给形参指针去接收,就相当于将主调函数的访问授权给子函数。子函数中对指针所指向的对象的操作,实际上就是对主调函数中实参的操作。

既希望传递地址,又不希望主调函数中的数据值被修改,这个时候定义常指针作形参,通过指针只能读取而不能修改

例:指向常量的指针做形参

#include <isotream>
using namespace std;
const int N = 6;
void print(const int *p,int n);
int main(){
	int array[N];
	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;
}



指针类型的函数

若函数的返回值是指针,该函数就是指针类型的函数。

指针函数的定义形式:

存储类型 数据类型 *函数名()

{//函数体语句

}

不要将非静态局部地址(离开函数就失效了)用作函数的返回值。

错误的例子

int main(){
	int* functino();
	int* ptr = function();//离开function,laocal被释放,原来的地址就是无效地址了
	*ptr = 5;//危险的访问!现在拿着无效地址写信息
}
int* function(){
	int local=0;//非静态局部变量作用域和寿命都仅限于本函数体内
	return &local;
}//函数运行结束时,变量local被释放

正确的例子:主函数中定义数组

#include <iostream>
using namespace std;
int main(){
	int array[10];//主函数中定义的数组
	int* search(int* a,int num);
	for(int i=0li<10;i++)
		cin>>array[i];
	int* zeroptr = rearch(array,10);//将主函数中数组的首地址传给子函数
	return 0;
}

int* search(itn* a,int num){ //指针a指向主函数中定义的数组
	for(int i=0;i<num;i++)
		if(a[i]==0)
			return &a[i];//返回的地址指向的元素是在主函数中定义的
}//函数运行结束,a[i]的地址仍有效

正确的例子2

在子函数中通过动态内存分配new操作取得的内存地址返回给主函数是合法有效的,但是内存分配和释放不在同一级别,要注意不能忘记释放,避免内存泄漏。

#include <iostream>
using namespace std;
int main(){
	int* newintvar();
	int* intptr = newintvar();
	*intptr=5;//访问的是合法有效的地址
	delete intptr;//如果忘记在这里释放,会造成内存泄漏
	return 0;
}
int* newintvar(){
	int* p=new int9);
	return p;//返回的地址指向的是动态分配的空间
}//函数运行结束时,p中的地址仍有效



指向函数的指针

容纳的是函数代码的起始地址

存储类型 数据类型 (*函数指针名)();

在这里插入图片描述

在这里插入图片描述

#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<<"Max of"<<a<<"and"<<b<<"is"<<res<<endl;
	res = compute(a,b, &sum);
	cout<<"Max of"<<a<<"and"<<b<<"is"<<res<<endl;
}



对象指针

对象指针定义形式

类名 *对象指针名;

Point a(5,10);
	Point *ptr;
	ptr=&a;

通过指针访问对象成员

对象指针名->成员名

例:ptr-getx()相当于(*ptr).getx();

例6-12 使用指针来访问Point类的成员

//6_12.cpp
#include <iostream>
using namespace std;

class Point {	//类的定义
public:	//外部接口
	Point(int x = 0, int y = 0) : x(x), y(y) { }	//构造函数
	int getX() const { return x; }	//返回x
	int getY() const { return y; }	//返回y
private:	//私有数据
	int x, y;
};

int main() {	//主函数
	Point a(4, 5);	//定义并初始化对象a
	Point *p1 = &a;	//定义对象指针,用a的地址将其初始化
	cout << p1->getX() << endl;	//利用指针访问对象成员
	cout << a.getX() << endl; 	//利用对象名访问对象成员
	return 0;
}

this指针

隐含于类的每一个非静态成员函数中

指出成员函数所操作的对象

在这里插入图片描述

曾经出现过的错误例子

class Fred;//前向引用声明
class Barney{
	Fred *x; //错误:类Fred的声明尚不完善.修正变成指向Fred的指针
};
class Fred{
	Barney y;
};



动态分配与释放内存

不知道数组开多大合适

要程序运行起来才知道需要多大数组

动态申请内存操作符new

new 类型名T(初始化参数列表)

释放内存操作符delete

delete 指针p

例6-16 动态创建对象举例

#include <isdtream>
using namespace std;
class PointP{
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 grtY() 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;
}



申请和释放动态数组

分配:new 类型名T [数组长度]

数组长度可以说任何整数类型表达式

释放:delete[] 数组名p

释放指针p所指向的数组,必须要有方括号才释放整个数组

p必须是用new分配得到的数组首地址

例6-17 动态创建对象数组举例

#include <iostream>
using namespace std;
class Point{//类的声明同6-16,略};
int main(){
	Point*ptr = new Point[2];//创建对象数组
	ptr[0].move(5,10);//通过指针访问数组元素的成员
	ptr[1].move(15,20);//通过指针访问数组元素的成员
	cout<<"Deleting..."<<endl;
	delete[] ptr;//删除整个对象数组
	return 0;
}

动态创建多维数组

new 类型名T[第一位长度][第二维长度]…;

char (*fp)[3];
fp = new char[2][3]

在这里插入图片描述

在这里插入图片描述

将动态数组封装成类

更加简洁,便于管理

可以在访问数组元素前检查下标是否越界

#include <iostream>
#include <cassert>
using namespace std;
class Point{//声明同例6-16...};
class ArrayOfPoints{
public:
	ArrayOfPoints(int size):size(size){
		points = new Points[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;
}

在这里插入图片描述



智能指针

在这里插入图片描述



vector对象

在这里插入图片描述

vector对象的定义

vector<元素类型> 数组对象名(数组长度);
vector<int> arr(5)
//建立大小为5的int数组

在这里插入图片描述

int main(){
	unsigend 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;
	
}



深层复制与浅层复制



移动构造



C风格字符串



string类



版权声明:本文为weixin_45965693原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。