您的位置:首页 > 其它

虚继承及无关

2013-11-06 17:21 106 查看
虚继承
class   A

{

public:

int a

}

class   B:public virtual A

{

public:

Int  b;

}

class   C:public virtual A

{

public:

Int  c;

}

class   D:public B,public C

{

public:

int  d;

}

B C 虚继承A,A是B C的虚基类。

三篇有用的文章:

/article/9274816.html

http://bbs.csdn.net/topics/390251988

http://blog.sina.com.cn/s/blog_93b45b0f01011pkz.html

虚继承的特点是,继承B,C的类不会从B,C直接继承A的成员,它只会直接从A中继承。所以不会造成D中有两份A成员的拷贝。基于这一点,D中必须显示调用A中的带参构造函数(如果有的话,且其基类部分维护D中继承自A的成员的指针)

对于A ca;

Sizeof(A)= 4,其成员为int a;

对于B cb;

Sizeof(B)= 12,其成员有:

Vb_ptr

B中的指针

Int b

B自己的成员

Int a

继承自A的成员

假设B的首地址为p;

Char* p = (char*)&cb;

Int offset = *Vb_ptr;

Int* data = (int*)(p+offset);

那么data指向a;

对于C cc;同上

对D cd;

vb_ptr: 继承自B的指针

int b: 继承自B公有成员

vc_ptr:继承自C的指针

int c: 继承自C的共有成员

int d: D自己的公有成员

int a: 继承自A的公有成员

按照普通继承D的结构为:

B

继承自A的 a

B 自己的b

C

继承自A的a

C自己的c

D

D自己的d

对于这个结构的解释是,D由三部分部组成B,C,D(myself),但是并不存在单独的B,C的对象。

只是D可以当作B,C来用(来解析),此例中由于有两个a所以会有二义性。且D中不必显示调用A的构造函数。

虚基类,虚继承就是为了解决这个二义性问题,方法就是把D的结构改造一下:

B

B的指针

B自己的b

C

C 的指针

C自己的c

D

D自己的d

A

继承自A的a

这样直接从虚基类继承a,而绕过B,C-----只有直接实例化的类才会真正调用虚基类的构造函数,其他中间类对A构造函数的调用都将被抑制。

之所以设置指针这个参数,只是为了让D可以当B,C使用。

也就是:

D d;

B *pb = &d

Pb->a;

实际是把图中B那块首地址给pb,调用a,其实是通过B的指针间接实现。这样我们既实现了D中只有一份a的拷贝,同时又可以将D作为B,C来使用,满足里氏替换原则(C++中子类都应当可以作为父类使用,而没有任何差异被感知)。

虚基类中将派生类的指针指向基类是不支持的,同时也是不提倡的做法,即使是该基类指针指向的实际是一个派生类对象也不可,不同编译器实现的方式不同,尽管有时先将基类指针转换成void*然后再赋值给派生类可以骗过编译器,但是有时也会出现运行时错误。还有一种方式,就是虚基类中有虚函数,那么就可以dynamic_cast<>在运行期强制转换。不过同样危险,总之要特别小心。目前认识非常初步,待完善。<C++ 对象模型>讲解较为详尽。

cin,cin.get(),cin.getline()那点小事:

(1) cin:读取以空白符结束。

(2) cin.get():读取一个字符,包括空白。

(3) cin.getline():读取一行,包括换行符(及其它空白)。

所以cin读取完事之后,缓冲区stdin还存留着一个换行符‘\n‘,而其他两个就没有。

如:

char ch;

cin>>ch;

cout<<ch<<endl;

ch = cin.get();

if(ch ==’\n‘)cout<<“ch is \\n.“<<endl;

输入:1

输出:

1

ch is \n.

这个输出有点奇怪了,我只输入了1啊,为什么会有后续输出呢,这就是我们要注意的了,其实我们不只是输入了1,那个cin的结束符enter键同样输入到了stdin缓冲区中。它不会被cin读取,而cin.get()则会读取它。这也是我们需要小心的地方,解决办法是,在使用两种同类型读取方式时,先用fflush(stdin)/cin.sync()来清除缓冲区。使得缓冲区为空,这样cin.get()才不会自动执行,才会等待用户输入。

然而在g++下这种方法确不奏效,这是因为fflush/sync函数不是标准库所有的。可移植性不强。所以我们可以取而代之为cin.ignore();

文件的几种状态:cin.rdstate()

(1)goodbit:无错误 如if(cin.rdstate() ==ios_base::goodbit)

