设为首页收藏本站

新微赢技术网

 找回密码
 注册
搜索
热搜: 回贴
查看: 788|回复: 8
打印 上一主题 下一主题

[求助]虚继承的问题

[复制链接]
跳转到指定楼层
1#
发表于 2009-11-6 01:54:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
程序代码:

class Usable;
class Usable_lock
{ //这家伙把这个类的构造函数声明为私有并把Usable类声明为他的友元
friend class Usable;
private:
Usable_lock() {}
Usable_lock(const Usable_lock&) {}
};

class Usable : public virtual Usable_lock //虚继承Usable_lock类
{ // ...
public:
Usable();
Usable(char*); // ...
};

class DD : public Usable
{ };
int main()
{
Usable a;
DD dd; //错误!因为 DD 不能访问 Usable_lock 的构造函数,也就创建不了 dd,防止了Usable被继承
return 0;
}
//这里的如果Usable不是虚继承基类的话DD dd;就不会错,虚继承决定了什么?!
2#
发表于 2009-11-6 01:54:29 | 只看该作者
现在才看到你的问题,帮你看了下。
你说 “这里的如果Usable不是虚继承基类的话DD dd;就不会错,虚继承决定了什么?!”
你把
class Usable : public /*virtual*/ Usable_lock //虚继承Usable_lock类
{ // ...
public:
Usable(){} //这里改下,加上定义
Usable(char*); // ...
};
这样,代码就可以通过,就没有达到防止Usable 被继承的作用。
////////////////////////////////////////////////////////////////////////////////
先说说上面的代码,包括继承原理:
首先,把我们不想被继承的类作为一个派生类去继承一个没哟意义的Usable_lock类,注意,这个类显示的把构造函数
声明为私有,然后把Usable作为友员类,这就是关键。
再来,继承中派生类创建的过程:首先要调用父类的构造函数,即先建立一个父类的对象。
这里Usable类是公有继承,所以是访问不到私有的构造函数,但是作为友员是可以的,所以消除了这个限制。由此,我们的Usable类是可以被创建的。

到这里,看看为什么普通的非虚拟继承,能使dd的创建合法。
首先,按照我们先前说讲,DD类公有继承Usable类,创建dd对象的过程是先调用父类构造函数(Usable类构造函数),这一步合法,因为可以访问到Usable的构造函数。这时就要创建一个Usable对象,要创建Usable 对象,就要调用Usable 的父类构造函数,就来到Usable_lock类,开始我们说了,由Usable访问Usable_lock是没有问题的。所以这样一来,dd对象就被正确的创建了。他间接的访问过Usable_lock。

////////////////////////////////////////////////////////////////////////
下面说说BJ在这个例子中是怎样通过虚拟技术防止Usable类被继承的:
虚拟继承,通过在声明派生类继承父类时加上virtual来指定。
虚拟继承的作用:当系统碰到多重继承的时候保证继承类成员函数的唯一性。
怎么理解他的作用,看下面这个图1:
class base //基类
class derive1 : public base
class derive2 : public base

class derive3 : public derive1,public derive2

这种情况下,derive3就同时拥有了两套base的函数。所以在调用时产生的模糊性会使程序出错。具体我就不谈那么多了。

解决这个问题,就用到了虚拟技术。
修改上面的代码 图2:
class base //基类
class derive1 : virtual public base
class derive2 : virtual public base
class derive3 : public derive1,public derive2

采用虚拟继承的时候,在derive3中,一旦一次继承了base类,再次继承base的时候就会忽略重复的base类。所以保证继承类成员函数的唯一性。

///////////////////////////////////////////////////////////////////
在这个问题中,BJ大师用到的不是这个原理,而是由此产生的一个构造函数调用顺序问题。
在非虚拟继承中,比如图1中。在构造derive3时,其父类调用顺序为derive1-derive2-base;
然后在虚拟继承中。比如图2中。在构造derive3时,其父类调用顺序为base-derive1-derive2;

同样,这样的顺序,作用显而易见,是防止base被多次继承。

