所谓对象指针,顾名思义就是有一个指针,其指向一个对象,下面经过一个例子来讲明这样一个问题。ios
在这个例子中,咱们定义了一个坐标的类(Coordinate),其有两个数据成员(一个表示横坐标,一个表示纵坐标)。当咱们定义了这个类以后,咱们就能够去实例化它了。若是咱们想在堆中去实例化这个对象呢,就要以下所示:函数
经过new运算符实例化一个对象后(这个对象就会执行它的构造函数),而对象指针p就会指向这个对象。咱们的重点是要说明p与这个对象在内存中的相关位置以及它们之间的对应关系。学习
当咱们经过这样的方式实例化一个对象后,它的本质就是在内存中分配出一块空间,在这块空间中存储了横坐标(m_iX)和纵坐标(m_iY),此时m_iX的地址与p所保存的地址应该是一致的,也就是说p所指向的就是这个对象的第一个元素(m_iX)。若是想用p去访问这个元素,很简单,就能够这样来访问(p -> m_iX或者p -> m_iY),也能够在p前加上*,使这个指针变成一个对象,而后经过点号(.)来访问相关的数据成员(如(*p).m_iY)。接下来看一下以下的具体范例。spa
注意:这里的new运算符能够自动调用对象的构造函数,而C语言中的malloc则只是单纯的分配内存而不会自动调用构造函数。3d
题目描述:指针
/* 示例要求code
定义Coordinate类对象
数据成员:m_iX和m_iYblog
声明对象指针,并经过指针操控对象内存
计算两个点,横、纵坐标的和
/* **************************************/
头文件(Coordinate.h)
class Coordinate { public: Coordinate(); ~Coordinate(); public: int m_iX; int m_iY; };
源程序(Coordinate.cpp)
#include"Coordinate.h" #include<iostream> using namespace std; Coordinate::Coordinate() { cout <<"Coordinate()"<< endl; } Coordinate::~Coordinate() { cout <<"~Coordinate()"<< endl; }
主调程序(demo.cpp)
#include"Coordinate.h" #include<iostream> #include<stdlib.h> using namespace std; int main() { /* 使用两种方法定义对象指针 */ Coordinate *p1 = NULL;//定义一个对象指针 p1 = new Coordinate; //让p1指向一段内存,这里也能够写成p1 = new Coordinate(),由于其默认构造函数没有参数 Coordinate *p2 = new Coordinate(); /* 使用两种方法让对象指针访问数据成员 */ p1->m_iX = 10; p1->m_iY = 20; (*p2).m_iX = 30; (*p2).m_iY = 40; cout << p1->m_iX +(*p2).m_iX << endl; cout << p1->m_iY +(*p2).m_iY << endl; delete p1; p1 = NULL; delete p2; p2 = NULL; system("pause"); return 0; }
运行结果:
此外,做为对象指针来讲,还能够指向栈中的一块地址,怎么来作呢?咱们来修改一下主调程序以下:
#include"Coordinate.h" #include<iostream> #include<stdlib.h> using namespace std; int main() { ///* 使用两种方法定义对象指针 */ //Coordinate *p1 = NULL;//定义一个对象指针 //p1 = new Coordinate; //让p1指向一段内存,这里也能够写成p1 = new Coordinate(),由于其默认构造函数没有参数 //Coordinate *p2 = new Coordinate(); // ///* 使用两种方法让对象指针访问数据成员 */ //p1->m_iX = 10; //p1->m_iY = 20; //(*p2).m_iX = 30; //(*p2).m_iY = 40; //cout << p1->m_iX +(*p2).m_iX << endl; //cout << p1->m_iY +(*p2).m_iY << endl; //delete p1; //p1 = NULL; //delete p2; //p2 = NULL; Coordinate p1; //从栈中实例化一个对象p1 Coordinate *p2 = &p1; //让对象指针p2指向p1 p2->m_iX = 10; p2->m_iY = 20; //这里咱们来打印p1的横坐标和纵坐标,来讲明是对象指针p2操纵了对象p1 cout <<"对象p1这个点的坐标是:("<< p1.m_iX <<","<< p1.m_iY <<")"<< endl; system("pause"); return 0; }
对象成员指针是什么呢?那么咱们来想想,以前咱们学习过对象成员。对象成员,就是做为一个对象来讲,它成为了另一个类的数据成员。而对象成员指针呢,则是对象的指针成为了另一个类的数据成员了。
咱们先来回顾一个熟悉的例子,以下:
左边呢,咱们定义了一个点的坐标类,它的数据成员有点的横坐标和纵坐标;右边呢,咱们定义了一个线段类,在这个线段类中,须要有两个点(一个起点和一个终点),咱们用点A和点B来表示,咱们当时用的是坐标类的对象,分别是m_coorA和m_coorB。如今呢,咱们要把它们变成指针,以下:
初始化的时候呢,与对象成员初始化的方法能够是同样的,使用初始化列表来初始化,只不过如今是指针了,因此咱们赋初值NULL。
除了可使用初始化列表进行初始化之外,还可使用普通的初始化,好比说,在构造函数中,写成以下方式:
固然,更多的是下面的状况,由于咱们这是两个指针,必定要指向某一个对象,才可以进行操做,才会有意义。而它指向的就应该是两个点的坐标对象:
在这里面,指针m_pCoorA指向了一个坐标对象(1,3),m_pCoorB指向了另一个坐标对象(5,6)。那么,这就至关于在构造函数当中,咱们从堆中分配了内存。既然在构造函数当中从堆中分配了内存,那么咱们就须要在析构函数中去把这个内存释放掉,这样才可以保证内存不被泄漏。
此外呢,做为对象成员和对象成员指针还有另一个很大的不一样。做为对象成员来讲,若是咱们使用sizeof这个对象的话,它就应该是里面全部对象的体积的总和(以下图所示)
而对象成员指针则不一样,咱们来看一看刚刚对象成员指针咱们定义的时候是如何定义的。咱们能够看到,咱们定义的时候呢,是写了两个指针做为它的对象成员。而咱们知道,一个指针在32位的编译器下面,它只占4个基本内存单元,那么两个指针呢,则占8个基本内存单元,而咱们前面所讲到的Coordinate类呢,它有两个数据成员,这两个数据成员都是int型的,因此呢,每个数据成员都应该占4个基本的内存单元。那么这样算下来呢,咱们来想想,若是咱们使用sizeof来判断一个line这样的对象,到底有多大呢?若是在line这个对象中定义的是对象成员(即两个Coordinate),那么这两个Coordinate每个就应该都占8个基本内存单元,那么两个呢,就应该占16个基本内存单元,打印出来就应该是16,可是如今呢,line对象中是两个对象成员指针,那么每个对象成员指针应该只占4个基本内存单元,因此sizeof(line)计算出来就应该是8,加起来是这两个指针的大小的总和。
当实例化line这个对象的时候,那么两个指针(m_pCoorA和m_pCoorB)也会被定义出来,因为两个指针都是指针类型,那么都会占4个基本内存单元。若是咱们在构造函数当中,经过new这样的运算符从堆中来申请内存,实例化两个Coordinate这样的对象的话呢,这两个Coordinate对象都是在堆中的,而不在line这个对象当中,因此刚才咱们使用sizeof的时候呢,也只能获得8,这是由于m_pCoorA占4个基本内存单元,m_pCoorB占4个基本内存单元,而右边的两个Coordinate对象并不在line这个对象的内存当中。当咱们销毁line对象的时候呢,咱们也应该先释放掉堆中的内存,而后再释放掉line这个对象。
/* 对象成员指针
要求:
定义两个类:
坐标类:Coordinate
数据成员:m_iX和m_iY
成员函数:构造函数、西沟函数、数据成员封装函数
线段类:Line
数据成员:点A指针 m_pCoorA,点B指针m_pCoorB
成员函数:构造函数、析构函数、信息打印函数
/* **************************************/
头文件(Coordinate.h)
class Coordinate { public: Coordinate(int x, int y); ~Coordinate(); int getX(); int getY(); public: int m_iX; int m_iY; };
源程序(Coordinate.cpp)
#include"Coordinate.h" #include<iostream> using namespace std; Coordinate::Coordinate(int x, int y) { m_iX = x; m_iY = y; cout <<"Coordinate() "<< m_iX <<","<< m_iY << endl; } Coordinate::~Coordinate() { cout <<"~Coordinate() "<< m_iX <<","<< m_iY << endl; } int Coordinate::getX() { return m_iX;; } int Coordinate::getY() { return m_iY;; }
头文件(Line.h)
#include"Coordinate.h" classLine { public: Line(int x1, int y1, int x2, int y2); ~Line(); void printInfo(); private: Coordinate *m_pCoorA; Coordinate *m_pCoorB; };
源程序(Line.cpp)
#include"Line.h" #include<iostream> using namespace std; Line::Line(int x1, int y1, int x2, int y2) { //从堆中实例化两个坐标对象,并使指针m_pCoorA和m_pCoorB分别指向这两个对象 m_pCoorA = new Coordinate(x1, y1); m_pCoorB = new Coordinate(x2, y2); cout <<"Line()"<< endl; } Line::~Line() { delete m_pCoorA; m_pCoorA = NULL; delete m_pCoorB; m_pCoorB = NULL; cout <<"~Line()"<< endl; } voidLine::printInfo() { cout <<"printInfo()"<< endl; cout <<"("<< m_pCoorA->getX() <<","<< m_pCoorA->getY() <<")"<< endl; cout <<"("<< m_pCoorB->getX() <<","<< m_pCoorB->getY() <<")"<< endl; }
主调函数(demo.cpp)
首先咱们只实例化一个线段对象(同时传入四个参数),而后就销毁这个对象,不作其余操做,以下:
#include"Line.h" #include<iostream> #include<stdlib.h> using namespace std; int main() { //从堆中实例化一个线段对象,并传入四个参数 Line *p = new Line(1,2, 3, 4); delete p; p = NULL; system("pause"); return 0; }
咱们来看一下运行结果:
从这个运行结果来看,首先实例化了一个点坐标对象A,而后又实例化了一个点坐标对象B,接着才实例化了一个线段的对象;因为后面调用了delete,A和B就会触发这两个Coordinate对象的析构函数,最后调用Line自己的析构函数。
此外,咱们如今在main函数中打印一下信息,经过p来调用printInfo()函数,同时经过sizeof来计算一下其大小,以下代码:
int main() { //从堆中实例化一个线段对象,并传入所个参数 Line *p = new Line(1,2, 3, 4); p->printInfo(); delete p; p = NULL; cout <<sizeof(p) << endl; cout <<sizeof(Line) << endl; system("pause"); return 0; }
再来看一下运行结果:
从运行结果看,经过p是能够正常调用信息打印printInfo()函数的(屏幕中间已经打印出信息打印函数名,而且也打印出了A点坐标和B点坐标)。最后,打印出4和8,告诉咱们,指针p自己大小为4,而Line对象大小为8(说明Line仅仅包含m_pCoorA和m_pCoorB这两个对象成员指针)。