第六章 数组,指针与字符串(二)
指针与数组
指针访问数组元素
pa=&a[0];
或 pa=a;
*pa
就是a[0]
,*(pa+1)
就是a[1]
,… ,*(pa+i)
就是a[i]
.
a[i]
, *(pa+i)
, *(a+i)
, pa[i]
都是等效的。
不能写a++,因为a是数组首地址、是常量。
指针数组:数组元素是指针类型
利用指针数组存放矩阵:
#include
using namespace std;
int main() {
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
指针与函数
传指针也是引用传递的一种;
为什么要用?
需要数据双向传递时(引用也可以达到此效果)
需要传递一组数据,只传首地址运行效率比较高;
例:
#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 << " 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[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;
}
指针类型的函数
返回值是指针;
存储类型 数据类型 *函数名()
{ //函数体语句
}
注意事项:
不要将非静态局部地址用作函数的返回值。
错误的例子:
在子函数中定义局部变量后将其地址返回给主函数,就是非法地址。
正确的例子:
主函数中定义的数组,在子函数中对该数组元素进行某种操作后,
返回其中一个元素的地址,这就是合法有效的地址。
在子函数中通过动态内存分配new操作取得的内存地址返回给主函数
是合法有效的,但是内存分配和释放不在同一级别,要注意不能忘记
释放,避免内存泄漏。
指向函数的指针
为什么需要?
通过函数指针调用的函数
例如将函数的指针作为参数传递给一个函数,使得在处理相似事件的时候可以灵活的使用不同的方法。
调用者不关心谁是被调用者
需知道存在一个具有特定原型和限制条件的被调用函数。
函数指针举例
对象指针
类名 *对象指针名;
很一样;
对象指针
通过指针访问对象成员
对象指针名->成员名
例:ptr->getx()相当于(*ptr).getx();
this指针
指向自己的指针
动态内存分配
动态分配与释放内存
动态申请内存操作符new
释放内存操作符delete
#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.
申请和释放动态数组(一)
语法:
new 类型名T[数组长度]
delete[] 数组名p
在什么地方new 就在什么地方delete
动态创建多维数组:
#include <iostream>
using namespace std;
int main()
{
int(*cp)[9][8] = new int[7][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;
return 0;
}
申请和释放动态数组(二)
将动态数组封装成类
更加简洁,便于管理
可以在访问数组元素前检查下标是否越界
智能指针
有三种:
unique_ptr:
不允许多个指针共享资源,可以用标准库中的move函数转换指针;
shared_ptr:
多个指针共享资源
weak_ptr:
可复制shared_ptr,但其构造或者释放对资源不产生影响;
Vector对象
为什么需要
封装任何类型的动态数组, 自动创建和删除;
数组下标越界检查;
例:
vector<int> arr(5)
建立大小为5的int数组
有:
vector对象名.size()
深层复制和浅层复制
深浅复制
浅层复制
实现对象间数据元素的一一对应复制;
深层复制
当被复制的对象数据成员是指针类型时,不是复制该指针成员本身,而是将指针所指对象进行复制;
区别
浅层复制的话, 可能导致两个对象指向同一地址, 导致析构时释放内存单元出错;
深层复制会同时开辟相同的内存单元;
移动构造
C++11 标准中提供的一种新的构造方法;
C++11之前,如果要将源对象的状态转移到目标对象只能通过复制;在某些情况下我们没有必要复制对象—只需要移动它们;
语义
源对象资源的控制权全部交给目标对象;
移动构造函数
例如: 函数返回含有指针成员的对象
深层复制构造函数
返回时构造临时对象,动态分配将临时对象返回到主调函数,然后删除临时对象。
移动构造函数
将要返回的局部对象转移到主调函数,省去了构造和删除临时对象的过程。
class_name ( class_name && )
&&是右值引用
C风格字符串
其中一种存放字符串的方法;
字符串常量
const char *STRING1 = “program”
各字符连续, 顺序存放, 每个字符占一个字节, 以'\0'结尾, 相当于一个隐含创建的字符常量数组;
"program"出现在表达式中, 表示这一char数组的首地址;
首地址可以赋给char常量指针;
用字符数组存储字符串(C风格字符串)
char str[8] = { ‘p’, ‘r’, ‘o’, ‘g’, ‘r’, ‘a’, ‘m’, ‘\0’ };
char str[8] = “program”;
char str[] = “program”;
用字符数组表示字符串的缺点
执行连接、拷贝、比较等操作,都需要显式调用库函数,很麻烦
当字符串长度很不确定时,需要用new动态创建字符数组,最后要用delete释放,很繁琐
字符串实际长度大于为它分配的空间时,会产生数组下标越界的错误
能看懂就行
string类
使用字符串类string表示字符串
string实际上是对字符数组操作的封装
string类常用的方式
和其他语言一样;