Читаем Thinking In C++. Volume 2: Practical Programming полностью

The initialization of g requires its E and F part to first be initialized, but the B and C subobjects are initialized first, because they are virtual bases, and are initialized from G’s initializer, G being the most-derived class. The class B has no base classes, so according to rule 3, its member object m is initialized, then its constructor prints "B from G", and similarly for the C subject of E. The E subobject requires A, B, and C subobjects. Since B and C have already been initialized, the A subobject of the E subobject is initialized next, and then the E subobject itself. The same scenario repeats for g’s F subobject, but without duplicating the initialization of the virtual bases.

<p>Name lookup issues</p>

The ambiguities we have illustrated with subobjects apply, of course, to any names, including function names. If a class has multiple direct base classes that share member functions of the same name, and you call one of those member functions, the compiler doesn’t know which one to choose. The following sample program would report such an error.

// C09:AmbiguousName.cpp

class Top {};

class Left : virtual public Top {

public:

   void f(){}

};

class Right : virtual public Top {

public:

   void f(){}

};

class Bottom : public Left, public Right {};

int main() {

   Bottom b;

   b.f();     // error here

}

The class Bottom has inherited two functions of the same name (the signature is irrelevant, since name lookup occurs before overload resolution), and there is no way to choose between them. The usual technique to disambiguate the call is to qualify the function call with the base class name:

//: C09:BreakTie.cpp

class Top {};

class Left : virtual public Top {

public:

   void f(){}

};

class Right : virtual public Top {

public:

   void f(){}

};

class Bottom : public Left, public Right {

public:

  using Left::f;

};

int main() {

   Bottom b;

   b.f();     // calls Left::f()

} ///:~

The name Left::f is now found in the scope of Bottom, so the name Right::f is not even considered. Of course, if you want to introduce extra functionality beyond what Left::f( ) provides, you would implement a Bottom::f( ) function that calls Left::f( ), in addition to other things.

Functions with the same name occurring in different branches of a hierarchy often conflict. The following hierarchy has no such problem:

//: C09:Dominance.cpp

class Top {

public:

  virtual void f() {}

};

class Left : virtual public Top {

public:

   void f(){}

};

class Right : virtual public Top {

};

class Bottom : public Left, public Right {};

int main() {

   Bottom b;

   b.f(); // calls Left::f()

} ///:~

In this case, there is no explicit Right::f( ), so Left::f( ), being the most derived, is chosen. Why? Well, pretend that Right did not exist, giving the single-inheritance hierarchy Top <= Left <= Bottom. You would certainly expect Left::f( ) to be the function called by the expression b.f( ), because of normal scope rules (a derived class is considered a nested scope of a base class). In general, a name A::f is said to dominate the name B::f if A derives from B, directly or indirectly, or in other words, if A is "more derived" in the hierarchy than B.[113] To summarize: in choosing between two functions with the same name, one of which dominates the other, the compiler chooses the one that dominates. If there is no dominant function, there is an ambiguity.

 The following program further illustrates the dominance principle.

//: C09:Dominance2.cpp

#include

using namespace std;

class A {

public:

   virtual void f() {cout << "A::f\n";}

};

class B : virtual public A {

public:

   void f() {cout << "B::f\n";}

};

class C : public B {};

class D : public C, virtual public A {};

int main()

{

   B* p = new D;

   p->f();  // calls B::f()

} ///:~

The class diagram for this hierarchy is as follows.

The class A is a (direct, in this case) base class for B, and so the name B::f dominates A::f.

<p>Avoiding MI</p>

When the question of whether to use multiple inheritance comes up, ask at least two questions:

1.Do you need to show the public interfaces of both these classes through your new type, or could one class be contained within the other, with only some of its interface exposed in the new class?

2.Do you need to upcast to both of the base classes? (This applies when you have more than two base classes, of course.)

If you can answer "yes" to either question, you can avoid using MI and should probably do so.

One situation to watch for is when one class needs to be upcast only as a function argument. In that case, the class can be embedded and an automatic type conversion operator provided in your new class to produce a reference to the embedded object. Any time you use an object of your new class as an argument to a function that expects the embedded object, the type conversion operator is used.[114] However, type conversion can’t be used for normal member selection; that requires inheritance.

<p>Extending an interface</p>
Перейти на страницу:

Похожие книги

3ds Max 2008
3ds Max 2008

Одни уверены, что нет лучшего способа обучения 3ds Мах, чем прочитать хорошую книгу. Другие склоняются к тому, что эффективнее учиться у преподавателя, который показывает, что и как нужно делать. Данное издание объединяет оба подхода. Его цель – сделать освоение 3ds Мах 2008 максимально быстрым и результативным. Часто после изучения книги у читателя возникают вопросы, почему не получился тот или иной пример. Видеокурс – это гарантия, что такие вопросы не возникнут: ведь автор не только рассказывает, но и показывает, как нужно работать в 3ds Мах.В отличие от большинства интерактивных курсов, где работа в 3ds Мах иллюстрируется на кубиках-шариках, данный видеокурс полностью практический. Все приемы работы с инструментами 3ds Мах 2008 показаны на конкретных примерах, благодаря чему после просмотра курса читатель сможет самостоятельно выполнять даже сложные проекты.

Владимир Антонович Верстак , Владимир Верстак

Программирование, программы, базы данных / Программное обеспечение / Книги по IT