一道面试题及答案,求分析

时间:2021-04-01 19:07:57
#include <iostream>
using namespace std;

int i = 0;

class A
{
public:
A() {cout << ++i << ". A()\n";}
~A() {cout << ++i << ". ~A()\n";}
};

class B
{
public:
B() {cout << ++i << ". B()\n";}
~B() {cout << ++i << ". ~B()\n";}
};


class C
{
public:
C() {cout << ++i << ". C()\n";}
~C() {cout << ++i << ". ~C()\n";}
};

class D
{
public:
D() {cout << ++i << ". D()\n";}
~D() {cout << ++i << ". ~D()\n";}
virtual void print() { cout << ++ i << ". D::print()\n";}
private:
B _b;
A _a;
};

class E : public D
{
public:
E() {cout << ++i << ". E()\n";}
~E() {cout << ++i << ". ~E()\n";}
void print() { cout << ++ i << ". E::print()\n";}
};

int main()
{
D * ptr = new E();
ptr->print();
delete ptr;
}


该题的运行结果是:

1. B()
2. A()
3. D()
4. E()
5. E::print()
6. ~D()
7. ~A()
8. ~B()

请各位大侠帮忙解释一下结果,拜谢。

8 个解决方案

#1


这个程序是有错误的,因为D里面有虚函数,析构函数却不是虚函数,所以会造成这样的结果

#2


要构造子类必须先构造基类,所以D()然后E(),D类里有A和B两个类的对象,所以成员变量要先构造结果就是B(),A(),D(),E()。然后是虚函数的调用,这个是多态的一个例子,父类的指针,指向继承类的对象,调用的方法是虚函数那么就会调用继承类的方法,然后是对象的删除,这里有个问题,D是基类,但是它的析构函数不是虚函数,这样尽管它指向了E的对象,但是E的析构函数却没有调用,这个是C++中的经典问题了,你的类要被人继承,那么析构函数就要是虚函数。

#3


1. 构造函数调用顺序,先基类,后派生类,按成员对象声明顺序构造类成员对象

2. 虚构顺序相反

#4


同意楼上

#5


其他的属于基本的,lz比较疑惑的应该在于第5行和第6行之间为什么没有输出  "6. ~E()". 

只要把D的析构函数写成虚函数 virtual ~D() {cout <<++i <<". ~D()\n";}就可以输出这一行

这也是为什么推荐吧析构函数写成虚函数。试想,如果E的构造函数中new了内存,却没有调到~E()导致没有机会释放内存,势必造成内存泄漏。

#6


D * ptr = new E(); 这种用法在实际中用的还是比较广泛的,因为经常会用的,需要吧一个基类的几个派生类成员统一管理,这样都用基类指针变量来保存会很方便

#7


1,2,5楼的都对..

#8


mark, 构造函数先基类,再成员对象,再自身。

#1


这个程序是有错误的,因为D里面有虚函数,析构函数却不是虚函数,所以会造成这样的结果

#2


要构造子类必须先构造基类,所以D()然后E(),D类里有A和B两个类的对象,所以成员变量要先构造结果就是B(),A(),D(),E()。然后是虚函数的调用,这个是多态的一个例子,父类的指针,指向继承类的对象,调用的方法是虚函数那么就会调用继承类的方法,然后是对象的删除,这里有个问题,D是基类,但是它的析构函数不是虚函数,这样尽管它指向了E的对象,但是E的析构函数却没有调用,这个是C++中的经典问题了,你的类要被人继承,那么析构函数就要是虚函数。

#3


1. 构造函数调用顺序,先基类,后派生类,按成员对象声明顺序构造类成员对象

2. 虚构顺序相反

#4


同意楼上

#5


其他的属于基本的,lz比较疑惑的应该在于第5行和第6行之间为什么没有输出  "6. ~E()". 

只要把D的析构函数写成虚函数 virtual ~D() {cout <<++i <<". ~D()\n";}就可以输出这一行

这也是为什么推荐吧析构函数写成虚函数。试想,如果E的构造函数中new了内存,却没有调到~E()导致没有机会释放内存,势必造成内存泄漏。

#6


D * ptr = new E(); 这种用法在实际中用的还是比较广泛的,因为经常会用的,需要吧一个基类的几个派生类成员统一管理,这样都用基类指针变量来保存会很方便

#7


1,2,5楼的都对..

#8


mark, 构造函数先基类,再成员对象,再自身。