#include "stdafx.h"
#include <iostream>
using namespace std;
static int x = 1;
class A
{
public:
A()
{
x = 4;
cout<<"A constructor!"<<endl;
}
virtual ~A() //基类的析构函数为虚函数
{
x = 7;
cout<<"A destructor!"<<endl;
}
virtual void foo() //虚函数,支持动态绑定。
{
x = 9;
}
private:
float a;
};
class B:public A
{
public:
B()
{
x = 5;
cout<<"B constructor!"<<endl;
}
virtual ~B()
{
x = 8;
cout<<"B destructor!"<<endl;
}
virtual void foo()
{
x = 10;
}
void Bar()
{
foo();
}
};
class C:public B
{
public:
C()
{
x = 6;
cout<<"C constructor!"<<endl;
}
virtual ~C()
{
x = 9;
cout<<"C destructor!"<<endl;
}
virtual void foo()
{
x = 11;
}
void Bar() //与基类相同,但基类该函数前无virtual关键字,则基类函数被隐藏
{
foo();
}
};
class D
{
public:
D()
{
x = 12;
cout<<"D constructor!"<<endl;
}
~D() //没有设置为虚函数
{
x = 13;
cout<<"D destructor!"<<endl;
}
private:
float a;
};
class E:public D
{
public:
E()
{
x = 14;
cout<<"E constructor!"<<endl;
}
~E()
{
x = 15;
cout<<"E destructor!"<<endl;
}
};
static D dd; //全局静态变量dd,类型为D。程序结束时,程序会自动调用它的析构函数
int _tmain(int argc, _TCHAR* argv[])
{
cout<<"x="<<x<<endl;
/*
output:
x=12
*/
/*
Why:
为什么不等于1呢?因为我们定义了“static D dd;”,它是静态全局变量,存储在静态数据区,它必须在main()函数前已经被构造初始化(先调用类D的构造函数)。这种执行是由C++ 编译器startup代码实现的,而startup代码是更早于程序进入点(main 或WinMain)执行起来的代码。
main函数还未执行,所以不能打印。
*/
A *p1 = NULL;
cout<<"A *p1 = NULL; x="<<x<<endl;
/*
output:
x=12
*/
/*
定义指针*p,它指向A对象,它没有使用new/delete关键字分配内存资源,所以它不能自动初始构造,不调用A的构造函数。
*/
B *p2 = NULL;
cout<<"B *p2 = NULL;x="<<x<<endl;
/*
output:
x=12
*/
A aObj;
cout<<"A aObj;x="<<x<<endl;
/*
output:
x=12
*/
/*
why:
当用类创建一个实例对象时,C++会为它分配足够的能存放对象所有成员的存储空间,所以它会调用该类的构造函数。这种定义对象方法,内存分配是分配到栈中的,由C++缺省创建构造和撤销析构,与new创建不同。
记住,对象是具体实例,占用内存空间。
*/
A aObjArray[2];
cout<<"A aObjArray;x="<<x<<endl;
/*
outout:
A constructor!
A constructor!
x=4
/*
why:
与上题原理一样,只不过是定义含有两个元素的对象数组,每个对象元素会占用内存,它会分别调用每个对象元素的构造函数,即两次。
记住,N个元素的对象数组,调用N次构造函数。
*/
C cObj;
cout<<"C cObj;x="<<x<<endl;
/*
output:
A constructor!
B constructor!
C constructor!
x=6
*/
/*
why:
对象是由“底层向上”开始构造的,当建立一个对象时,首先调用最原始基类的构造函数,然后调用下一个派生类的构造函数,依次类推,直至到达自身为止。在对象析构时,其顺序正好与之相反。
记住,调用直接基类构造函数时,如果无专门说明,就调用直接基类的默认构造函数。我们必须明确的是当一个类继承与基类,并且自身还包含有其他类的成员对象的时候,构造函数的调用顺序为:调用基类的构造函数->调用成员对象的构造函数->调用自身的构造函数。
构造函数的调用次序完全不受构造函数初始化列表的表达式中的次序影响,与基类的声明次数和成员对象在函数中的声明次序有关。
*/
A *pA1 = (C*)&cObj;
cout<<"A *pA1 = (C*)&cObj;x="<<x<<endl;
/*
output:
x=6
*/
/*
why:
与以上同理,这里把派生类C对象cObj地址赋给基类A的指针pA1,它涉及到了派生类的向上转换,不用调用构造函数。
*/
pA1->foo();
cout<<"pA1->foo();x="<<x<<endl;
/*
output:
x=11
*/
/*
why:
我们知道,虚函数它支持动态绑定,基类的指针可以在程序运行时可以根据需要指向其派生类,调用不同的派生类成员函数。由前可知,基类指针动态绑定为派生类指针,所以当基类指针调用foo()函数时,会调用子类C的foo()函数。这里其实可以通过对象内存模型中的虚函数表中更深入去了解,只不过虚函数可是个大专题,在此只能讲个大概了。
但有一点,基类与派生类中必须要有完全相同的虚函数才行。记住,虚函数是面向对象多态特性的体现。
*/
C *pC1 = new C();
cout<<"C *pC1 = new C();x="<<x<<endl;
/*
output:
A constructor!
B constructor!
C constructor!
x=6
*/
/*
why:
使用new关键字分配内存资源时,它会自动初始构造,而且同上一样(C cObj;)按照继承顺序分别调用基类的构造函数。
这是创建对象的另外一种方法,它是在堆上分配内存来创建对象的(与上A aObj;不同);不同的是,C++用new创建对象时返回的是一个对象指针,pC1指向C的一个对象,C++分配给pC1的仅仅是存放指针值的空间。而且,用new 动态创建的对象必须用delete来撤销该对象,只有delete对象才会调用其析构函数,C++不会缺省撤销析构。
注意:new创建的对象不是用“*”或“.”来访问该对象的成员函数的,而是用运算符“->”;
*/
pC1->Bar();
cout<<"pC1->Bar();x="<<x<<endl;
/*
output:
x=11
*/
/*
why:
该函数与基类B相同,但基类B中该函数前无virtual关键字,则基类函数被隐藏,所以它只能调用自身的foo()函数。而且pC1在编译期间已经被静态绑定为C类型的,所以会调用C的Bar函数。
*/
B *pbObj = new B();
cout<<"B *pbObj = new B();x="<<x<<endl;
pbObj->Bar();
cout<<"pbObj->Bar();x="<<x<<endl;
/*
output:
A constructor!
B constructor!
x = 5
pbObj->Bar();x=10
*/
delete pC1;
cout<<"delete pC1;x="<<x<<endl;
/*
output:
C destructor!
B destructor!
A destructor!
x = 7
*/
/*
why;
在对象析构时,其顺序正好与构造函数相反。
*/
pA1 = NULL;
cout<<"pA1 = NULL;x="<<x<<endl;
/*
output:
x = 7
*/
/*
why:
与A *p1 = NULL;同理
*/
delete pbObj;
cout<<"delete pbObj;x="<<x<<endl;
/*
output:
B destructor!
A destructor!
x = 7
*/
D *pdObj = new E();
/*
output:
D constructor!
E constructor!
x = 14
*/
/*
why:
与以上同理,因为是要new 类E的对象,所以先调用基类D的构造函数,然后子类E的构造函数。
*/
delete pdObj;
cout<<"delete pdObj;x="<<x<<endl;
pdObj = NULL;
/*
output:
D destructor!
x = 13
*/
/*
why:
在类的继承中,如果有基类指针指向派生类,那么delete基类指针时,如果析构函数不定义成虚函数,则派生类中派生的那部分无法析构,从而可能导致内存泄露。我们可以看到类D中的析构函数不是虚函数,所以当我们delete基类指针(指向子类E)时,它只调用基类D的析构函数。
记住,当基类指针指向子类时,我们最好把基类的析构函数定义为虚函数。
*/
/*
output:
C destructor!
B destructor!
A destructor!
A destructor!
A destructor!
A destructor!
D destructor!
x = 13
*/
/*
why:
C destructor!
B destructor!
A destructor!
前三个析构函数是对应“C cObj;”语句的,因为对象是栈上分配内存的,所以程序结束时自动缺省调用析构函数,而析构顺序与构造顺序相反。
A destructor!
是对应“A aObj;”语句的,程序结束时自动缺省调用析构函数。
A destructor!
A destructor!
是对应“A aObjArray[2];”语句的,有两个对象元素,则要调用析构函数两次。
D destructor!
是对应“static D dd;”语句的。因为它的类型是全局静态变量dd,程序结束时会最后才自动调用它的析构函数。
*/
return 0;
}
Output:
x=12
A *p1 = NULL; x=12
B *p2 = NULL;x=12
A constructor!
A aObj;x=4
A constructor!
A constructor!
A aObjArray;x=4
A constructor!
B constructor!
C constructor!
C cObj;x=6
A *pA1 = (C*)&cObj;x=6
pA1->foo();x=11
A constructor!
B constructor!
C constructor!
C *pC1 = new C();x=6
pC1->Bar();x=11
A constructor!
B constructor!
B *pbObj = new B();x=5
pbObj->Bar();x=10
C destructor!
B destructor!
A destructor!
delete pC1;x=7
pA1 = NULL;x=7
B destructor!
A destructor!
delete pbObj;x=7
D constructor!
E constructor!
D destructor!
delete pdObj;x=13
C destructor!
B destructor!
A destructor!
A destructor!
A destructor!
A destructor!
D destructor!
请按任意键继续. . .
最终结果一下面为准
D constructor!
x=12
A *p1 = NULL; x=12
B *p2 = NULL;x=12
A constructor!
A aObj;x=4
A constructor!
A constructor!
A aObjArray;x=4
A constructor!
B constructor!
C constructor!
C cObj;x=6
A *pA1 = (C*)&cObj;x=6
pA1->foo();x=11
A constructor!
B constructor!
C constructor!
C *pC1 = new C();x=6
pC1->Bar();x=11
A constructor!
B constructor!
B *pbObj = new B();x=5
pbObj->Bar();x=10
C destructor!
B destructor!
A destructor!
delete pC1;x=7
pA1 = NULL;x=7
B destructor!
A destructor!
delete pbObj;x=7
D constructor!
E constructor!
D destructor!
delete pdObj;x=13
C destructor!
B destructor!
A destructor!
A destructor!
A destructor!
A destructor!
D destructor!
试题来自: http://blog.csdn.net/oceanlucy/article/details/21353855