继承机制下编译系统先执行函数
A. php类继承的问题,子类继承父类,实例化子类,先执行父类还是先执行子类
不是这个意思。所谓“实例化子类的对象前要先实例化父类中的内容,为父类初始化”,是指当子类正在实例化时,总是链式调用父类构造方法初始化父类空间。换句话说,子类的构造方法必须先调用父类的构造方法,完了才能干别的初始化工作。如果子类没有显式调用语句,编译器会隐式帮你加上。
如:
public class A{ public A() { }}public class B extends A{ public B() { }}等价于:
public class A{ public A() { super();//这里实际上是Object(); //其他初始化工作 }}public class B extends A{ public B() { super();//这里实际上是A(); //其他初始化工作 }}另外,如果有这种情况:
public class A{ X x=new X(); public A() { }}public class B extends A{ Y y=new Y(); public B() {//这是类A空间里的字段都已经初始化完毕。 }}在成员y被赋值之前,x一定已经存在。换句话说,X x=new X();一定先于Y y=new Y();执行。
B. 在php中,子类extends继承了父类,当子类和父类同时存在构造函数__construct先执行哪一个呢
1、如果父类和子类中都没有显式的定义__construct,在实例化子类对象时,只会隐含的调用子类自己的构造方法。
2、如果父类中有显式的构造方法__construct,而子类中没有定义__construct,在实例化子类对象时,就会调用父类中的构造方法。
3、如果父类和子类中都显式的定义了__construct,在实例化子类对象时,只会调用子类自己的构造方法(这就像是子类重构了父类的构造方法),而如果也想要调用父类的构造方法的话,就需要在子类的__construct 方法中显式的调用,(如 __construct(){ parent::_construct();})。
(2)继承机制下编译系统先执行函数扩展阅读
子类的构造函数名与子类名相同。
在子类里父类的构造函数不会自动执行。
要在子类里执行父类的构造函数,必须执行类似以下语句:
$this->[父类的构造函数名()]
类的构造函数统一命名为__construct()。
子类的构造函数名也是__construct()(也是废话)。
在子类里父类的构造函数会不会执行,分两种情况:
1、如子类不定义构造函数 __construct(),则父类的构造函数默认会被继承下来,且会自动执行。
2、如子类定义了构造函数 __construct(),因为构造函数名也是__construct(),所以子类的构造函数实际上是覆盖(override)了父类的构造函数。这时执行的是该子类的构造函数。
这时如果要在子类里执行父类的构造函数,必须执行类似以下语句:parent::__construct();
C. C++多继承方式构造函数的执行顺序
明一个子类的对象穗册时,首先梁族斗调用父类的构造函数,如果一个子类有多个父类时,则按照声明的顺序一次执行父类的构造函数,如class Derived : public Base1,public Base2 你先声明Base1,后Base2,所以先调用Base1后Base2的构造函数,而与 Base2(z),Base1(y)的顺序无关。
本人现在缺分,橡磨给我加分吧!
D. C++继承的构造顺序
1.构造函数
先看下面的类定义
class
B1 class B2
{ {
public: public:
int
i; int i;
B1() { i = 0;
} B2() { i = 0; }
virtual void f()
{} virtual void f() {}
}
; } ;
class
M1 class M2
{ {
public: public:
int
i; int i;
M1() { i = 0;
} M2() { i = 0; }
virtual void mf()
{} virtual void mf() {}
}
; };
class C : public B1, public B2
{
public:
virtual void f() {}
M1 m1;
M2 m2;
};
(1)编译器会不会为C生成默认构造(default ctor)
编译器只在需要时才会为一个类生成def
ctor。“需要时”指:
a.一个类有虚函数
b.一个类有虚基类
c.一个类的基类有def ctor
d.一个类的成员类有def ctor
在这里,class C符合c,d两个条件,编译器会为它生成def
ctor.
(2)默认构造的内容
编译器生成的def ctor是这样的
C::C()
{
B1::B1()
B2::B2()
设定虚表指针指向C的虚表
m1::M1()
m2::M2()
}
a.按声明顺序构造基类
b.设定虚表指针
c.按声明顺序构造成员类
(3)对自定义构造函数的改造
对于B1,B2,M1,M2,已有构造函数,但编译器会对其改造,加入一些代码,完成必要的初始化工作。改造后的ctor如下:(以B1为例)
B1::B1()
{
设定虚表指针指向B1的虚表
i = 0;
}
a.设定虚表指针
b.如果用户写有代码(如i=0),则执行这些代码
(4)综合(2),(3),构造函数完整代码如下
T::T()
{
按声明顺序构造基类
设定虚表指针
按声明顺序构造成员类
如果用户写有代码,则执行这些代码
}
在用户代码执行前,基类与成员类已构造完毕,虚指针已设定。
所以C的构造顺序是:B1-〉B2-〉虚指针设定-〉m1-〉m2->C自己的代码(如果有的话)
由(3)可知,每个类的ctor中,都会暂时将虚指针指向自己的虚表。调用B1::B1时,虚指针指向B1的虚表,然后在C::C中,虚指针被重新设定为C的虚表。
在继承体系中,构造是由上而下的,B1或B2构造时,C还未构造。C构造时,B1和B2已构造完成。
所以在继承体系中,一个类的构造函数执行时,该类的所有基类已构造完毕,虚指针已设定,所有成员类也已构造完毕。但该类的所有子类均未构造完成。
2.析构函数
(1)编译器会不会为C生成析构函数(dtor)
编译器只在需要时才会为一个类生成dtor。“需要时”指:
a.类的基类有析构
b.类的成员类有析构
与构造不同,少了两点,因为在一个类析构时,这是一个破坏操作(destory),既然类以已经没用了,何必再把虚指针设一下呢。
(2)析构的内容
编译器生成的析构是这样的
C::~C()
{
m2::M2()
m1::M1()
B2::B2()
B1::B1()
}
a.按声明顺序相反的顺序析构成员类
b.按声明顺序相反的顺序析构基类
这里的相反顺序是C++标准规定的要求,不同编译器都应做到,否则不符合标准(实际上编译器或多或少都有不符标准的地方,当然不是指这里所说的顺序问题)
(3)完整的析构的调用顺序
对于已有析构函数的类,编译器会对其改造,加入一些代码,完成必要的工作。改造后的dtor如下:
T::~T()
{
设定虚表指针指向T的虚表
执行用户代码
按声明顺序相反的顺序析构成员类
按声明顺序相反的顺序析构基类
}
这是完整的析构的调用顺序
在用户代码执行前,虚指针已重新设定,以便用户代码能正确执行。然后再析构成员类和基类。
所以,如果上面例子中的五个类都有析构函数的话,调用顺序是:
虚指针设定-〉C自己的代码->m2-〉m1-〉B2-〉B1
每个类的dtor中,都会暂时将虚指针指向自己的虚表。调用C::~C时,虚指针指向C的虚表,然后在B1::~B1中,虚指针被重新设定为B1的虚表。
可见,在继承体系中,析构是由下而上的,B1或B2析构时,C已被析构。C析构时,B1和B2还未被析构。
所以在继承体系中,一个类的析构函数执行时,该类的所有基类还未被析构,所有成员类也未被析构。但该类的所有子类均已析构完成。
3.虚函数
在构造或析构中调用虚函数,会怎样.如:
B1::B1()
{
f();
}
调用的是B1::f()还是C::f()
答案是B1::f()
每个类的ctor中,都会暂时将虚指针指向自己的虚表。调用B1::B1时,虚指针指向B1的虚表
所以是B1::f()
如果在C的构造函数中调用f(),那就调用C::f()