第十八天(C++代码重用III)

         我只想说一句:码字是件很辛苦的事。所以以下是刚过去没多久的“昨天”的笔记


2012-3-17(Reusing Code in C++ part III)

9. Multiple Inheritance (MI)

MI describes a class that has more than one immediate base-class. MI can introduce new problems for programmers. One main problem is inheriting different methods with the same name from two different base classes. Using the scope-resolution, type cast or other means can be easy to fix it. 

Another main problem appears when two or more immediate base-classes have the same base-class. Fix this problem involves introducing a few new rules and additional syntax.

Thus, using MI makes problem solution more complex than using single inheritance. We will discuss about the most complex portion of MI by the following program, and see what the problems and solutions are.

#include <string>

using std::string;

class First	//an ABC
{
private:
    string common1;
    int common2;
public:
    First():common1("NULL"),common2(0) {}
    First(const string& s, int n): common1(s),common2(n) {}
    virtual ~First()= 0;    //pure virtual destructor
    virtual void set();
    virtual void show() const;
};

class Second1: public First
{
private:
    int different1;
public:
    Second1(): First(),different1(0) {}
    Second1(const string& s, int n1, int n2 = 0)
        :First(s,n1),different1(n2) {}
    void set();
    void show() const;
};

class Second2: public First
{
private:
    double different2;
public:
    Second2(): First(),different2(0) {}
    Second2(const string& s, int n1, int d = 0.0)
        :First(s,n1),different2(d) {}
    void set();
    void show() const;
};

class Third: public Second1, public Second2
{   
    //???
};
In this example, class First derives class Second1 and Second2 , and both of them are the immediate base-class of class Third .

Both Second1 and Second2 inheritance a First component, then Third winds up with two First components. As you might expect, this raises problems. For example:

                          Third t;

                       First* f = &t;

It would course no problem to set a base-class pointer to the address of the base-class object within the derived object when single inheriting. But t contains two First objects, which addresses should it choose? You can specify which object by using a type cast:

                    First* f1 = (Second1 *)&t;    //the First in Second1

                 First* f2 = (Second2 *)&t;    //the First in Second2

Obviously, it doesn't work when using an array of base-class pointers to refer to a variety of objects.

So what on earth course this kind of awkward? Two copies of First objects. Theoretically, a single Third object should have just one common1 and common2. C++ using a new technology to come it true:virtual base class.

 

10. Virtual Base Class

Virtual base class allow an object derived from multiple base the themselves share a common base class to inherit just one object of that shared base class. Using keyword virtual to achievethis objective:

                     class Second1: virtual public First {…};

                  class Second2: public virtual First {…};

Then you could define Third as before:

                      class Third:public Second1, public Second2 {…};

In essence, the inheritedSecond1 and Second2 objects share a common First object instead of each bringing in its own copy.

Several questions about this syntax:

  •   Why the term virtual?
  •   Are there any catches?

I. Why the term virtual

After all, there doesn't seem to be an obvious connection between the concepts of virtual functions and virtual base class. For a program language, it's hard to introduce a new keyword when it is applied widely.For example, if a new keyword corresponded to the name of some important functions or variable in a major program. So C++ merely recycled the keyword virtual for the new facility——a bit of key word overloading.

II. Why don't we declare base class virtual to achieve this aim?

First, there are cases in which you might want multiple copies of base class. Second, making a base class virtual requires that a program do some additional accounting, and you shouldn't have to pay for that facility if you don't need it.

III. Are there any catches?

Yes, of course, and not small. Making virtual base classes work requires adjustments to C++ rules, and you have to code somethings differently. We will discuss about that next.

 

11. Not The End..

There are two main modification of code after using virtual base class.

I. New Constructor Rules

So far, if we use single inheritance, initializer list will bring us no trouble, because, the class members' values are passed between base-class and derived-class automatically and correctly. But multiple inheritance will:

                   Third(const string& c1,int c2, int d1,double d2)

                    :Second1(c1,c2,d1),Second2(c1,c2,d2) {}

This kind of initializer list seems strange.Actually, it courses a problem.

That is automatic passing of information would pass Second1 and Second2 object via two separate paths. Popularly speaking, after the first initializer list (Second1(c1,c2,d1)) initializing the only one common component (common1 and common2), the second one do that again. So, to avoid this potential conflict, C++ disables the automatic passing of the information through an intermediate class to a base class if the base class is virtual. However the compiler must construct a base object component before constructing derived objects, in this case, it will use the default First constructor.

       If you don't want to use the default constructor, you need to invoke the appropriate base class constructor explicitly, like:

                  Third(const string& c1,int c2, int d1,double d2)

                    :First(c1,c2),Second1(c1,c2,d1),Second2(c1,c2,d2) {}

It seems stranger, but that is the right answer.Note that this usage is legal and often necessary for virtual base class, and it is illegal non-virtual base classes.

II. Which Method

MI often requires other coding adjustments. Now that you want to print all data members' information of Third class:

                             Third t("T",1,2,5.5);

                          t.show();

Obviously, that is an ambiguous error:Third class has two recent ancestral show() definition, the complier doesn't know which method you want to invoke.

One solution is to use the scope-resolution operator to clarify what you mean:

                             Third t("T",1,2,5.5);

                          t.Second1::show();

But it can only display the members' information of Second1, so a better approach is to redefine show() for Third:

void Third::show()
{
	Second1::show();
	Second2::show();	
}
This display common1 and common2 twice because Second1::show() and Second2::show() both call First::show() .

How can you fix this? One way is to use modular approach instead of incremental approach. That is, provide additional methods to display only First components, and to display only Second1 and Second2 components:

void First::data(){ }             //method to display common1 and common2
void Second1::data(){ }       //method to display different1
void Second2::data(){ }       //method to display different2
void Third::data()
{
        Second1::data();
        Second2::data();
}
void Third::show()
{
        First::data();
        data();
}
These additional functions should be qualified protected :they can only be called by derived class, meanwhile, isn't available outside.

The set() methods, which solicit data for setting object values, parent a similar problem.

 

12. Mixed Virtual and Non-virtual Base

Suppose that class B is a virtual base class to class C and D, and a non-virtual base class to class X and Y. Then class M is derived from C,D,X and Y. How many B sub-objects does class M contain? The answer is 3. 

In general, when a class inherits a particular base class through several virtual paths and several non-virtual paths, the class has one base-class sub-object to represent all the virtual paths and a separate base-class sub-object to represent each non-virtual path.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值