(2)badbit:有致命错误,无法挽回。

(3)eofbit:文件末尾。

(4)failbit:输入错误,可以挽回。

几个函数:

1. cin.clear():恢复goodbit.

2. cin.sync():清空缓冲区,but,linux g++下无效。VC特有的

3. fflush():清空缓冲区,linux g++下无效。VC等支持

4. cin.ignore():清空缓冲区,g++支持。

cin.ignore(std::numeric_limits<std::streamsize>::max(),'\n');

用于准确清空缓冲区,std::numeric_limits<std::streamsize>::max()计算缓冲区大小。(需要头文件:#include <limits>)

网上摘抄一段:

其实用fflush是不对的,因为在标准C中只定义了输出流、更新流的刷新,而输入流的刷新是未定义的。当然,在vc下面的fflush(stdin)是微软自己扩展的,而GCC下面是没有的。

c语言:

while( (c=getchar())!='\n' && c != EOF)

;

c++:

cin.clear(); //这里如果用cin.clear(istream::failbit); 是不行的

cin.ignore(numeric_limits<streamsize>::max(),'\n');

组合(聚合)与继承关系:

公有继承:is-a关系,满足里氏替换原则,B继承A ,那么我们说B 是一个A,没问题,B能当作A使用。

私有继承:is-implemented-in-terms-of(根据某物实现出),不需要满足里氏替换原则,如,B需要用到A,那么就可以这样做。

保护继承:实现继承,同私有继承。

组合(聚合):has-a关系,就是在B中有一个A的对象a(当然也可以是多个)。

派生类的三种继承方式小结:

  公有继承public)、 私有继承(private)和保护继承(protected)是常用的三种继承方式。

1.对于公有继承方式:

  ·基类成员对其对象的可见性与一般类及其对象的可见性相同,公有成员可见,其他成员不可见。这里保护成员与私有成员相同。

  ·基类成员对派生类的可见性对派生类来说,基类的公有成员和保护成员可见:基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态;基类的私有成员不可见:基类的私有成员仍然是私有的,派生类不可访问基类中的私有成员。

  ·基类成员对派生类对象的可见性对派生类对象来说,基类的公有成员是可见的,其他成员是不可见。

  所以,在公有继承时,派生类的对象可以访问基类中的公有成员;派生类的成员函数可以访问基类中的公有成员和保护成员。

2.对于私有继承方式:

  ·基类成员对其对象的可见性与一般类及其对象的可见性相同,公有成员可见,其他成员不可见。

  ·基类成员对派生类的可见性对派生类来说,基类的公有成员和保护成员是可见的:基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问;基类的私有成员是不可见的:派生类不可访问基类中的私有成员。

  ·基类成员对派生类对象的可见性对派生类对象来说,基类的所有成员都是不可见的。

  所以,在私有继承时,基类的成员只能由直接派生类访问,而无法再往下继承。

3.对于保护继承方式:

  这种继承方式与私有继承方式的情况相同。两者的区别仅在于对派生类的成员而言,

  ·基类成员对其对象的可见性与一般类及其对象的可见性相同,公有成员可见,其他成员不可见。

  ·基类成员对派生类的可见性对派生类来说,基类的公有成员和保护成员是可见的:基类的公有成员和保护成员都作为派生类的保护成员,并且不能被这个派生类的子类所访问;基类的私有成员是不可见的:派生类不可访问基类中的私有成员。

  ·基类成员对派生类对象的可见性对派生类对象来说,基类的所有成员都是不可见的。

  所以,在保护继承时,基类的成员也只能由直接派生类访问,而无法再往下继承。

本章小结

  C++支持多重继承,从而大大增强了面向对象程序设计的能力。

  多重继承是一个类从多个基类派生而来的能力。派生类实际上获取了所有基类的特性。

  当一个类是两个或多个基类的派生类时,必须在派生类名和冒号之后,列出所有基类的类名,基类间用逗号隔开。

  派生类的构造函数必须激活所有基类的构造函数,并把相应的参数传递给它们。

  派生类可以是另一个类的基类,这样,相当于形成了一个继承链,当派生类的构造函数被激活时,它的所有基类的构造函数也都会被激活。

   在面向对象的程序设计中,继承和多重继承一般指公共继承。在无继承的类中,protected和private控制符是没有差别的。在继承中,基类的private对所有的外界都屏蔽(包括自己的派生类), 基类的protected控制符对应用程序是屏蔽的,但对其派生类是可访问的。

  保护继承和私有继承只是在技术上讨论时有其一席之地。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: