# 数组
数组是具有一定顺序关系的若干相同类型变量的集合体,组成数组的变量。
# 范围 for 循环
|  | #include <iostream> | 
|  | using namespace std; | 
|  | int main(void) | 
|  | { | 
|  | 	int a[10], b[10]; | 
|  | 	for (int i = 0; i < 10; i++) | 
|  | 	{ | 
|  | 		a[i] = i * 2 - 1; | 
|  | 		b[10 - i - 1] = a[i]; | 
|  | 	} | 
|  | 	 | 
|  | 	 | 
|  | 	for (const auto& e : a)   | 
|  | 		cout << e << " "; | 
|  | 	cout << endl; | 
|  | 	for (int i = 0; i < 10; i++)  | 
|  | 	{ | 
|  | 		cout << b[i] << " "; | 
|  | 	} | 
|  | 	cout << endl; | 
|  |  | 
|  | 	return 0; | 
|  | } | 
# 数组初始化
- 如果不作任何初始化,局部作用域的非静态数组中会存在垃圾数据,static 数组中的数组默认初始化为 0
- 如果只对部分元素初始化,剩下的未显式初始化的元素,将自动被初始化为零
# 对象数组
- 定义对象数组
- 访问对象数组元素
- 对象数组初始化- 数组中每一个元素对象被创建时,系统都会调用类构造函数初始化该对象。
- 通过初始化列表赋值
- Point a[2]={Point(1,2),Point(3,4)}
- 如果没有为数组元素指定显式初始值,数组元素便使用默认值初始化 (调用默认构造函数)
 
- 对象数组的析构- 当数组中每一个对象被删除时,系统都要调用一次析构函数
 
