白话C++系列(13)-- 对象指针、对象成员指针

对象指针

所谓对象指针,顾名思义就是有一个指针,其指向一个对象,下面经过一个例子来讲明这样一个问题。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这两个对象成员指针)。

相关文章
相关标签/搜索