CnPack Forum


 
Subject: 讨论一个virtual的问题。
Passion (LiuXiao)
管理员
Rank: 9Rank: 9Rank: 9


UID 359
Digest Posts 19
Credits 6760
Posts 3556
点点分 6760
Reading Access 102
Registered 2004-3-28
Status Offline
Post at 2007-7-7 16:29  Profile | Blog | P.M. 
讨论一个virtual的问题。

C++里头,类的构造函数不能是virtual的。
但ObjectPascal里头的类,其constructor却能是virtual的,可以被override。

我还没想明白为什么会这样。
欢迎讨论。
Top
kendling (小冬)
高级版主
Rank: 8Rank: 8
MyvNet


Medal No.1  
UID 703
Digest Posts 5
Credits 978
Posts 580
点点分 978
Reading Access 101
Registered 2005-2-18
Location 广东
Status Offline
Post at 2007-7-9 09:31  Profile | Site | Blog | P.M.  | QQ | Yahoo!
难道C++里是默认Virtual,要不怎么在继承类里使用构造函数初始化类?




小冬
http://MyvNet.com
Top
shenloqi
灌水处处长
Rank: 4



UID 34
Digest Posts 1
Credits 287
Posts 179
点点分 287
Reading Access 10
Registered 2003-3-15
Status Offline
Post at 2007-7-9 14:40  Profile | P.M. 
个人的看法,对C++不熟悉,所以可能是错误的,而且也查了一些资料,似乎观点跟我的观点的出发点是不一样的。

我认为虚函数的作用主要是重用基类代码和延后性(动态联编或晚绑定)。

Delphi的构造函数是可以为虚的,但是创建对象的时候还是需要明确知道被创建的类的类型的,并不能通过 varChild := (class of varParent).Create 来构造(当然我相信如果编译器再聪明一些的话应该是可以的),这样也就是说虚函数的延后性的特点是没有了,只剩下重用基类的功能了(其实只有对象正确建立之后才能使用虚函数的延后性,我觉得对于强类型系统来说这都是一样的)。

Delphi里面的类的构造函数标志着需要调用@ClassCreate,调用NewInstance分配需要的内存并初始化,然后再调用构造函数,也就是说在真正调用虚构造函数的时候已经确定了内存大小和类型信息等等,所以这时候完全可以做到在构造函数的重用基类的构造函数了(只要能够正确得到基类的类型信息,提供需要的内存结构就可以了)。Delphi的构造函数并不负责自身的创建,所以构造函数可以互相调用,也可以调用多次——前提是构造函数本身不涉及再次构造别的对象。

C++的对象的创建过程不是这样的,C++的构造函数是负责分配资源的,负责把对象变成一个存在的物,所以便不允许使用虚构造函数了(在构造函数中,VPTR指向的是当前类的构造器,所以在构造函数中调用虚方法也是没有延后性的,不过Delphi中即使构造函数可以继承,但是由于创建对象时的类型限制,在构造函数中调用虚函数也仅仅是起到重用的功能)。

还有人说因为传统的C++没有动态的类型信息,所以很难实现虚构造函数。我觉得从原理上Delphi与C++的标准实现一样,每个类型都有一个VTABLE排列了虚函数(Delphi有Dynamic和Virtual之分,而且Delphi还有Interface的实现等等不同,但是原理上都是一样的),然后对对象的虚函数调用都会被编译器修改(a->method() ==> a->vptr[offset]()),所以如果要实现Delphi所实现的重用性的话,应该也是可以的(但是考虑到C++与Delphi的构造函数的意义的差别,如果也把C++的构造函数修改为Delphi的一样,可能会破坏以前的代码的兼容性)。

其实,我觉得如果C++设计的时候太过分在乎虚函数的延后性了,而且C++中对象创建比Delphi灵活(直接创建,赋值创建,拷贝创建等等),所以C++大概就牺牲了虚函数的另外一个好处:重用性了。
Top
kendling (小冬)
高级版主
Rank: 8Rank: 8
MyvNet


Medal No.1  
UID 703
Digest Posts 5
Credits 978
Posts 580
点点分 978
Reading Access 101
Registered 2005-2-18
Location 广东
Status Offline
Post at 2007-7-10 10:12  Profile | Site | Blog | P.M.  | QQ | Yahoo!
构造函数作虚函数貌似用处不大。




小冬
http://MyvNet.com
Top
Passion (LiuXiao)
管理员
Rank: 9Rank: 9Rank: 9


UID 359
Digest Posts 19
Credits 6760
Posts 3556
点点分 6760
Reading Access 102
Registered 2004-3-28
Status Offline
Post at 2007-7-10 23:38  Profile | Blog | P.M. 
CnPack IV 群里讨论过这个问题,其中水逸寒风给出了部分讨论结果:

水逸寒风 17:29:42
我记得以前看过文章
C++中的构造函数
按照C++标准
在构造函数被调用时,对象根本就没有被建构起来,所以你无法知道这个对象是什么类型

c++中  你如果在构造中出现了问题  有异常 必须自己处理   系统不会析构
而  pascal中 应该就是可以的
所以 出现问题的时候  自己必须抛出异常 自己再处理
Top
shenloqi
灌水处处长
Rank: 4



UID 34
Digest Posts 1
Credits 287
Posts 179
点点分 287
Reading Access 10
Registered 2003-3-15
Status Offline
Post at 2007-7-11 09:50  Profile | P.M. 
这个答案是大部分说的答案,但是我觉得这个答案已经不足以解释了,因为现在C++构造函数在调用的最初就已经设定了VPTR了,所以如果要实现Delphi这样的也是可以的,所以C++在构造函数中也是可以调用虚函数的,只是这个虚函数没有延后性而已。
Top
kendling (小冬)
高级版主
Rank: 8Rank: 8
MyvNet