|  | #include <iostream> | 
|  | #include "Pont.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; | 
|  | } | 
|  | int main(void) | 
|  | { | 
|  | 	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 类型的指针,该指针可以被赋予任何类型对象的地址
- 以往用 0 或者 NULL 去表达空指针的问题- C/C++ 的 NULL 宏是个有很多潜在 BUG 的宏。因为有的库把其定义为整数 0,有的定义成 (void*) 0.
- C++11 使用 nullptr 关键字,是表达更准确,类型安全的空指针
 
# 指向常量的指针
不能通过指向常量的指针改变所指对象的值,但指针本身可以改变,可以指向另外的对象
|  | int a; | 
|  | const int *p1 = &a;  | 
|  | int b; | 
|  | p1 = &b;  | 
|  | *p1 = 1; | 
|  | b=6; | 
# 指针类型的常量
若声明指针常量,则指针本身的值不能被改变,指针本身是常量
|  | int a; | 
|  | int * const p2 = &a; | 
|  | *p2 = 3;   | 
|  | int b; | 
|  | p2 = &b;   | 
# 指针数组
利用数组与二维数组对比比
|  | #include <iostream> | 
|  | using namespace std; | 
|  |  | 
|  | int main(void) | 
|  | { | 
|  | 	int line1[] = { 1,0,0 }; | 
|  | 	int line2[] = { 0,1,0 }; | 
|  | 	int line3[] = { 0,0,1 }; | 
|  | 	 | 
|  | 	int* Pline[3] = { line1,line2,line3 }; | 
|  | 	cout << "Matrix test:" << endl; | 
|  | 	 | 
|  | 	for (int i = 0; i < 3; i++) | 
|  | 	{ | 
|  | 		for (int j = 0; j < 3; j++) | 
|  | 		{ | 
|  | 			cout << Pline[i][j] << " "; | 
|  | 		} | 
|  | 		cout << endl; | 
|  | 	} | 
|  |  | 
|  | 	return 0; | 
|  | } | 
|  |  | 
|  | Matrix test: | 
|  | 1 0 0 | 
|  | 0 1 0 | 
|  | 0 0 1 | 
|  | */ | 
# 以指针作为函数参数
- 需要数据双向传递时 (引用也可以达到响应效果)用指针作为函数的参数,可以使被调函数通过形参指针存取主调函数中实参指针指向的数组,实现数据的双向 传递 
 
- 小传递一组数据,只传首地址运行效率比较高实参是数组名时形参可以是指针 
 
|  |  | 
|  | project: 从浮点数中取整数和浮点数 | 
|  | 注:浮点数在机器内部近似存储 | 
|  | */ | 
|  | #include <iostream> | 
|  | using namespace std; | 
|  | void splitFloat(float x, int* intpart, float* fracPart) | 
|  | { | 
|  | 	*intpart = static_cast<int>(x);  | 
|  | 	*fracPart = x - *intpart;  | 
|  | } | 
|  | int main(void) | 
|  | { | 
|  | 	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; | 
|  | } | 
|  |  | 
|  | Enter 3 float point numbers: | 
|  | 3.14 | 
|  | integer Part=3 Fraction Part = 0.14 | 
|  | 9.7 | 
|  | integer Part=9 Fraction Part = 0.7 | 
|  | 5 | 
|  | integer Part=5 Fraction Part = 0 | 
|  | */ | 
# 指针常量
|  |  | 
|  | Project:有关常量的几个问题 | 
|  | */ | 
|  | #include <iostream> | 
|  | using namespace std; | 
|  | const int N = 6;    | 
|  | int const M = 9; | 
|  | const int* p;  | 
|  | int const *q = 0;  | 
|  | const int* const A = &N;  | 
|  | int main(void) | 
|  | { | 
|  | 	 | 
|  | 	 | 
|  | 	int a = 7; | 
|  | 	p = &a;   | 
|  | 	 | 
|  | 	p = &N; | 
|  | 	 | 
|  | 	q = &a; | 
|  | 	cout << *q << endl; | 
|  | 	return 0; | 
|  | } | 
|  |  | 
|  | project: 使用 const 进行权限管理 | 
|  | */ | 
|  | #include <iostream> | 
|  | using namespace std; | 
|  | const int N = 6; | 
|  | void print(const int* p, int n)  | 
|  | { | 
|  | 	cout << "{" << *p; | 
|  | 	for (int i = 1; i < n; i++) | 
|  | 	{ | 
|  | 		cout << "," << *(p + i); | 
|  | 	} | 
|  | 	cout << "}" << endl; | 
|  | } | 
|  | int main(void) | 
|  | { | 
|  | 	int array[N]; | 
|  | 	for (int i = 0; i < N; i++) | 
|  | 	{ | 
|  | 		cin >> array[i]; | 
|  | 	} | 
|  | 	print(array, N); | 
|  | 	return 0; | 
|  | } | 
|  |  | 
|  | {1,2,3,4,5,6} | 
|  | */ | 
# 指针类型的函数
注意:
- 不要将非静态局部地址用作函数的返回值- 在 子函数中定义局部变量后将其地址返回给主函数,就是非法地址
 
- 返回的指针要确保在主调函数中是有效,合法的地址- 主函数中定义的数组,在子函数中对该数组元素进行某种操作后,返回其中一个元素的地址
- 在子函数中通过动态内存分配 new 操作取得的内存地址返回给主函数是合法有效的,但是内存分配和释放不在同一级别,要注意不能忘记释放,避免内存泄露。
 
|  | #include <iostream> | 
|  | int* newintvar() | 
|  | { | 
|  | 	int* p = new int();   | 
|  | 	return p;  | 
|  | 	 | 
|  | } | 
|  | int main(void) | 
|  | { | 
|  | 	int* newintvar(); | 
|  | 	int* intptr = newintvar(); | 
|  | 	*intptr = 5;  | 
|  | 	delete intptr;   | 
|  | 	return 0; | 
|  | } | 
内存泄露 :程序中已分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
# 函数指针的定义:函数指针指向的是程序代码存储区。
# 函数指针的典型用途
- 通过函数指针调用的函数- 将函数的指针作为参数传递给一个函数,使得在处理相似事件的时候可以灵活的使用不同的方法
 
- 调用者不关心谁是被调用者
|  | #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(void) | 
|  | { | 
|  | 	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; | 
|  | } | 
# 对象指针
|  | Point a(5,10); | 
|  | Point *ptr; | 
|  | ptr = &a; | 
|  | ptr->getx() 相当于(*ptr).getx(); | 
|  | #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(void) | 
|  | { | 
|  | 	Point a(4, 5); | 
|  | 	Point* p1 = &a;   | 
|  | 	cout << p1->getX() << endl;  | 
|  | 	cout << a.getY() << endl;	 | 
|  | 	return 0; | 
|  | } | 
# this 指针
- 隐含于类的每一个非静态成员函数中
- 指出成员函数所操作的对象- 当通过一个对象调用成员函数时,系统先将该对象的地址赋给 this 指针,然后调用成员函数,成员函数对对象的数据成员进行操作时,就隐含使用了 this 指针
- Point 类的 getX 函数中 return x 相当于 return this->x;
 
# 前向引用声明
|  | class Fred;  | 
|  | class Barney | 
|  | { | 
|  | 	Fred *x;   | 
|  | } | 
|  | class Fred | 
|  | { | 
|  | 	Barney y; | 
|  | } | 
# 动态内存分配
- new 类型名 T (初始化参数)
- 功能:- 在程序执行期间,申请用于存放 T 类型对象的存储空间,并依次初始化参数进行初始化。
- 基本类型初始化:如果有初始化参数,依初始化参数进行初始化,如果没有括号和初始化参数,不进行初始化,新分配的内存中内容不确定,如果有括号但初始化参数为空,初始化为 0
- 对象类型:如果有初始化参数,以初始化参数中的值为参数调用构造函数进行初始化,如果没有括号和初始化参数或者有括号但初始化参数为空,用默认构造函数初始化
 
- 结果值:成功:T 类型的指针,指向新分配的内存,失败:抛出异常
# 动态申请动态数组
- new 类型名 T [表达式][常量表达式]......()
- 功能:- 在程序执行期间,申请用于存档 T 类型对象数组的内存空间,可以有 "()" 但初始化列表必须为空
- 如果有 "()", 对每个元素的初始化与执行 "new T ()" 所做进行初始化的方式相同
- 如果没有 "()", 对每个元素的初始化与执行 "new T" 所做进行初始化的方式相同
 
- 结果值- 如果内存申请成功,返回一个指向新分配内存首地址的指针
- 如果失败,抛出异常
 
|  | double* array = new double[n](); | 
|  | char(*fp)[3];   | 
|  | fp = new char[n][3]; | 
# 释放内存操作符 delete
- delete 指针 p
- 功能:释放指针 p 所指向的内存,p 必须是 new 操作的返回值
- delete [] 指针 p
- 功能:释放指针 p 所指向的数组,p 必须是用 new 分配得到的数组首地址
|  | #include <iostream> | 
|  | using namespace std; | 
|  | class Point | 
|  | { | 
|  | public: | 
|  | 	Point(int x, int y) | 
|  | 	{ | 
|  | 		cout << "Destructor called." << endl; | 
|  | 	}; | 
|  | 	Point() :x(x), y(y) | 
|  | 	{ | 
|  | 		cout << "Default Destructor 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(void) | 
|  | { | 
|  | 	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 Destructor called. | 
|  | Destructor called | 
|  | Step two | 
|  | Destructor called. | 
|  | Destructor called | 
|  | */ | 
|  | #include <iostream> | 
|  | using namespace std; | 
|  | class Point | 
|  | { | 
|  | public: | 
|  | 	Point(int x, int y) | 
|  | 	{ | 
|  | 		cout << "Destructor called." << endl; | 
|  | 	}; | 
|  | 	Point() :x(x), y(y) | 
|  | 	{ | 
|  | 		cout << "Default Destructor 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(void) | 
|  | { | 
|  | 	Point* ptr = new Point[2]; | 
|  | 	ptr[0].move(5, 10);  | 
|  | 	ptr[1].move(15, 20);  | 
|  | 	cout << "Deleting..." << endl; | 
|  | 	delete[] ptr;  | 
|  | 	return 0; | 
|  | } | 
|  |  | 
|  | Default Destructor called. | 
|  | Default Destructor called. | 
|  | Deleting... | 
|  | Destructor called | 
|  | Destructor called | 
|  | */ | 
# 创建动态数组
|  | #include <iostream> | 
|  | using namespace std; | 
|  | int main(void) | 
|  | { | 
|  | 	int n; | 
|  | 	cin >> n; | 
|  | 	 | 
|  | 	int(*cp)[9][8] = new int[n][9][8];  | 
|  | 	for (int i = 0; i < 7; i++) | 
|  | 	{ | 
|  | 		for (int j= 0; j < 9; j++) | 
|  | 		{ | 
|  | 			for (int k = 0; k < 8; k++) | 
|  | 			{ | 
|  | 				*(*(*(cp + i) + j) + k) = (i * 100 + j * 10 + k); | 
|  | 			} | 
|  | 		} | 
|  | 	} | 
|  | 	for (int i = 0; i < 7; i++) | 
|  | 	{ | 
|  | 		for (int j = 0; j < 9; j++) | 
|  | 		{ | 
|  | 			for (int k = 0; k < 8; k++) | 
|  | 			{ | 
|  | 				cout << cp[i][j][k] << " "; | 
|  | 			} | 
|  | 			cout << endl; | 
|  | 		} | 
|  | 		cout << endl; | 
|  | 	} | 
|  |  | 
|  | 	delete[] cp;  | 
|  |  | 
|  | } | 
# 将动态数组封装成类
- 更加简介,便于管理- 建立和删除数组的过程比较繁琐
- 封装成类后更加简洁,便于管理
 
- 可以在访问数组元素前检查下标是否越界- 用 assert 来检查,assert 只在调试时生效
 
|  | #include <iostream> | 
|  | #include <cassert> | 
|  | using namespace std; | 
|  | class Point | 
|  | { | 
|  | public: | 
|  | 	Point(int x, int y) | 
|  | 	{ | 
|  | 		cout << "Destructor called." << endl; | 
|  | 	}; | 
|  | 	Point() :x(x), y(y) | 
|  | 	{ | 
|  | 		cout << "Default Destructor 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; | 
|  | }; | 
|  | 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(void) | 
|  | { | 
|  | 	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 count of points:3 | 
|  | Default Destructor called. | 
|  | Default Destructor called. | 
|  | Default Destructor called. | 
|  | Deleting ... | 
|  | Destructor called | 
|  | Destructor called | 
|  | Destructor called | 
|  | */ | 
# 为什么 element 函数返回对象的引用
- 返回 "引用" 可以用来操作封装数组对象内部的数据元素,如果返回 "值" 则只是返回了一个副本,通过副本是无法操作原来数组中的元素的。
# vector 容器
# vector 容器定义:
- vector <元素类型> 数组对象名 (数组长度)
- vector<int>arr (5) // 建立大小为 5 的 int 数组
|  | #include <iostream> | 
|  | #include <vector> | 
|  | using namespace std; | 
|  |  | 
|  | double average(const vector<double>& arr) | 
|  | { | 
|  | 	double sum = 0; | 
|  | 	for (unsigned i = 0; i < arr.size(); i++) | 
|  | 	{ | 
|  | 		sum += arr[i]; | 
|  | 	} | 
|  | 	return sum / arr.size(); | 
|  | } | 
|  | int main(void) | 
|  | { | 
|  | 	unsigned n; | 
|  | 	cout << "n = "; | 
|  | 	cin >> n; | 
|  | 	vector<double>arr(n);  | 
|  | 	cout << "Please input " << n << " real number:" << endl; | 
|  | 	for (unsigned i = 0; i < n; i++) | 
|  | 	{ | 
|  | 		cin >> arr[i]; | 
|  | 	} | 
|  | 	cout << "Average = " << average(arr) << endl; | 
|  | 	return 0; | 
|  | } | 
# 基于 for 循环配合 auto 举例
|  |  | 
|  | * 基于范围的 for 循环配合 auto 举例 | 
|  | */ | 
|  | #include <vector> | 
|  | #include <iostream> | 
|  | using namespace std; | 
|  | int main(void) | 
|  | { | 
|  | 	vector<int>v = { 1,2,3 }; | 
|  | 	 | 
|  | 	for (auto i = v.begin(); i != v.end(); ++i)  | 
|  | 	{ | 
|  | 		cout << *i << endl; | 
|  | 	} | 
|  | 	for (auto e : v) | 
|  | 	{ | 
|  | 		cout << e << endl; | 
|  | 	} | 
|  |  | 
|  | } |