2.3 对象指针

  • 一、要点归纳
  • 1.用new动态创建实例
  • 2.用delete销毁对象指针指向的实例
  • 二、面试真题解析
  • 面试题1
  • 面试题2
  • 面试题3


一、要点归纳

  和普通变量的指针一样,用户也可以定义对象的指针。对象指针训词创建它指向的实例,然后通过对象指针操作这个指向的实例。

1.用new动态创建实例

  可以用new运算符动态地创建对象指针指向的实例。在用new运算符创建对象实例(匿名对象)时同样要自动调用构造函数,以便完成对象实例数据成员的初始化。

2.用delete销毁对象指针指向的实例

  对于用new运算符动态创建的对象实例,可以使用delete运算符销毁,在销毁时会自动调用析构函数。若不使用delete运算符销毁,在程序结束时该对象仍然存在,并占用相应的存储空间,即系统不能自动销毁动态创建的对象实例。例如如下示例:

#include <iostream>
class Test
{
    char name;
    int x,y;
public:
    Test(char na,int x1,int y1)//构造函数
    {
        name=na;x=x1;y=y1;
        std::cout<<"调用"<<name<<"的构造函数"<<std::endl;
    }
    ~Test()
    {
        std::cout<<"调用"<<name<<"的析构函数"<<std::endl;
    }
    void dispoint()
    {
        std::cout<<"("<<x<<","<<y<<")"<<std::endl;
    }
};

int main(int argc, char const *argv[])
{
    Test a('a',1,2),b('b',3,4);
    Test *p=new Test('p',5,6);//通过对象指针p指向创建的匿名对象
    std::cout<<"p:";p->dispoint();
    delete p;
    std::cout<<"a:";a.dispoint();
    std::cout<<"b:";b.dispoint();
    return 0;
}

输出如下:

调用a的构造函数
调用b的构造函数
调用p的构造函数
p:(5,6)
调用p的析构函数
a:(1,2)
b:(3,4)
调用b的析构函数
调用a的析构函数

从程序的执行结果可以看到以下几点:

  • 当创建多个类对象时,按她们创建的先后顺序调用构造函数,如该程序中a\b\p的顺序调用构造函数;
  • 匿名对象必须使用delete运算符销毁,在执行delete运算时会调用析构函数;
  • main函数执行完毕,首先执行匿名对象的析构函数(有delete操作),再按类对象创建的相反次序调用它们的析构函数;

  归纳起来,对于动态实例的创建和销毁,其过程如下:

  • new的时候做两件事,一是分配实例的内存空间,二是调用构造函数进行数据成员的初始化;
  • delete的时候也做两件事,一是调用析构函数做清理工作,二是释放实例的内存空间。

二、面试真题解析

面试题1

【面试题】若Test是一个类名,其有如下语句序列,下面语句中,调用构造函数的次数是(2)

Test c1, *c2;
    Test *c3 = new Test;
    Test &c4 = c1;

【答】定义c1对象调用一次构造函数,为对象指针c3使用new运算符动态分配内存空间时调用一次构造函数,而定义c1的引用c4时并不会调用构造函数。故只有两次;

面试题2

【面试题】假设有类AB,有相应的构造函数定义,能执行以下语句:

AB a(4),b(5),c[3],*p[2]={&a,&b}

执行完此语句后调用该类构造函数的次数是(5)
【答】a,b各调用构造函数一次,定义对象数组c时调用构造函数3次。定义对象指针数组p并初始化时并没有创建新的实例,所以不会调用构造函数。

面试题3

  已知A,B,C,D四个类,问以下代码执行时析构函数的调用顺序是什么?(ABDC)

C c;
void main()
{
	A *pa=new A();
	B b;
	static D d;
	delete pa;
}

以上各个类创建时调用构造函数的顺序是:类C的全局对象c、pa指向的类A对象、类B的对象b和静态对象d。遇到delete pa语句时调用A的析构函数。程序结束调用b的析构函数。接着释放静态对象d,调用D的析构函数,最后释放全局对象c,调用C的析构函数。