Читаем 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>
Перейти на страницу:

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

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

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