您的位置:程序门 -> c/c++ -> c++ 语言



终于能在这发贴了,再来一个  关于用基类指针指向子类对象的问题


[收藏此页] [打印本页]选择字色:背景色:字体:[][][]


终于能在这发贴了,再来一个 关于用基类指针指向子类对象的问题[已结贴,结贴人:red_berries]
发表于:2007-05-30 09:42:16 楼主
这是我以下的测试程序
class   a
{
public:
a()
{
cout < < "aaaa出生了 " < <endl;
}
        virtual   ~a()
{
cout < < "aaaa死了 " < <endl;
}
};
class   b:public   a
{
public:
b()
{
cout < < "bbbb出生了 " < <endl;
}
~b()
{
cout < < "bbbb死了 " < <endl;
}
};
int   main()
{
b   b;
a   *a;
a=&b;
delete   a;
return   0;
}
问题如下
class   b:public   a   如将public   换成   private,编绎通不过,提示 "error   c2243:   'type   cast '   :   conversion   from   'class   b   * '   to   'class   a   * '   exists,   but   is   inaccessible "     不明白.用基类指针指向派生类对象难道非要公有派生吗?
发表于:2007-05-30 09:48:061楼 得分:5
是的。
发表于:2007-05-30 09:49:082楼 得分:0
mark之
发表于:2007-05-30 09:49:133楼 得分:0
不能
发表于:2007-05-30 09:49:524楼 得分:0
taodm这个出于什么理由,设计所用原则,等详解okokok
发表于:2007-05-30 09:51:075楼 得分:0
//.使用private继承后,无法在类作用域外直接使用多态,  
//     派生类指针无法自动转型为基类指针
发表于:2007-05-30 09:55:116楼 得分:0
private就这个含义。
为什么要private继承,看effective   c++   2e   item   35/36
发表于:2007-05-30 09:57:577楼 得分:0
私有继承后,   无法在派生类作用域外进行多态,   派生类指针无法自动转换为基类指针(但可强制类型转换).
发表于:2007-05-30 10:02:298楼 得分:5
转:

class   person   <   ...   > ;  

class   student:   private   person   <   ...   > ;   //   inheritance   is   now   private  

void   eat(const   person&   p);   //   anyone   can   eat  

void   study(const   student&   s);   //   only   students   study  

person   p;   //   p   is   a   person  

student   s;   //   s   is   a   student  

eat(p);   //   fine,   p   is   a   person  

eat(s);   //   error!   a   student   isnt   a   person  

  很明显,private   inheritance(私有继承)不意味着   is-a。那么它意味着什么呢?  

  “喂!”你说:“在我们得到它的含义之前,我们先看看它的行为。private   inheritance(私有继承)有怎样的行为呢?”好吧,支配   private   inheritance(私有继承)的第一个规则你只能从动作中看到:与   public   inheritance(公有继承)对照,如果   classes(类)之间的   inheritance   relationship(继承关系)是   private(私有)的,编译器通常不会将一个   derived   class   object(派生类对象)(诸如   student)转型为一个   base   class   object(基类对象)(诸如   person)。这就是为什么为   object(对象)s   调用   eat   会失败。第二个规则是从一个   private   base   class(私有基类)继承的   members(成员)会成为   derived   class(派生类)的   private   members(私有成员),即使它们在   base   class(基类)中是   protected(保护)的或   public(公有)的。  

  行为不过如此。这就给我们带来了含义。private   inheritance(私有继承)意味着   is-implemented-in-terms-of(是根据……实现的)。如果你使   class(类)d   从   class(类)b   私有继承,你这样做是因为你对于利用在   class(类)b   中才可用的某些特性感兴趣,而不是因为在   types(类型)b   和   types(类型)d   的   objects(对象)之间有什么概念上的关系。同样地,private   inheritance(私有继承)纯粹是一种实现技术。(这也就是为什么你从一个   private   base   class(私有基类)继承的每一件东西都在你的   class(类)中变成   private(私有)的原因:它全部都是实现的细节。)利用《接口继承和实现继承》中提出的条款,private   inheritance(私有继承)意味着只有   implementation(实现)应该被继承;interface(接口)应该被忽略。  

  如果   d   从   b   私有继承,它就意味着   d   objects   are   implemented   in   terms   of   b   objects(d   对象是根据   b   对象实现的),没有更多了。private   inheritance(私有继承)在   software   design(软件设计)期间没有任何意义,只在   software   implementation(软件实现)期间才有。   private   inheritance(私有继承)意味着   is-implemented-in-terms-of(是根据……实现的)的事实有一点混乱,正如《通过composition模拟“has-a”》一文中所指出的   composition(复合)也有同样的含义。你怎么预先在它们之间做出选择呢?答案很简单:只要你能就用   composition(复合),只有在绝对必要的时候才用   private   inheritance(私有继承)。什么时候是绝对必要呢?主要是当   protected   members(保护成员)和/或   virtual   functions(虚拟函数)掺和进来的时候,另外还有一种与空间相关的极端情况会使天平向   private   inheritance(私有继承)倾斜。我们稍后再来操心这种极端情况。  

  毕竟,它只是一种极端情况。   假设我们工作在一个包含   widgets   的应用程序上,而且我们认为我们需要更好地理解   widgets   是怎样被使用的。例如,我们不仅要知道   widget   member   functions(成员函数)被调用的频度,还要知道   call   ratios(调用率)随着时间的流逝如何变化。带有清晰的执行阶段的程序在不同的执行阶段可以有不同的行为侧重。例如,一个编译器在解析阶段对函数的使用与优化和代码生成阶段就有很大的不同。  

  我们决定修改   widget   class   以持续跟踪每一个   member   function(成员函数)被调用了多少次。在运行时,我们可以周期性地检查这一信息,与每一个   widget   的这个值相伴的可能还有我们觉得有用的其它数据。为了进行这项工作,我们需要设立某种类型的   timer(计时器),以便在到达收集用法统计的时间时我们可以知道。  

  尽可能复用已有代码,而不是写新的代码,我在我的工具包中翻箱倒柜,而且满意地找到下面这个   class(类):  

