Читаем 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

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

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

3ds Max 2008
3ds Max 2008

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

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

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