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的析构函数。