# 数组
数组是具有一定顺序关系的若干相同类型变量的集合体,组成数组的变量。
# 范围 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; |
| } |
| |
| } |