class   timer   <  

public:  

explicit   timer(int   tickfrequency);  

virtual   void   ontick()   const;   //   automatically   called   for   each   tick  

...  

> ;  

  这正是我们要找的:一个我们能够根据我们的需要设定   tick   频率的   timer   object,而在每次   tick   时,它调用一个   virtual   function(虚拟函数)。我们可以重定义这个   virtual   function(虚拟函数)以便让它检查   widget   所在的当前状态。很完美!  

为了给   widget   重定义   timer   中的一个   virtual   function(虚拟函数),widget   必须从   timer   继承。但是   public   inheritance(公有继承)在这种情况下不合适。widget   is-a(是一个)timer   不成立。widget   的客户不应该能够在一个   widget   上调用   ontick,因为在概念上那不是的   widget   的   interface(接口)的一部分。允许这样的函数调用将使客户更容易误用   widget   的   interface(接口),这是一个对《使接口易于正确使用难错误使用》中的关于“使接口易于正确使用,而难以错误使用”的建议的明显违背。public   inheritance(公有继承)在这里不是正确的选项。  

  因此我们就   inherit   privately(秘密地继承):  

class   widget:   private   timer   <  

private:  

virtual   void   ontick()   const;   //   look   at   widget   usage   data,   etc.  

...  

> ;  

  通过   private   inheritance(私有继承)的能力,timer   的   public(公有)ontick   函数在   widget   中变成   private(私有)的,而且在我们重新声明它的时候,也把它保留在那里。重复一次,将   ontick   放入   public   interface(公有接口)将误导客户认为他们可以调用它,而这违背了我在《使接口易于正确使用难错误使用》。  

  这是一个很好的设计,但值得注意的是,private   inheritance(私有继承)并不是绝对必要的。如果我们决定用   composition(复合)来代替,也是可以的。我们仅需要在我们从   timer   公有继承来的   widget   内声明一个   private   nested   class(私有嵌套类),在那里重定义   ontick,并在   widget   中放置一个那个类型的   object(对象)。以下就是这个方法的概要:  

class   widget   <  

 private:  

  class   widgettimer:   public   timer   <  

  public:  

   virtual   void   ontick()   const;  

   ...  

  > ;  

  widgettimer   timer;  

 ...  

> ;  

  这个设计比只用了   private   inheritance(私有继承)的那一个更复杂,因为它包括   (public)   inheritance((公有)继承)和   composition(复合)两者,以及一个新   class   (widgettimer)   的引入。老实说,我出示它主要是为了提醒你有多于一条的道路通向一个设计问题,而且它也可以锻炼你自己你自己考虑多种方法(参见《c++箴言:最小化文件之间的编译依赖》)。然而,我可以想到为什么你可能更愿意用   public   inheritance(公有继承)加   composition(复合)而不用   private   inheritance(私有继承)的两个原因。  

  首先,你可能要做出允许   widget   有   derived   classes(派生类)的设计,但是你还可能要禁止   derived   classes(派生类)重定义   ontick。如果   widget   从   timer   继承,那是不可能的,即使   inheritance(继承)是   private(私有)的也不行。(回忆《c++箴言:考虑可选的虚拟函数的替代方法》derived   classes(派生类)可以重定义   virtual   functions(虚拟函数),即使调用它们是不被允许的。)但是如果   widgettimer   在   widget   中是   private(私有)的而且是从   timer   继承的,widget   的   derived   classes(派生类)就不能访问   widgettimer,因此就不能从它继承或重定义它的   virtual   functions(虚拟函数)。如果你曾在   java   或   c#   中编程并且错过了禁止   derived   classes(派生类)重定义   virtual   functions(虚拟函数)的能力(也就是,java   的   final   methods(方法)和   c#   的   sealed),现在你有了一个在   c++   中的到类似行为的想法。
