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

  cout << "bp == cp? " << boolalpha << (bp == cp) << endl;

  cp = 0;

  bp = cp;

  cout << bp << endl;

}

/* Output:

sizeof(A) == 4

sizeof(B) == 4

sizeof(C) == 12

&c == 1245052

ap == 1245052

bp == 1245056

cp == 1245052

bp == cp? true

0

*/ ///:~

As you can see, the B portion of the object c is offset 4 bytes from the beginning of the entire object, suggesting the following layout:

The object c begins with it’s A subobject, then the B portion, and finally the data from the complete type C itself. Since a C is-an A and is-a B, it is possible to upcast to either base type. When upcasting to an A, the resulting pointer points to the A portion, which happens to be at the beginning of the C object, so the address ap is the same as the expression &c. When upcasting to a B, however, the resulting pointer must point to where the B subobject actually resides, because class B knows nothing about class C (or class A, for that matter). In other words, the object pointed to by bp must be able to behave as a standalone B object (except for any required polymorphic behavior, of course).

When casting bp back to a C*, since the original object was a C in the first place, the location where the B subobject resides is known, so the pointer is adjusted back to the original address of the complete object. If bp had been pointing to a standalone B object instead of a C object in the first place, the cast would be illegal.[109] Furthermore, in the comparison bp == cp, cp is implicitly converted to a B*, since that is the only way to make the comparison meaningful in general (that is, upcasting is always allowed), hence the true result. So when converting back and forth between subobjects and complete types, the appropriate offset is applied.

The null pointer requires special handling, obviously, since blindly subtracting an offset when converting to or from a B subobject will result in an invalid address if the pointer was zero to start with. For this reason, when casting to or from a B*, the compiler generates logic to check first to see if the pointer is zero. If it isn’t, it applies the offset; otherwise, it leaves it as zero.

With the syntax we’ve seen so far, if you have multiple base classes, and if those base classes in turn have a common base class, you will have two copies of the top-level base, as you can see in the following example.

//: C09:Duplicate.cpp

// Shows duplicate subobjects

#include

using namespace std;

class Top {

  int x;

public:

  Top(int n) { x = n; }

};

class Left : public Top {

  int y;

public:

  Left(int m, int n) : Top(m) { y = n; }

};

class Right : public Top {

  int z;

public:

  Right(int m, int n) : Top(m) { z = n; }

};

class Bottom : public Left, public Right {

  int w;

public:

  Bottom(int i, int j, int k, int m)

  : Left(i, k), Right(j, k) { w = m; }

};

int main() {

  Bottom b(1, 2, 3, 4);

  cout << sizeof b << endl; // 20

} ///:~

Since the size of b is 20 bytes,[110] there are five integers altogether in a complete Bottom object. A typical class diagram for this scenario usually appears as:

This is the so-called "diamond inheritance", but in this case it would be better rendered as:

The awkwardness of this design surfaces in the constructor for the Bottom class in the previous code. The user thinks that only four integers are required, but which arguments should be passed to the two parameters that Left and Right require? Although this design is not inherently "wrong," it is usually not what an application calls for. It also presents a problem when trying to convert a pointer to a Bottom object to a pointer to Top. As we showed earlier, the address may need to be adjusted, depending on where the subobject resides within the complete object, but in this case there are two Top subobjects to choose from. The compiler doesn’t know which to choose, so such an upcast is ambiguous and therefore not allowed. The same reasoning explains why a Bottom object would not be able to call a function that is only defined in Top. If such a function Top::f( ) existed, calling b.f( ) above would need to refer to a Top subobject as a context in which to execute, and there are two to choose between.

<p>Virtual base classes</p>

What we usually want in such cases is true diamond inheritance, in which a single Top object is shared by both Left and Right subobjects within a complete Bottom object, which is what the first class diagram depicts. This is achieved by making Top a virtual base class of Left and Right:

//: C09:VirtualBase.cpp

// Shows a shared subobject via a virtual base

#include

using namespace std;

class Top {

protected:

  int x;

public:

  Top(int n) { x = n; }

  virtual ~Top(){}

  friend ostream&

  operator<<(ostream& os, const Top& t) {

    return os << t.x;

  }

};

class Left : virtual public Top {

protected:

  int y;

public:

  Left(int m, int n) : Top(m) { y = n; }

};

class Right : virtual public Top {

protected:

  int z;

public:

  Right(int m, int n) : Top(m) { z = n; }

};

class Bottom : public Left, public Right {

  int w;

public:

Перейти на страницу:

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

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

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

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

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

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

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

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