找回密码
 注册
搜索
热搜: 回贴
  • 前程无忧官网首页 有什么好的平台可以
  • 最新的销售平台 互联网营销的平台有哪
  • 制作网页的基本流程 网页制作和网页设
  • 【帝国CMS】输出带序号的列表(数字排
  • 网站建设公司 三一,中联,极东泵车的
  • 织梦 建站 织梦网站模版后台怎么更改
  • 云服务官网 哪些网站有免费的简历模板
  • 如何建网站要什么条件 建网站要用什么
  • 吉林市移动公司电话 吉林省退休人员网
  • 设计类毕业论文 网站设计与实现毕业论
查看: 787|回复: 8

[求助]虚继承的问题

[复制链接]
发表于 2009-11-6 01:54:28 | 显示全部楼层 |阅读模式 IP:江苏扬州
程序代码:

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;就不会错,虚继承决定了什么?!
发表于 2009-11-6 01:54:29 | 显示全部楼层 IP:江苏扬州
现在才看到你的问题,帮你看了下。
你说 “这里的如果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被继承。



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

使用道具 举报

发表于 2009-11-6 01:54:30 | 显示全部楼层 IP:江苏扬州
//这里的如果Usable不是虚继承基类的话DD dd;就不会错,虚继承决定了什么?!


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

使用道具 举报

发表于 2009-11-6 01:54:31 | 显示全部楼层 IP:江苏扬州
不错, 分析很到位, 学习了
回复

使用道具 举报

发表于 2009-11-6 01:54:32 | 显示全部楼层 IP:江苏扬州
这种情况下,derive3就同时拥有了两套base的函数。所以在调用时产生的模糊性会使程序出错。具体我就不谈那么多了。

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

使用道具 举报

发表于 2009-11-6 01:54:35 | 显示全部楼层 IP:江苏扬州
恩,分析很透彻,
回复

使用道具 举报

发表于 2009-11-6 01:54:36 | 显示全部楼层 IP:江苏扬州
在存在虚拟继承的继承结构层次中:子类的构造函数必须在初始化列表中将他所有的直接基类以及非直接虚基类都进行构造(如果是默认构造可以忽略)。
如下:

程序代码:
#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);
}
回复

使用道具 举报

发表于 2009-11-6 01:54:37 | 显示全部楼层 IP:江苏扬州
以下是引用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不成功,我只是问虚继承的问题
回复

使用道具 举报

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

使用道具 举报

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

本版积分规则

QQ|小黑屋|最新主题|手机版|微赢网络技术论坛 ( 苏ICP备08020429号 )

GMT+8, 2024-10-1 19:27 , Processed in 0.278226 second(s), 13 queries , Gzip On, MemCache On.

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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