再回到我们最初的话题,在DD类创建对象dd时,最先被调用的是Usable_lock类的构造函数,但是,由于Usable_lock构造函数声明被私有,就出现不能访问的错误。由此,是不能创建DD对象的。用这个方法,完成了防止了Usable被继承。



//////////////////////////////////////////////////////////////////////
仓促中作了些对继承和虚拟继承的分析。不对的地方和需要完善的地方请指出。
很欣赏楼主这种思考精神。
回复 支持 反对

使用道具 举报

3#
发表于 2009-11-6 01:54:30 | 只看该作者
//这里的如果Usable不是虚继承基类的话DD dd;就不会错,虚继承决定了什么?!


就是改变了构造函数调用的顺序???
回复 支持 反对

使用道具 举报

4#
发表于 2009-11-6 01:54:31 | 只看该作者
不错, 分析很到位, 学习了
回复 支持 反对

使用道具 举报

5#
发表于 2009-11-6 01:54:32 | 只看该作者
这种情况下,derive3就同时拥有了两套base的函数。所以在调用时产生的模糊性会使程序出错。具体我就不谈那么多了。

也不用谈了,菱形的二义性,基本了解
谢谢斑竹的分析
补足了我继承与派生方面的知识,其实我记得教材有的,但是怎么翻也翻不到,百度出来也是一堆乱七八糟
回复 支持 反对

使用道具 举报

6#
发表于 2009-11-6 01:54:35 | 只看该作者
恩,分析很透彻,
回复 支持 反对

使用道具 举报

7#
发表于 2009-11-6 01:54:36 | 只看该作者
在存在虚拟继承的继承结构层次中:子类的构造函数必须在初始化列表中将他所有的直接基类以及非直接虚基类都进行构造(如果是默认构造可以忽略)。
如下:

程序代码:
#include <iostream>
using namespace std;
class A
{
public:
A(int a){cout<<"A::Constructor"<<endl;}
};
class B:public virtual A
{
public:
B(int b):A(b){cout<<"B::Constructor"<<endl;}
};
class C:public B
{
public:
C(int c):A(c),B(c){cout<<"C::Constructor"<<endl;}//很显然,A(c)要在初始化列表中写出来
//因此就是C(int)调用A(int),所以在LZ的代码中失败!
};
void main()
{
C c(0);
}
回复 支持 反对

使用道具 举报

8#
发表于 2009-11-6 01:54:37 | 只看该作者
以下是引用wfpb在2007-5-31 22:04:45的发言:

在存在虚拟继承的继承结构层次中:子类的构造函数必须在初始化列表中将他所有的直接基类以及非直接虚基类都进行构造(如果是默认构造可以忽略)。
如下:

程序代码:
#include <iostream>
using namespace std;
class A
{
public:
A(int a){cout<<"A::Constructor"<<endl;}
};
class B:public virtual A
{
public:
B(int b):A(b){cout<<"B::Constructor"<<endl;}
};
class C:public B
{
public:
C(int c):A(c),B(c){cout<<"C::Constructor"<<endl;}//很显然,A(c)要在初始化列表中写出来
//因此就是C(int)调用A(int),所以在LZ的代码中失败!
};
void main()
{
C c(0);
}




我的例子是BJ大叔防止自己的类被继承的本意就是要DD dd不成功,我只是问虚继承的问题
回复 支持 反对

使用道具 举报

9#
发表于 2009-11-6 01:54:38 | 只看该作者
是啊,我是说明为什么会调用失败的原因,因为子类要在初始化列表中构造他所有的直接基类以及非直接虚基类。
而Usable_lock()这个非直接虚拟基类的构造函数是无参构造函数,可以省略,所以在class DD中就没有显示写出,但是其实是存在的,也即是说DD类的构造函数是这样的:
DD:Usable_lock(),Usable(){}
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

申请友链|小黑屋|最新主题|手机版|新微赢技术网 ( 苏ICP备08020429号 )  

GMT+8, 2024-11-19 22:41 , Processed in 0.087303 second(s), 9 queries , Gzip On, Memcache On.

Powered by xuexi

© 2001-2013 HaiAn.Com.Cn Inc. 寰耽

快速回复 返回顶部 返回列表