本文最后更新于:2023年4月7日 下午
C++进阶笔记
数组
数组是具有一定顺序关系的若干对象的集合。
array[N] 数组的下标从0开始。
声明
数据类型 标识符 [常量表达式1] [常量表达式2]
数据类型:
整型、浮点型、结构体 、类 等
使用
数组名 [常量表达式1] [常量表达式2]
范围for循环
对给定序列中每一个元素按序列中元素的顺序逐一访问,配合auto自动判断元素类型。用来实现数组元素的快速遍历。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 for (const auto &e : a){ cout << e << endl; }int arry[][3 ] = { {1 ,2 ,3 },{4 ,5 ,6 } };for (int (&row)[3 ] : arry) { for (int &e : row) { cout << e << endl; } }for (auto &row : arry) { for (auto &e : row) { cout << e << endl; } }
下标迭代for循环
1 2 3 for (int i = 0 ; i < n; i++){ cout << b[i] << endl; }
关于二维数组
C++中二维数组被当作一维的数组,int m[2][3]可以看作大小是2,每个元素都是一个大小为3,类型为int类型的数组。
数组定义
1 2 3 4 5 6 7 8 9 10 int a[3 ]= {1 ,1 ,1 };int a[] = {1 ,1 ,1 };int a[2 ][3 ] = {1 ,2 ,3 ,4 ,5 ,6 };int a[2 ][3 ] = {{1 ,2 ,3 },{4 ,5 ,6 }};int a[][3 ] = {1 ,2 ,3 ,4 ,5 ,6 };int a[][3 ] = {{1 ,2 ,3 },{4 ,5 ,6 }}; Location loc[2 ] ={Location (3 ,4 ),Location (1 ,2 )}
二维数组与地址
数组作为函数参数
传递的是地址 。数组作为参数时,函数里一般不指定第一维的大小。
数组的名称代表数组首元素的地址
数组的名称取地址(&数组名称)代表整个数组开始的地址 ,虽然和数组首元素的地址
实例
注意.h文件的 def
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #ifndef _POINT_H #define _POINT_H class Point {private : int x, y;public : Point (); ~Point (); Point (int x, int y); int getX () { return x; } int getY () { return y; } void movePoints (int newX, int newY) ; };#endif
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> #include "Point.h" using namespace std; Point::Point ():x (0 ),y (0 ) { cout << "Deafult constructor called." << endl; } Point::~Point () { cout << "Destructor called" << endl; } Point::Point (int x,int y):x (x),y (y){ cout << "Constructor called" << endl; }void Point::movePoints (int newX, int newY) { x = newX; y = newY; cout << "Move to new Points(" << newX << "," << newY << ")" << endl; }
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <iostream> #include "Point.h" using namespace std;int main () { Point a[2 ]; for (int i = 0 ; i < 2 ; i++){ a[i].movePoints (i + 10 , i + 11 ); } return 0 ; }
实例2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #ifndef _Point_H #define _Point_H class Point {public : Point (float x = 0 , float y = 0 ) :x (x), y (y) {} float getX () const { return x; } float getY () const { return y; } friend float lineFit (const Point points[], int nPoint) ;private : float x, y; };#endif
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <iostream> #include "Point.h" using namespace std;float lineFit (const Point points[], int nPoint) { float avgX = 0 , avgY = 0 ; float lxx = 0 , lyy = 0 , lxy = 0 ; for (int i = 0 ; i < nPoint; i++){ avgX += points[i].x/nPoint; avgY += points[i].y/nPoint; } for (int i = 0 ; i < nPoint; i++){ lxx += (points[i].x - avgX) * (points[i].x - avgX); lyy += (points[i].y - avgY) * (points[i].y - avgY); lxy += (points[i].x - avgX) * (points[i].y - avgY); } cout << "This lien can be fitted by y=ax+b." << endl; cout << "a=" << lxy / lxx << " " << endl; cout << "b=" << avgY - lxy * avgX / lxx << endl; return static_cast <float >(lxy / sqrt (lxx * lyy)); }int main () { Point p[10 ] = { Point (6 ,10 ),Point (14 ,20 ), Point (26 ,30 ), Point (33 ,40 ),Point (46 ,50 ),Point (54 ,60 ),Point (67 ,70 ),Point (75 ,80 ),Point (84 ,90 ), Point (100 ,100 ) }; float r = lineFit (p, 10 ); cout << "Line coefficient r=" << r << endl; return 0 ; }
指针
指针 也是一种数据类型,具有指针类型的变量称为指针变量 ,指针变量是用来存放内存单元地址 的。
声明
数据类型 *标识符
数据类型 可以是任意类型,代表指针所指向的对象的类型。
举例:
int *ptr
定义了一个指向int类型数据的指针变量,这个指针的名字是ptr,存放int型数据的地址。
与地址相关的运算 * 和 &
* 表示获取指针变量所指向的值,一元操作符。
& 表示获得一个对象的地址,一元操作符。
注意:
*
出现在声明语句中,在被声明的变量名之前时,表面声明的是指针。
int *p 声明指针变量p
*
出现在执行语句中或者声明语句的初值表达式中作为一元运算符时,表示指针所访问的对象的内容。
cout<< *p
打印p所指向的内存单元的值
&
出现在变量声明语句 中位于被声明变量的左边 时,表示声明的是引用 。
int &rf 表明rf是个引用
&
在给变量赋初值 的时候出现在等号右边 或者在执行语句中作为一元运算符出现 时,表示取对象的地址。
int a,b;
int *pa,*pb = &b;
声明指针变量pa、pb,b的地址赋给指针pb.
pa = &a;
a的地址赋给指针pa.
赋值
存储类型 数据类型 *指针名 = 初始地址
指针名 = 地址
数组名其实是一个不能被赋值的指针,即指针常量
int a[10];
int *ptr = a;
空指针
1 2 3 4 int *p; p=0 ;int *p = NULL ;
void类型指针
void类型的指针经过显示转换之后可以访问任何类型的数据。一般只在指向的数据类型不确定时使用。
1 2 3 4 5 void *pv;int i =5 ; pv = &i;int *pint = static_cast <int *>(pv); cout << "*pint=" << *pint << endl;
指向常量的指针与指针类型的常量
1 2 3 4 5 6 7 int a;const int *p1 = &a;int b; p1 = &b; *p1 = 1 ;
1 2 3 4 int a,b;int * const p2 = &a; p2 = &b;
运算
指针可以和整数进行加减运算。
*(p1+n1) 或p1[n1]表示p1
当前所指位置后方的第 n1 个数的内容。
C++指针
begin 与 end 函数
C++ 11
引入了begin和end函数,将数组作为参数。begin返回指向数组首元素 的指针,end返回指向数组尾元素下一位置 的指针。
1 2 3 4 int *pbeg = begin (arr),*pend = end (arr);while (pbeg != pend && *pbeg >= 0 ) ++pbeg;
一维实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <iostream> using namespace std;int main () { int a[3 ] = {1 ,2 ,3 }; for (int i = 0 ; i < 3 ; i++){ cout << a[i] << " " ; } cout << endl; int * p = a; for (int i = 0 ; i < 3 ; i++) { cout << *(p+i) << " " ; } cout << endl; for (int * q = a; q < (a+3 ); q++) { cout << *q << " " ; } cout << endl; }
指针数组与数组指针
数组中的每个元素都是指针变量。指针数组中的每个元素都必须是同一类型的指针。
数据类型 *数组名[下标表达式]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <iostream> using namespace std;int main () { int line1[] = { 1 , 2 , 3 }; int line2[] = { 4 , 5 , 6 }; int line3[] = { 7 , 8 , 9 }; int * pLine[3 ] = { line1,line2,line3 }; for (int i = 0 ; i < 3 ; i++){ for (int j = 0 ; j < 3 ; j++){ cout << *(*(pLine + i) + j) << " " ; } cout << endl; } int arr[3 ][3 ] = { {1 ,2 ,3 },{4 ,5 ,6 },{7 ,8 ,9 } }; for (int i = 0 ; i < 3 ; i++) { for (int j = 0 ; j < 3 ; j++) { cout << *(*(arr + i) + j) << " " ; } cout << endl; } int (*arrPtr)[3 ] = arr; for (int i = 0 ; i < 3 ; i++) { for (int j = 0 ; j < 3 ; j++) { cout << *(*(arrPtr + i) + j) << " " ; } cout << endl; } int * ptrArr[3 ] = { arr[0 ],arr[1 ],arr[2 ]}; for (int i = 0 ; i < 3 ; i++) { for (int j = 0 ; j < 3 ; j++) { cout << *(*(ptrArr + i) + j) << " " ; } cout << endl; } return 0 ; }
指针数组与二维数组
辨析
根据给出的代码,以下是输出结果和解释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 array[0 ][0 ]: 1 sizeof array[0 ][0 ]: 4 &array[0 ][0 ]: 0x7fffa23b2af0 sizeof &array[0 ][0 ]: 8 array[0 ]: 0x7fffa23b2af0 int (*p) = array[0 ]; sizeof array[0 ]: 16 &array[0 ]: 0x7fffa23b2af0 int (*p)[4 ] = &array[0 ]; sizeof &array[0 ]: 8 array: 0x7fffa23b2af0 sizeof array: 32 &array : 0x7fffa23b2af0 sizeof &array : 8
array[0][0]:输出第一行第一列的元素值,即 1。 sizeof
array[0][0]:输出一个 int 类型变量所占据的字节数,即 4。
&array[0][0]:输出第一行第一列的元素的内存地址,即 0x7fffa23b2af0。
sizeof &array[0][0]:输出一个指向 int 类型的指针所占据的字节数,即
8。 array[0]:输出第一行的数组的起始地址,即 0x7fffa23b2af0。 sizeof
array[0]:输出一个包含 4 个 int 类型元素的一维数组所占据的字节数,即
16。 &array[0]:输出第一行的数组的起始地址,即 0x7fffa23b2af0。
sizeof &array[0]:输出一个指向包含 4 个 int
类型元素的一维数组的指针所占据的字节数,即 8。
array:输出整个二维数组的起始地址,即 0x7fffa23b2af0。 sizeof
array:输出一个包含 4 个包含 4 个 int
类型元素的一维数组的二维数组所占据的字节数,即 32。
&array:输出整个二维数组的起始地址,即 0x7fffa23b2af0。 sizeof
&array:输出一个指向包含 4 个包含 4 个 int
类型元素的一维数组的二维数组的指针所占据的字节数,即 8。
解释:本题中的二维数组 array 由 4 个一维数组组成,每个一维数组包含 4
个 int 类型的元素。 因此,array[0][0] 输出第一行第一列的元素值 1,
sizeof array[0][0] 输出一个int 类型变量所占据的字节数 4。
&array[0][0] 输出第一行第一列的元素的内存地址, sizeof(array) =
32:因为数组array共有2行4列,每个元素是int类型,所以占用总共的空间是2 *
4 * sizeof(int) = 32个字节。 &array =
0x7ffcbf5a5d50:这是数组array的地址,它是一个指向数组的指针,占用8个字节。
sizeof(&array) =
8:&array是一个指向数组的指针,它的大小也是8个字节。
总之,数组名在大多数情况下会被解释为指向数组第一个元素的指针,但是当它作为sizeof、&、和赋值操作符的操作数时,它会被解释为指向整个数组的指针。
数组名 array 在这里表示整个数组,因此在 sizeof 操作符中使用 array
时,返回的是整个数组占用的空间大小,即 2 * 4 * sizeof(int) = 32
个字节。
在输出 array 的地址时,使用的是 &array,即数组名 array
的地址。这个地址占用的空间大小是一个指针的大小,即 8 个字节。
需要注意的是,虽然数组名在很多情况下会被自动转换为指向数组首元素的指针,但是在
sizeof 操作符和 &
操作符中,它们都代表整个数组。因此,对于数组名的使用要根据具体的语境来理解。
1 2 3 4 5 6 7 8 9 10 11 int arr[3 ][3 ] = { {1 ,2 ,3 },{4 ,5 ,6 },{7 ,8 ,9 } };int (*arrPtr)[3 ] = arr;int arr[3 ] = {1 ,2 ,3 }; int (*arrPtr)[3 ] = &arr; 为什么第四句话有取地址符而第二句话没有?
在第二句代码中,数组名 arr 表示的是二维数组
int arr[3][3] 中第一个一维数组 int arr[0]
的地址,即 arr 等价于 &arr[0]。因此,将
arr 直接赋值给指针 arrPtr,是可以正确的。
而在第四句代码中,数组名 arr 表示的是一维数组
int arr[3] 中第一个元素 int arr[0]
的地址。由于指针 arrPtr 的类型为
int (*)[3],即指向一个包含 3 个 int
元素的数组的指针,因此需要使用取地址符 & 将一维数组
arr 的地址取出,并强制转换为 int (*)[3]
类型的指针,才能正确地将其赋值给 arrPtr。
简而言之:数组中数组名代表第一个元素的地址。一维数组的数组名代表第一个元素的地址(&a[0]),想赋值给(*p)[]
(指向数组的指针),必须传数组开始的地址(虽然值上可能一样),所以需要取地址符&。二
吗数组中数组名代表第一个元素也就是第一个一维数组的地址,想赋值给(*p)[]
(指向数组的指针),可以直接赋值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 int b[][4 ] = { 1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 };int (*b)[4 ]={1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 }; cout << b << endl; cout << sizeof (b) << endl; cout << &b << endl; cout << sizeof (&b) << endl; cout << b[0 ] << endl; cout << sizeof (b[0 ]) << endl; cout << &b[0 ] << endl; cout << sizeof (&b[0 ]) << endl; cout << &b[0 ][0 ] << endl; cout << sizeof (b[0 ][0 ]) << endl;
元素大小
二维数组
指针作为函数参数
使得实参与形参指针指向共同的内存空间,
达到参数双向传递的目的。即通过被调函数中直接处理主函数中的数据而将函数的处理结果返回其调用者。
减少函数调用时候的开销。C++中可以通过引用和指针实现。
C++中指向函数的指针传递函数代码的首地址。
如果函数体中不需要通过指针改变指针所指向变量的内容,应在参数表中将其声明为指向常量的指针,这样使得常对象被取地址后也可以作为该函数的参数。 指向常量的指针与指针类型的常量
指针作为参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <iostream> using namespace std;void floatSplit (float x, int * intPart, float * floatPart) { *intPart = static_cast <int >(x); *floatPart = x - *intPart; }int main () { float x=3.145 , f; int n; floatSplit (x, &n, &f); cout << "n:" << n << "f" << f << endl; return 0 ; }
指针型函数
函数的返回值为指针类型 ,这个函数就是指针型函数。使用指针函数的主要目的就是要在函数结束时把大量的数据从被调函数返回到主调函数中 。
函数返回数组指针
因为数组不能被复制,因此不能直接返回数组,但是可以返回数组的指针 。
利用类型别名 的方法简化操作。
1 2 3 typedef int arr[10 ]; using arr = int [10 ]; arr *foo (int i) ;
不使用类型别名的话,数组的维度必须跟在函数的名字之后,函数的形参列表也跟在函数名字后面并且先于数组的维度。
类型说明符 (*函数名 ( 参数表) )[数组维度]
int (*foo(int i))[10]
返回一个大小为10的整型数组指针。
C++ 11中提供了简化上述声明的方法。尾置返回类型
auto foo(int i) -> int(*)[10];
接受一个int类型的参数,返回一个指向10个int类型的数组的指针。
如果知道函数返回的指针将指向哪个数组,就可以用decltype关键词声明返回类型。
1 2 3 4 5 6 int a[] = {0 ,1 ,2 ,3 ,4 };int b[] = {5 ,6 ,7 ,8 ,9 };decltype (a) *func (int i){ return (i%2 ) ? &a:&b; }
具体例子
1 2 3 int arr[10 ]; int *p1[10 ]; int (*p2)[10 ];
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <iostream> using namespace std;using arrT = int [5 ];int arry[5 ] = {1 ,2 ,3 ,4 ,5 };arrT* func () { return &arry; }int main () { int (*p)[5 ] =func (); for (int i=0 ;i<5 ;i++) { cout<<*(*p + i)<<" " <<endl; } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <iostream> int a[5 ]={1 ,2 ,3 ,4 ,5 };using namespace std;int (*make_array ())[5 ] { return &a; }int main (int argc,char *argv[]) { int (*p)[5 ]=make_array (); cout<<*p<<endl; for (int i=0 ;i!=5 ;++i) { cout<<(**p)++<<" " ; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <iostream> using namespace std;int arry[10 ] = {1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 };auto func () -> int (*) [10] { return &arry; }int main () { int (*p)[10 ] =func (); for (int i=0 ;i<10 ;i++) { cout<<(**p)++<<" " ; } cout<<endl; return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <cstddef> using std::size_t ;#include <iostream> using namespace std;int odd[] = {1 ,3 ,5 ,7 ,9 };int even[] = {0 ,2 ,4 ,6 ,8 };decltype (odd) *arrPtr (int i) { return (i % 2 ) ? &odd : &even; }int main () { int (*arrP)[5 ] = arrPtr (5 ); for (size_t i = 0 ; i < 5 ; ++i) cout << (*arrP)[i] << endl; return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 int odd[] = {1 ,3 ,5 ,7 ,9 };int even[] = {0 ,2 ,4 ,6 ,8 };decltype (odd) &arrPtr (int i) { return (i % 2 ) ? odd : even; }int main () { int (&arrP)[5 ] = arrPtr (5 ); for (size_t i = 0 ; i < 5 ; ++i) cout << (arrP)[i] << endl; return 0 ; }
指向函数的指针
每个函数的函数名代表代码在内存中的起始地址。调用函数的通常形式“函数名
(参数表)” 的实质就是“函数代码的首地址(参数表)” 。
指向函数的指针 (函数指针)就是用来存放函数代码首地址的变量。
数据类型 (* 函数指针名) (形参表)
函数指针在使用前也要进行赋值,使指针指向一个已经存在的函数代码的起始地址。
指针名 = 函数名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <iostream> using namespace std;void print1 (float ) { cout << "print1" << endl; }void print2 (float data) { cout << data << endl; }int main () { void (*test)(float ); test = print1; test (2.0 ); test = print2; test (3.0 ); return 0 ; }
可以使用typedef简化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <iostream> using namespace std;typedef void (*typeFuncPtr) (float ) ;void print1 (float ) { cout << "print1" << endl; }void print2 (float data) { cout << data << endl; }int main () { typeFuncPtr funcPtr; funcPtr = print1; funcPtr (2.0 ); funcPtr = print2; funcPtr (3.0 ); return 0 ; }
对象指针
对象指针是用来存放对象地址的变量。
类名 * 对象指针名
1 2 3 Point* pointPtr; Point p1; pointPtr = &p1;
使用对象指针可以方便的访问对象的成员,但是使用之前一定要初始化,让它指向一个已经声明过的对象在使用。通过对象指针可以访问对象的公有成员。
对象指针名->成员名
等价于(*对象指针名).成员名
1 2 3 4 5 6 7 8 class Fred ;class Barney { Fred cFred; Fred *ptrFred; };class Fred { Barney y; };
指向类的静态成员的指针
类的静态成员 的访问是不依赖于对象 的,因此可以用普通的指针 来指向和访问静态成员 。
访问静态数据成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #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; } static int count;private : int x, y; };int Point::count = 0 ;int main () { Point p (4 , 5 ) ; int * ptr = &Point::count; return 0 ; }
访问静态函数成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <iostream> using namespace std;class Point {public : Point (int x = 0 , int y = 0 ) :x (x), y (y) { count++; } Point (const Point& p) :x (p.x), y (p.y) { count++; } ~Point () { count--; } int getX () const { return x; } int getY () const { return y; } static void showCount () { cout << "Object count=" << count << endl; }private : int x, y; static int count; };int Point::count = 0 ;int main () { void (* showCountPTR)() = Point::showCount; Point pt (4 , 5 ) ; cout << "Point A: " << pt.getX () << "," << pt.getY () << endl; showCountPTR (); Point pt2 (pt) ; cout << "Point B: " << pt2.getX () << "," << pt2.getY () << endl; showCountPTR (); return 0 ; }
this指针
this指针是一个隐含于某一个类的非静态成员函数中的特殊指针,用于指向正在被成员函数操作的对象。
原理
每一次对成员函数的调用都存在一个目的对象,this指针就是指向这个目的对象的指针。this指针明确指出了成员函数当前所操作的数据所属的对象。
this指针实际上是类成员函数的一个隐含形式参数 。当通过一个对象调用成员函数的时候,系统先将该对象的地址通过该参数传递给成员函数,成员函数对对象的数据成员进行操作时,就隐含使用了this指针。
this是一个指针常量,对于常成员函数,this同时又是一个指向常量的指针。在成员函数中,可以使用*this来标识正在调用该函数的对象。
重要的辨析
成员函数通过this这个额外的隐式参数 来访问调用它的对象。当我们调用成员函数时,会用对象的地址来初始化this指针(对象就是调用成员函数的对象) 。这样我们在成员函数里就可以拿到对象的私有变量了。
this形参是隐式定义的,不会显式地出现在成员函数形参列表里,但实际是存在的,所以任何自定义为this的参数或变量都是非法的。
因为this指针总是指向这个对象 ,所以this是一个常量指针(指针的值是const的) ,不允许修改this指针的值。this指针的默认初始化过程等价TestClass* const this = &tc。
C++规定只能使用指向常量的指针来存放常量对象的地址 ,如果tc是个常量对象 ,那么this的默认初始化就是非法的,参数列表 后的const作用是用来修改隐式this指针的类型 ,把它变成常指针常量 。可以理解为const对象只能调用const方法?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class TestClass {public : TestClass () : val (100 ) {} virtual ~TestClass () {} int getVal_v1 () { return val; } int getVal_v2 () const { return val; }private : int val; };int main () { const TestClass tc; int a = tc.getVal_v1 (); int b = tc.getVal_v2 (); cout << "getVal_v1: " << a << endl; cout << "getVal_v2: " << b << endl; return 0 ; }
指向类的非静态成员的指针
f类的成员自身也是一些变量、函数或者对象等。因此也可以将它们的地址存放到一个指针变量中。这样,可以通过指针直接指向对象的成员,进而可以通过这些指针访问对象的成员。
指向对象的指针使用前先声明、再赋值、然后引用。
声明
类型说明符 类名::* 指针名;
声明指向数据成员的指针
类型说明符 (类名::* 指针名)(参数表);
声明指向函数成员的指针
引用
声明了指向成员的指针之后,需要对其进行赋值 ,也就是确定指向类的哪一个成员。
指针名 = &类名::数据成员名;
访问
注: 由于类的定义值确定了各个成员的类型、所占内存大小以及它们的相对位置,并不为数据成员分配具体的地址。上述赋值只说明了被赋值的指针是专门用来指向哪个数据成员的,以及指针中存放的数据成员在类中的相对位置。
由于类是通过对象实例化的,在声明类的对象时才为具体的对象分配内存空间。这时只要将对象在内存中的起始地址与成员指针中存放的相对偏移结合起来就可以访问到对象的数据成员
访问数据成员:
对象名 .* 类成员指针名
对象指针名-> *类成员指针名
在一个类的作用域之外不能够对它的私有成员取地址。成员函数指针的声明、赋值、使用过程中,返回值类型、函数参数表一定要相互匹配。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #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 () { Point p (4 , 5 ) ; Point* ptPtr = &p; int (Point::*funcPtr)() const ; funcPtr = &Point::getX; int (Point:: * funcPtr2)() const = &Point::getX; cout << (p.*funcPtr)() << endl; cout <<(ptPtr->*funcPtr)() << endl; return 0 ; }
完整例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #ifndef _POINT_H #define _POINT_H class Point {public : Point (int x, int y); ~Point (); static int showCount () ; int getCount () ; int publicInt=5 ; static int publicStaticInt;private : int x, y; static int count; const int test = 3113212 ; };#endif
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include "point.h" #include <iostream> int Point::count = 0 ;int Point::publicStaticInt = 10 ; Point::Point (int x, int y) :x (x), y (y) { count++; }; Point::~Point () { count--; }int Point::getCount () { return count; }int Point::showCount () { return count; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include "point.h" #include <iostream> using namespace std;int main () { Point pt (4 , 5 ) ; Point* ptPtr = &pt; int (Point:: * ptMemberIntPtr) = &Point::publicInt; int * ptMemberStaticIntPtr = &Point::publicStaticInt; int (*showCountPtr)() = Point::showCount; int (Point:: * getCountPtr)() = &Point::getCount; cout << "pt.publicInt " << pt.publicInt << endl; cout << "ptPtr->publicInt " << ptPtr->publicInt << endl; cout << "publicStaticInt " << Point::publicStaticInt << endl; cout << "ptPtr->*ptMemberIntPtrt " << pt.*ptMemberIntPtr << endl; cout << "ptPtr->*ptMemberIntPtrt " << ptPtr->*ptMemberIntPtr << endl; cout << "*ptMemberStaticIntPtr " << *ptMemberStaticIntPtr << endl; cout << "(pt.*getCountPtr)() " << (pt.*getCountPtr)() << endl; cout << "(ptPtr->*getCountPtr)() " << (ptPtr->*getCountPtr)() << endl; cout << "(*showCountPtr)() " << (*showCountPtr)() << endl; pt.getCount (); ptPtr->getCount (); return 0 ; }
1 2 3 4 5 6 7 8 9 10 pt.publicInt 5 ptPtr->publicInt 5 publicStaticInt 10 ptPtr->*ptMemberIntPtrt 5 ptPtr->*ptMemberIntPtrt 5 *ptMemberStaticIntPtr 10 (pt.*getCountPtr)() 1 (ptPtr->*getCountPtr)() 1 (*showCountPtr)() 1
与const结合 很重要的辨析
const与指针
1 2 3 4 5 const int func(const int & a) const 第一个是表示返回值是个int 型的常值 第二个代表是个const 型的引用,就是这个a是可以引用一个int 型变量,但是不可以改变这个变量的值(可以读值) 第三个代表这个函数(应该是类中的成员函数),不可以改变调用对象中的数据成员的值。(可以读写数据成员的值,不可以改写其值)
对于这个函数const
int func(const int& a)
const声明中,三个const分别是什么意思?..._叛逆的鲁鲁修love
CC的博客-CSDN博客
动态内存分配
C++中动态内存分配 技术可以保证程序在运行过程中按实际需要申请适量的内存,使用结束后还可以释放。
这种在程序运行过程中申请和释放的存储单元也称为堆对象 。
在C++程序中,使用new
和delete运算符,建立和删除堆对象,动态分配内存。
语法
new
new 数据类型 (初始化参数列表)
说明: 该语句在运行过程中申请分配用于指定类型数据的内存空间,并根据初始化参数别表中给出的值进行初始化。如果内存申请成功,new运算便返回一个指向新分配内存首地址的类型的指针, 可以通过这个指针堆堆对象进行访问。
普通对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int * point; point = new int (2 ); int * point = new int ; int * point = new int (); Object obj = new Object; Object obj = new Object ();
数组对象
new 类型名 [数组长度]
说明: new关键字创建数组之后,[]后仍可以添加(),但括号内不能带任何啊参数。加上()后,代表对数组的每个元素的初始化。
delete
普通对象
delete 指针名
说明: 该语句用来删除由new建立的对象,释放指针所指向的内存空间。如果被删除的是对象 ,该对象的析构函数将被调用 。对于new建立的对象,只能使用delete进行一次 删除操作。如果对一块内存空间多次使用delete进行删除将会导致运行错误。
数组对象
delete [] 指针名
实例
一般实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #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 << "Default Constructor2 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 ; }
动态数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #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 << "Default Constructor2 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; cout << "Moving to:(" << x << "," << y << ")" << endl; }private : int x, y; };int main () { Point* ptr = new Point[2 ]; ptr[0 ].move (5 ,10 ); ptr[1 ].move (15 , 20 ); ptr->move (5 , 10 ); (ptr+1 )->move (15 , 20 ); cout << "Deleting ..." << endl; return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <cassert> #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 << "Default Constructor2 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; cout << "Moving to:(" << x << "," << y << ")" << endl; }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 () { int count = 2 ; ArrayOfPoints points (count) ; points.element (0 ).move (5 , 0 ); points.element (1 ).move (15 , 20 ); return 0 ; }
动态多维数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <iostream> using namespace std;int main () { float (* cp)[4 ] = new float [3 ][4 ]; for (int i = 0 ; i < 3 ; i++) { for (int j = 0 ; j < 4 ; j++) { *(*(cp + i) + j) = (i+1 ) * (j+1 ); } } for (int i = 0 ; i < 3 ; i++) { for (int j = 0 ; j < 4 ; j++) { cout<<*(*(cp + i) + j)<<" " ; } } cout << endl; for (int i = 0 ; i < 3 ; i++) { for (int j = 0 ; j < 4 ; j++) { cout << cp[i][j] << " " ; } } delete [] cp; return 0 ; }
Vector创建数组
C++ 库提供了被封装的动态数组
vector,可以具有任何类型 。vector不是一个类,而是一个类模板。
语法
声明
vector<元素类型> 数组对象名(数组长度)
vector<元素类型> 数组对象名(数组长度,元素初值)
vector定义的数组对象的所有元素都会被初始化 ,如果数组元素的元素类型为基本数据类型,则所有元素都会被以0初始化 。如果数组元素为类类型 ,
则会调用类的默认构造函数 初始化(需要保证有默认构造函数)。初值也可以自己指定。
访问
数组对象名[下标表达式]
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <iostream> #include <vector> using namespace std;double average (const vector<double >& arr) { double sum = 0.0 ; for (unsigned i = 0 ; i < arr.size (); i++){ sum += arr[i]; } return sum / arr.size (); }int main () { unsigned n; cout << "n= " << endl; cin >> n; vector<double > arr (n) ; cout << "input" << n << "numbers:" << endl; for (unsigned i = 0 ; i < n; i++){ cin >> arr[i]; } cout << "Average=" << average (arr) << endl; return 0 ; }
浅层复制与深层复制
浅层复制
默认构造函数将对象的数据项简单复制之后,不同对象的指针指向的是同一片地址 。表面上好像完成了复制,但是并没有形成真正的副本。
更大的问题在于,在程序结束之前,不同对象的析构函数会被调用,但由于指向同一片空间,该空间会被释放两次,导致出错。
浅层复制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 #include <cassert> #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 << "Default Constructor2 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; cout << "Moving to:(" << x << "," << y << ")" << endl; }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 () { int count = 2 ; ArrayOfPoints pointsArray (count) ; pointsArray.element (0 ).move (5 , 0 ); pointsArray.element (1 ).move (15 , 20 ); ArrayOfPoints pointsArray2 = pointsArray; cout << "Copy of pointsArray" << endl; cout << pointsArray2.element (0 ).getX () << ":" << pointsArray2.element (0 ).getY () << endl; cout << pointsArray2.element (1 ).getX () << ":" << pointsArray2.element (1 ).getY () << endl; cout << pointsArray.element (0 ).getX () << ":" << pointsArray.element (0 ).getY () << endl; cout << pointsArray.element (1 ).getX () << ":" << pointsArray.element (1 ).getY () << endl; pointsArray.element (0 ).move (25 , 30 ); pointsArray.element (1 ).move (35 , 40 ); cout << pointsArray2.element (0 ).getX () << ":" << pointsArray2.element (0 ).getY () << endl; cout << pointsArray2.element (1 ).getX () << ":" << pointsArray2.element (1 ).getY () << endl; cout << pointsArray.element (0 ).getX () << ":" << pointsArray.element (0 ).getY () << endl; cout << pointsArray.element (1 ).getX () << ":" << pointsArray.element (1 ).getY () << endl; return 0 ; }
解决方法: 编写复制构造函数,实现深层复制 。
深层复制
深层复制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 #include <cassert> #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 << "Default Constructor2 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; cout << "Moving to:(" << x << "," << y << ")" << endl; }private : int x, y; };class ArrayOfPoints {public : ArrayOfPoints (int size) :size (size) { points = new Point[size]; } ArrayOfPoints (const ArrayOfPoints& v); ~ArrayOfPoints () { cout << "Deleting... " << endl; delete [] points; } Point& element (int index) { assert (index >= 0 && index < size); return points[index]; }private : Point* points; int size; }; ArrayOfPoints::ArrayOfPoints (const ArrayOfPoints& v) { size = v.size; points = new Point[size]; for (int i = 0 ; i < size; i++){ points[i] = v.points[i]; } }int main () { int count = 2 ; ArrayOfPoints pointsArray (count) ; pointsArray.element (0 ).move (5 , 0 ); pointsArray.element (1 ).move (15 , 20 ); ArrayOfPoints pointsArray2 = pointsArray; cout << "Copy of pointsArray" << endl; cout << pointsArray2.element (0 ).getX () << ":" << pointsArray2.element (0 ).getY () << endl; cout << pointsArray2.element (1 ).getX () << ":" << pointsArray2.element (1 ).getY () << endl; cout << pointsArray.element (0 ).getX () << ":" << pointsArray.element (0 ).getY () << endl; cout << pointsArray.element (1 ).getX () << ":" << pointsArray.element (1 ).getY () << endl; pointsArray.element (0 ).move (25 , 30 ); pointsArray.element (1 ).move (35 , 40 ); cout << pointsArray2.element (0 ).getX () << ":" << pointsArray2.element (0 ).getY () << endl; cout << pointsArray2.element (1 ).getX () << ":" << pointsArray2.element (1 ).getY () << endl; cout << pointsArray.element (0 ).getX () << ":" << pointsArray.element (0 ).getY () << endl; cout << pointsArray.element (1 ).getX () << ":" << pointsArray.element (1 ).getY () << endl; return 0 ; }