Medal No.1  
UID 703
Digest Posts 5
Credits 978
Posts 580
点点分 978
Reading Access 101
Registered 2005-2-18
Location 广东
Status Offline
Post at 2007-7-11 15:48  Profile | Site | Blog | P.M.  | QQ | Yahoo!
以C++在构造函数中好像可以调用this指针,这又怎么解释?




小冬
http://MyvNet.com
Top
Passion (LiuXiao)
管理员
Rank: 9Rank: 9Rank: 9


UID 359
Digest Posts 19
Credits 6760
Posts 3556
点点分 6760
Reading Access 102
Registered 2004-3-28
Status Offline
Post at 2007-7-11 16:19  Profile | Blog | P.M. 
我的一点点想法是,实际上,大部分情形下,Delphi和C++的类在构建的时候,类都具体指定了的,所以构造函数虚不虚没啥影响。Delphi中某类Txxxx.Create的时候,这个Create调用的就是这个类Txxxx的constructor,是确定了的。

但Delphi还有另外一种类引用的情形,比如TApplication的CreateForm,它用类引用来创建组件,先NewInstance后,再调用实例名.Create;这种情况下Create是可以虚,并且对于component的子类来说是必须虚的。

但这种情况下并不足以说明Delphi和C++的创建过程就有根本不同。实际上用类引用来创建时,真正的创建过程是NewInstance和InitInstance等,Create这个constructor是创建后的一个紧接着调用的方法,地位已相当于普通方法了,因此可以虚。而C++里头貌似没有将这种NewInstance/InitInstance和构造函数实体区分出来的机制,因此干脆规定不能虚。
Delphi的某类Txxxx.Create的时候,从代码上来看,也没法将NewInstance/InitInstance和constructor的实体区分,因此这种情况下也不能说constructor算虚,即使它定义为virtual。

这里关键在于区分两部分概念:真正创建对象的过程是不虚的,而构造函数中对象创建后初始化的部分是可以虚的(如果代码形式上允许它被独立调用的话),C++里头构造函数里能使用this就是这个原因,它是初始化后调用的。
Top
shenloqi
灌水处处长
Rank: 4



UID 34
Digest Posts 1
Credits 287
Posts 179
点点分 287
Reading Access 10
Registered 2003-3-15
Status Offline
Post at 2007-7-12 09:59  Profile | P.M. 
kendling, this就是我所说的vptr,因为vptr是在编译器生成构造函数的代码的最初就赋值了的,所以自然可以使用this。

passion, Delphi就算是使用TApplication.CreateForm,也需要先用NewInstance构造实例,然后使用实例的Create来初始化,我似乎以前讨论过的,对象的Create和类的Create会有不同的寄存器值,相应的Delphi会给予他们有不同的表现。而且NewInstance而且还是需要传入一个Class类型,只不过Delphi有个特色,就是Class类型,使得这种方式有了一定的虚的能力。
Top
Passion (LiuXiao)
管理员
Rank: 9Rank: 9Rank: 9


UID 359
Digest Posts 19
Credits 6760
Posts 3556
点点分 6760
Reading Access 102
Registered 2004-3-28
Status Offline
Post at 2007-7-12 13:57  Profile | Blog | P.M. 
对。调Constructor时,DL寄存器会传个1还是0,表示是对象创建时第一回调用还是被inherited或其他的方式进行的普通调用,前者传的是类,后者传的是对象。

那么,可不可以就这么说:因为Delphi的类引用机制的在才允许constructor带上一定的虚机制?
Top
zzzl (早安的空气)
版主
Rank: 7Rank: 7Rank: 7



UID 590
Digest Posts 0
Credits 399
Posts 199
点点分 399
Reading Access 100
Registered 2004-11-29
Status Offline
Post at 2007-7-12 20:55  Profile | Blog | P.M.  | QQ
两个理由:如果是virtual的可能会调用未初始化的成员。另一个太麻烦不讲了。

PS:怀疑楼主在制造话题
Top
Passion (LiuXiao)
管理员
Rank: 9Rank: 9Rank: 9


UID 359
Digest Posts 19
Credits 6760
Posts 3556
点点分 6760
Reading Access 102
Registered 2004-3-28
Status Offline
Post at 2007-7-12 21:20  Profile | Blog | P.M. 
不瞒楼上说,这是我上周在笔试的时候碰到的一个题目,虽然知道答案但没当时没想明白理由。

还有一个理由是啥?约略说说。
Top
kendling (小冬)
高级版主
Rank: 8Rank: 8
MyvNet


Medal No.1  
UID 703
Digest Posts 5
Credits 978
Posts 580
点点分 978
Reading Access 101
Registered 2005-2-18
Location 广东
Status Offline
Post at 2007-7-13 08:52  Profile | Site | Blog | P.M.  | QQ | Yahoo!
这个还是值得讨论一下的。




小冬
http://MyvNet.com
Top
 




All times are GMT++8, the time now is 2024-5-5 15:33

    本论坛支付平台由支付宝提供
携手打造安全诚信的交易社区 Powered by Discuz! 5.0.0  © 2001-2006 Comsenz Inc.
Processed in 0.012575 second(s), 8 queries , Gzip enabled

Clear Cookies - Contact Us - CnPack Website - Archiver - WAP