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

One of the best uses for multiple inheritance involves code that’s out of your control. Suppose you’ve acquired a library that consists of a header file and compiled member functions, but no source code for member functions. This library is a class hierarchy with virtual functions, and it contains some global functions that take pointers to the base class of the library; that is, it uses the library objects polymorphically. Now suppose you build an application around this library and write your own code that uses the base class polymorphically.

Later in the development of the project or sometime during its maintenance, you discover that the base-class interface provided by the vendor doesn’t provide what you need: a function may be non-virtual and you need it to be virtual, or a virtual function is completely missing in the interface, but essential to the solution of your problem. Multiple inheritance is the perfect solution.

For example, here’s the header file for a library you acquire:

//: C09:Vendor.h

// Vendor-supplied class header

// You only get this & the compiled Vendor.obj

#ifndef VENDOR_H

#define VENDOR_H

class Vendor {

public:

  virtual void v() const;

  void f() const;

  ~Vendor();

};

class Vendor1 : public Vendor {

public:

  void v() const;

  void f() const;

  ~Vendor1();

};

void A(const Vendor&);

void B(const Vendor&);

// Etc.

#endif // VENDOR_H ///:~

Assume the library is much bigger, with more derived classes and a larger interface. Notice that it also includes the functions A( ) and B( ), which take a base reference and treat it polymorphically. Here’s the implementation file for the library:

//: C09:Vendor.cpp {O}

// Implementation of VENDOR.H

// This is compiled and unavailable to you

#include

#include "Vendor.h"

using namespace std;

void Vendor::v() const {

  cout << "Vendor::v()\n";

}

void Vendor::f() const {

  cout << "Vendor::f()\n";

}

Vendor::~Vendor() {

  cout << "~Vendor()\n";

}

void Vendor1::v() const {

  cout << "Vendor1::v()\n";

}

void Vendor1::f() const {

  cout << "Vendor1::f()\n";

}

Vendor1::~Vendor1() {

  cout << "~Vendor1()\n";

}

void A(const Vendor& V) {

  // ...

  V.v();

  V.f();

  //..

}

void B(const Vendor& V) {

  // ...

  V.v();

  V.f();

  //..

} ///:~

In your project, this source code is unavailable to you. Instead, you get a compiled file as Vendor.obj or Vendor.lib (or with the equivalent file suffixes for your system).

The problem occurs in the use of this library. First, the destructor isn’t virtual. In addition, f( ) was not made virtual; we assume the library creator decided it wouldn’t need to be. And you discover that the interface to the base class is missing a function essential to the solution of your problem. Also suppose you’ve already written a fair amount of code using the existing interface (not to mention the functions A( ) and B( ), which are out of your control), and you don’t want to change it.

To repair the problem, create your own class interface and multiply inherit a new set of derived classes from your interface and from the existing classes:

//: C09:Paste.cpp

//{L} Vendor

// Fixing a mess with MI

#include

#include "Vendor.h"

using namespace std;

class MyBase { // Repair Vendor interface

public:

  virtual void v() const = 0;

  virtual void f() const = 0;

  // New interface function:

  virtual void g() const = 0;

  virtual ~MyBase() { cout << "~MyBase()\n"; }

};

class Paste1 : public MyBase, public Vendor1 {

public:

  void v() const {

    cout << "Paste1::v()\n";

    Vendor1::v();

  }

  void f() const {

    cout << "Paste1::f()\n";

    Vendor1::f();

  }

  void g() const {

    cout << "Paste1::g()\n";

  }

  ~Paste1() { cout << "~Paste1()\n"; }

};

int main() {

  Paste1& p1p = *new Paste1;

  MyBase& mp = p1p; // Upcast

  cout << "calling f()\n";

  mp.f();  // Right behavior

  cout << "calling g()\n";

  mp.g(); // New behavior

  cout << "calling A(p1p)\n";

  A(p1p); // Same old behavior

  cout << "calling B(p1p)\n";

  B(p1p);  // Same old behavior

  cout << "delete mp\n";

  // Deleting a reference to a heap object:

  delete ∓ // Right behavior

} ///:~

In MyBase (which does not use MI), both f( ) and the destructor are now virtual, and a new virtual function g( ) is added to the interface. Now each of the derived classes in the original library must be re-created, mixing in the new interface with MI. The functions Paste1::v( ) and Paste1::f( )need to call only the original base-class versions of their functions. But now, if you upcast to MyBase as in main( )

MyBase* mp = p1p; // Upcast

any function calls made through mp will be polymorphic, including delete. Also, the new interface function g( ) can be called through mp. Here’s the output of the program:

calling f()

Paste1::f()

Vendor1::f()

calling g()

Paste1::g()

calling A(p1p)

Paste1::v()

Vendor1::v()

Vendor::f()

calling B(p1p)

Paste1::v()

Vendor1::v()

Vendor::f()

delete mp

~Paste1()

~Vendor1()

~Vendor()

~MyBase()

The original library functions A( ) and B( ) still work the same (assuming the new v( ) calls its base-class version). The destructor is now virtual and exhibits the correct behavior.

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

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

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

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

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

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

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

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

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