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

  operator<<(ostream& os, const Bottom& b) {

    return os << static_cast(b)

      << ',' << static_cast(b)

      << ',' << b.w;

  }

};

int main() {

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

  cout << b << endl;  // 1,2,1,3,4

} ///:~

You can’t just blindly share the responsibility upward in the usual fashion because the Left and Right stream inserters each call the Top inserter, and again there will be duplication of data. Instead you need to mimic what the compiler automatically does with initialization. One solution is to provide special functions in the classes that know about the virtual base class, which ignore the virtual base when printing (leaving the job to the most derived class):

//: C09:VirtualBase3.cpp

// A correct stream inserter

#include

using namespace std;

class Top {

  int x;

public:

  Top(int n) { x = n; }

  friend ostream&

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

    return os << t.x;

  }

};

class Left : virtual public Top {

  int y;

protected:

  void specialPrint(ostream& os) const {

    // Only print Left's part

    os << ','<< y;

  }

public:

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

  friend ostream&

  operator<<(ostream& os, const Left& l) {

    return os << static_cast(l) << ',' << l.y;

  }

};

class Right : virtual public Top {

  int z;

protected:

  void specialPrint(ostream& os) const {

    // Only print Right's part

    os << ','<< z;

  }

public:

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

  friend ostream&

  operator<<(ostream& os, const Right& r) {

    return os << static_cast(r) << ',' << r.z;

  }

};

class Bottom : public Left, public Right {

  int w;

public:

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

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

  friend ostream&

  operator<<(ostream& os, const Bottom& b) {

    os << static_cast(b);

    b.Left::specialPrint(os);

    b.Right::specialPrint(os);

    return os << ',' << b.w;

  }

};

int main() {

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

  cout << b << endl;  // 1,2,3,4

} ///:~

The specialPrint( ) functions are protected since they will be called only by Bottom. They print only their own data and ignore their Top subobject, because the Bottom inserter is in control when these functions are called. The Bottom inserter must know about the virtual base, just as a Bottom constructor needs to. This same reasoning applies to assignment operators in a hierarchy with a virtual base, as well as to any function, member or not, that wants to share the work throughout all classes in the hierarchy.

Having discussed virtual base classes, we can now illustrate the "full story" of object initialization. Since virtual bases give rise to shared subobjects, it makes sense that they should be available before the sharing takes place. Therefore, the order of initialization of subobjects follows these rules (recursively, as needed, of course):

3.All virtual base class subobjects are initialized, in top-down, left-to-right order according to where they appear in class definitions.

4.Non-virtual base classes are then initialized in the usual order.

5.All member objects are initialized in declaration order.

6.The complete object’s constructor executes.

The following program illustrates this behavior.

//: C09:VirtInit.cpp

// Illustrates initialization order with virtual bases

#include

#include

using namespace std;

class M {

public:

  M(const string& s) {

    cout << "M " << s << endl;

  }

};

class A{

  M m;

public:

  A(const string& s) : m("in A") {

     cout << "A " << s << endl;

  }

};

class B

{

  M m;

public:

  B(const string& s) : m("in B")  {

    cout << "B " << s << endl;

  }

};

class C

{

  M m;

public:

  C(const string& s) : m("in C")  {

    cout << "C " << s << endl;

  }

};

class D

{

  M m;

public:

  D(const string& s) : m("in D") {

    cout << "D " << s << endl;

  }

};

class E : public A, virtual public B, virtual public C

{

  M m;

public:

  E(const string& s)

  : A("from E"), B("from E"), C("from E"), m("in E") {

    cout << "E " << s << endl;

  }

};

class F : virtual public B, virtual public C, public D

{

  M m;

public:

  F(const string& s)

  : B("from F"), C("from F"), D("from F"), m("in F") {

    cout << "F " << s << endl;

  }

};

class G : public E, public F

{

  M m;

public:

  G(const string& s)

  : B("from G"), C("from G"), E("from G"),

    F("from G"), m("in G") {

    cout << "G " << s << endl;

  }

};

int main() {

  G g("from main");

} ///:~

The classes in this code can be represented by the following diagram:

Each class has an embedded member of type M. Note that only four derivations are virtual: E from B and C, and F from B and C. The output of this program is

M in B

B from G

M in C

C from G

M in A

A from E

M in E

E from G

M in D

D from F

M in F

F from G

M in G

G from main

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

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

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

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

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

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

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

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

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