发表于:2007-05-30 10:26:419楼 得分:0
private继承问题
发表于:2007-05-30 13:39:5010楼 得分:0
非常感谢星羽的回答,让我想通了不少东西,根据你的说法,我觉得好像应该这么理解:既然是private继承就说明子类对基类的具体实现不是很关心,也可能是为了保护基类的成员.
        想到这我想起了我以前问的一个类成员函数调用机制问题中星晨说提到的类的私有性检察是在编绎时进行的,而不会在运行时进行.从而当用一个基类指针来调用基类的成员函数时因为在编绎时还不知道这个基类指针到底指向的是基类对象还是子类对象,也就无法判断是否应该让其访问,所以不能让其通过编绎,如果能通过编绎,结果基类指向的是一个子类的对象(欺骗了编绎器),就可以访问了类的公有成员了,这样就失去了private的意义了.
          纯属胡想,希望大家指证.呵呵,
发表于:2007-05-30 14:01:0611楼 得分:5
恩,星晨   说的是对的

写个程序就可以测试了

#include   "stdafx.h "
#include   "stdlib.h "
#include   "iostream "

using   namespace   std;


class   base  
{
public   :
        virtual   void   show()   {   cout < < "i   am   public   show   :) " < <endl;   }

private   :
        virtual   void   show2()   {   cout < < "i   am   private   show2:) " < <endl;   }

};

class   devic   :   public   base
{
private   :
        virtual   void   show()   {   cout < < "i   am   private   show   :) " < <endl;   }

public   :
        virtual   void   show2()   {   cout < < "i   am   public   show2:) " < <endl;   }
};

int   main()
{
        devic   d;

        //   d.show();     //   编译不过,访问了private

        base*   p   =   &d;

        p-> show();         //   ok

       
        //p-> show2();   //   编译不过,访问了private    

        d.show2();         //   ok


        system( "pause ");

        return   1;
}
发表于:2007-05-30 14:03:1612楼 得分:0
所以可以看出,

保护属性public   private。。。

是按你调用的类型来决定的,不管这个函数实际被调用的时候是那种类型
发表于:2007-05-30 14:08:1013楼 得分:0
父类public的方法在以private方法继承的子类中为private,用子类的指针是访问不到的,如果允许你转换的话,你就间接的可以使用父类的指针去访问这个 "public "的方法了,这当然是不和逻辑的...........
发表于:2007-05-30 14:08:5214楼 得分:5
应该是这样的,虚函数表并不保存每个函数的保护属性(public,private,...)

这个属性是有类来维护的

比如上面的程序

show   这个函数在base类里是public的

show   这个函数在devic里是private的

当把device指针赋给base的时候

base里的show函数指向虚函表里的show,也就是devic的show函数

但你通过base去调用show的时候,编译器在base这个类里查到show的属性是public

所以可以编译通过并顺利调用

发表于:2007-05-30 14:09:3815楼 得分:0
首先,想实现虚函数功能必须公有派生,这才能体现“is-a“,用private派生只是表达

“用。。。来实现”,这时,基类指针将无法指向子类对象。


另外,我在楼主的程序里并没看到private字样,

你的问题是在

int   main()
{
b   b;
a   *a;
a=&b;
delete   a;
return   0;
}

这里的a   *a指向的b不是new出来的,怎么能去delete   a呢?

要改成a   *a=new   b;
发表于:2007-05-30 14:10:3116楼 得分:0
或者是你的程序写错了?应该是class   b   :   private   a       ?
发表于:2007-05-30 16:55:0217楼 得分:0
程序是那样,不过我的问题是如果将public换成private............
你指出的我程序中的这个问题
//////////////////////////////////////////////////////
这里的a   *a指向的b不是new出来的,怎么能去delete   a呢?

要改成a   *a=new   b;
/////////////////////////////////////////////////////
确实是个问题,程序执行结束会发生异常,我本来还以为是类的问题呢,发帖子时想到是我delete错了,也就没有再问.

发表于:2007-05-30 17:11:0018楼 得分:0
按chiyer(星羽)的分析,可以使用指向自己的基类指针来访问自己的私有虚函数了...长了点见识
发表于:2007-05-31 11:38:4019楼 得分:0
public   stands   for   "is-a "
发表于:2007-05-31 11:44:3820楼 得分:0
"is-a "   是一部分的意思??还有那个 "is-什么 "的,老见人提到,不知道什么意思哈,指点下
发表于:2007-05-31 12:55:0221楼 得分:0
chiyer(星羽)解释的相当深刻,茅塞顿开啊!
发表于:2007-05-31 17:34:1322楼 得分:0
程序是那样,不过我的问题是如果将public换成private............
你指出的我程序中的这个问题
真正编程中private继承很少用,这样还不如用组合呢!
如果是public/protected     我们就可以说   子类就是基类     这么说不知道楼主能不能理解
但是你要是private就不能那么说       就是因为不能那么说才会报错
也就是说   public/protected   时基类指针可以指向子类对象     private时就不可以!  
有什么用意去私有继承呢,几乎只有在书本上能看到;
都是public   或者virtual继承,其实上你强行转换得话还是可以运行正常的,所以子类就是父类,依然正确;


快速检索

最新资讯
热门点击