| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 2501 人关注过本帖
标题:[求助]虚继承的问题
只看楼主 加入收藏
zkkpkk
Rank: 2
等 级:论坛游民
威 望:5
帖 子:489
专家分:28
注 册:2006-6-17
收藏
 问题点数:0 回复次数:21 
[求助]虚继承的问题

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;就不会错,虚继承决定了什么?!
搜索更多相关主题的帖子: Usable lock class 继承 public 
2007-05-31 09:27
aipb2007
Rank: 8Rank: 8
来 自:CQU
等 级:贵宾
威 望:40
帖 子:2879
专家分:7
注 册:2007-3-18
收藏
得分:0 

现在才看到你的问题,帮你看了下。
你说 “这里的如果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被继承。



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


Fight  to win  or  die...
2007-05-31 11:59
天下第二刀
Rank: 3Rank: 3
等 级:新手上路
威 望:8
帖 子:157
专家分:0
注 册:2007-1-8
收藏
得分:0 
//这里的如果Usable不是虚继承基类的话DD dd;就不会错,虚继承决定了什么?!


就是改变了构造函数调用的顺序???

不知天堂有没有后门~~~
2007-05-31 13:55
天下第二刀
Rank: 3Rank: 3
等 级:新手上路
威 望:8
帖 子:157
专家分:0
注 册:2007-1-8
收藏
得分:0 
不错, 分析很到位, 学习了

不知天堂有没有后门~~~
2007-05-31 14:09
zkkpkk
Rank: 2
等 级:论坛游民
威 望:5
帖 子:489
专家分:28
注 册:2006-6-17
收藏
得分:0 
这种情况下,derive3就同时拥有了两套base的函数。所以在调用时产生的模糊性会使程序出错。具体我就不谈那么多了。

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

[此贴子已经被作者于2007-5-31 15:50:48编辑过]


Viva,espana!
2007-05-31 15:43
raulxxyuer
Rank: 1
等 级:新手上路
威 望:1
帖 子:178
专家分:0
注 册:2007-4-23
收藏
得分:0 

我很沒用,總是學不會遺忘,總是學不會割捨本不屬於我的東西。
2007-05-31 15:49
yuyunliuhen
Rank: 6Rank: 6
等 级:贵宾
威 望:20
帖 子:1435
专家分:0
注 册:2005-12-12
收藏
得分:0 

恩,分析很透彻,


Go confidently in the  directions of your dreams,live the life you have imagined!Just do it!
It is no use learning without thinking!
2007-05-31 21:06
wfpb
Rank: 6Rank: 6
等 级:贵宾
威 望:29
帖 子:2188
专家分:0
注 册:2006-4-2
收藏
得分:0 

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

#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);
}


[此贴子已经被作者于2007-5-31 22:05:49编辑过]


[glow=255,red,2]wfpb的部落格[/glow] 学习成为生活的重要组成部分!
2007-05-31 22:04
zkkpkk
Rank: 2
等 级:论坛游民
威 望:5
帖 子:489
专家分:28
注 册:2006-6-17
收藏
得分:0 
以下是引用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不成功,我只是问虚继承的问题


Viva,espana!
2007-05-31 22:31
wfpb
Rank: 6Rank: 6
等 级:贵宾
威 望:29
帖 子:2188
专家分:0
注 册:2006-4-2
收藏
得分:0 
是啊,我是说明为什么会调用失败的原因,因为子类要在初始化列表中构造他所有的直接基类以及非直接虚基类。
而Usable_lock()这个非直接虚拟基类的构造函数是无参构造函数,可以省略,所以在class DD中就没有显示写出,但是其实是存在的,也即是说DD类的构造函数是这样的:
DD:Usable_lock(),Usable(){}

[glow=255,red,2]wfpb的部落格[/glow] 学习成为生活的重要组成部分!
2007-06-02 01:35
快速回复:[求助]虚继承的问题
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.029113 second(s), 7 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved