1 指向一维数组的指针
参考此篇文章:
C语言之指向一维数组的指针
int array[5] = {1, 2, 3, 4, 5}; // 定义数组
int (*ptr)[5] = &array; // 定义指向数组的指针
指针都是指向一种类型。所谓类型, 不过是对内存中数据的解释的约定。
对于int array[5] ,可以将 具有5个int元素的数组认为是一种类型,这种类型在内存上的特点是连续存储5个整形数据。
例如 int (*ptr)[5] ,ptr这个指针就是指向这种类型的指针,ptr是一个指向数组的指针
int (*ptr)[5]的形式是用来定义二维数组的指针的,用此方法将ptr指向一维数组的原因是:
一维数组array[5]可以看成是只有一个元素的二维数组,该二维数组的元素是int[5]类型的
array[5] = {{1,2,3,4,5} }
当ptr是指向二维数组的指针时,*(ptr+i)表示第i行,ptr[i]表示第i行
*ptr == ptr[0] (这个二维数组的第一行,即进入这个一维数组)
*ptr [1] == prt[0][1] == 一维数组的第2个元素 == 2
无法使用*(ptr+1) 或 ptr[1],因为这个二维数组只有一个元素
指向一维数组的指针的定义方式要使用&
int b[3] = { 6,5,4 };
int(*sb)[3] = &b;
cout << sb << endl; //这六个输出值完全相等
cout << *sb << endl;
cout << sb[0] << endl;
cout << b << endl;
cout << &b << endl;
cout << &b[0] << endl;
2 左值与右值,返回封装数组元素的引用
当一个对象被用作右值时,用的是对象的值(内容);当对象被用作左值时,用的是对象的身份(在内存中的位置)
右值只能出现在等号右边。
- a = 5 / a = b + c; 赋值运算符的左侧运算对象必须是左值,得到的结果也是一个左值
- int* p = &a; &取址运算符作用于一个左值运算对象(a),返回一个指向对象的指针,这个指针是一个右值(p)
- int a[5],这里a是首元素的地址,是右值
- *,[] 运算符求值结果是左值,*p是左值(可以直接修改指向对象的值),a[2]是左值,可以直接修改数组的第3个元素
- ++、- - 作用于左值,但只有前置版本的结果是左值(++a,–a)
- decltype(*p)的结果是int &,可以理解是因为 * p是左值
- decltyoe((变量))的结果永远是引用,注意是双层括号
int a[5] = {1,2,3,4,5}
a[0] = 100; 这个赋值操作中,a[0]是左值,100是右值
#include <iostream>
#include <cassert>
#include <typeinfo>
using namespace std;
class Point {
public:
Point() : x(0), y(0) {
cout << "调用Point类默认构造函数" << endl;
}
Point(int x, int y) : x(x), y(y) {
cout << "调用Point类构造函数" << endl;
}
~Point() { cout << "调用Point类析构函数" << 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) {
cout << "调用ArrayOfPoints类构造函数" << endl;
points = new Point [size]; //动态创建含size个元素的Point类型数组
}
~ArrayOfPoints() {
cout << "调用ArrayOfPoints类析构函数并delete动态分配内存" << endl;
delete[] points;
}
Point& element(int index) { //一定要返回Point&,即左值
assert(index >= 0 && index < size);
return points[index];
}
private:
Point* points; //指向动态数组首地址
int size; //数组大小
};
int main() {
int count;
cout << "输入要创建Point类对象的个数,存储在points指针指向的一维数组中: ";
cin >> count;
ArrayOfPoints aaa(count); //创建数组对象
Point ppp(100, 100); //调用构造函数,非默认构造函数
aaa.element(0).move(5, 0); //访问数组元素的成员
aaa.element(1).move(15, 20); //访问数组元素的成员
return 0;
}
结果:
输入要创建Point类对象的个数,存储在points指针指向的一维数组中: 2
调用ArrayOfPoints类构造函数
调用Point类默认构造函数
调用Point类默认构造函数
调用Point类构造函数 /符合后构造,先析构
调用Point类析构函数
调用ArrayOfPoints类析构函数并delete动态分配内存
调用Point类析构函数 /Point对象是动态内存分配的,是封装的,最后才析构
调用Point类析构函数
- 上述代码ArrayOfPoints类的构造函数中动态分配内存创建了一个有2个元素的Point类型数组,用points指针指向其首地址。
- 在自定义类中(ArrayOfPoints),不能直接用下标运算,要取得这个封装数组的元素,要使用element函数,通过返回值获得。(因为points是私有成员)
- assert函数验证输入的下标是否越界,如果输入合法,用输入的下标数值去找points[]这个数组的对应元素,注意,因为 Point* points = new Point [2],所以points这个指针可以当做该数组的数组名使用
- 返回值是points[index],返回类型是该元素的引用,可以在后续操作改变封装数组内部的元素(例如调用move函数改变points[0]、points[1]的x,y值),即返回引用类型是返回points[index]的左值。
Point& element(int index)
如果返回值不加引用,返回的是points[index]的副本,即points[index]的右值,后续怎么操作都不影响封装数组内部元素的值。
如果去掉引用符号,即Point element(int index)
aaa.element(0).move(5, 10);
cout << aaa.element(0).getX() << endl; //仍然返回0
move不起作用,因为element返回的是ArrayOfPoints类中封装的Point数组的副本,是右值,即对象以值传递的方式从函数返回,调用默认复制构造函数,对其进行move不会改变封装的数组的Point对象的x,y值。故aaa.element(0).getX()
的x不变,仍然是默认的0。