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

void testPrintable(const Printable& p) {

  p.print(cout);

  cout << endl;

}

template

void testIntable(const Intable& n) {

  int i = n.toInt() + 1;

  cout << i << endl;

}

template

void testStringable(const Stringable& s) {

  string buf = s.toString() + "th";

  cout << buf << endl;

}

int main() {

  Able a(7);

  testPrintable(a);

  testIntable(a);

  testStringable(a);

} ///:~

The names Printable, Intable, and Stringable are now just template parameters that assume the existence of the operations indicated in their respective contexts. Some people are more comfortable with the first version, because the type names guarantee by inheritance that the expected interfaces are implemented. Others are content with the fact that if the operations required by the test functions are not satisfied by their template type arguments, the error is still caught at compile time. The latter approach is technically a "weaker" form of type checking than the former (inheritance) approach, but the effect on the programmer (and the program) is the same. This is one form of weak typing that is acceptable to many of today’s C++ programmers.

<p>Implementation inheritance</p>

As we stated earlier, C++ provides only implementation inheritance, meaning that you inherit everything from all your base classes. This can be a good thing, of course, because it frees you from having to implement everything in the derived class, as we had to do with the interface inheritance examples earlier. A common use of multiple inheritance involves using mixin classes, which are classes not intended to be instantiated independently, but exist to add capabilities to other classes through inheritance.

As an example, suppose we are clients of a class that supports access to a database. We will likely only have a header file available (which is part of the point we are about to make), but for illustration, assume the following, simple implementation of a Database class:

//: C09:Database.h

// A prototypical resource class

#ifndef DATABASE_H

#define DATABASE_H

#include

#include

#include

using std::cout;

using std::string;

using std::runtime_error;

struct DatabaseError : runtime_error {

  DatabaseError(const string& msg) : runtime_error(msg)

  {}

};

class Database {

public:

  Database(const string& dbStr) : dbid(dbStr) {}

  virtual ~Database(){}

  void open() throw(DatabaseError) {

    cout << "connected to " << dbid << '\n';

  }

  void close() {

    cout << dbid << " closed\n";

  }

  //Other database functions...

private:

  string dbid;

};

#endif ///:~

We’re leaving out actual database functionality (storing, retrieving, and so on), but that’s actually not important here. Using this class requires a database connection string and that you call Database::open( ) to connect and Database::close( ) to disconnect:

//: C09:UseDatabase.cpp

#include "Database.h"

int main() {

  Database db("MyDatabase");

  db.open();

  // Use other db functions...

  db.close();

}

/* Output:

connected to MyDatabase

MyDatabase closed

*/ ///:~

In a typical client-server situation, a client will have multiple objects sharing a connection to a database. It is important that the database eventually be closed, but only after access to it is no longer required. It is common to encapsulate this behavior through a class that tracks the number of client entities using the database connection and to automatically terminate the connection when that count goes to zero. To add reference counting to the Database class, we create a mixin class named Countable and mix it into the Database class by creating a new class, DBConnection, through multiple inheritance. Here’s the Countable mixin class:

//: C09:Countable.h

// A "mixin" class

#ifndef COUNTABLE_H

#define COUNTABLE_H

#include

class Countable {

public:

  long attach() { return ++count; }

  long detach() {

    return (--count > 0) ? count : (delete this, 0);

  }

  long refCount() const { return count; }

protected:

  Countable() { count = 0; }

  virtual ~Countable() { assert(count == 0); }

private:

  long count;

};

#endif ///:~

It is evident that this is not a standalone class because its constructor is protected; it therefore requires a friend or a derived class to use it. It is important that the destructor is virtual, of course, because it is called only from the delete this statement in detach( ), and we of course want derived objects to be completely destroyed.[107] The DBConnection class derives from both Database and Countable and provides a static create( ) function that initializes its Countable subobject. (This is an example of the Factory Method design pattern, discussed in the next chapter.)

//: C09:DBConnection.h

// Uses a "mixin" class

#ifndef DBCONNECTION_H

#define DBCONNECTION_H

#include "Countable.h"

#include "Database.h"

#include

#include

using std::string;

class DBConnection : public Database, public Countable {

public:

  static DBConnection* create(const string& dbStr)

  throw(DatabaseError) {

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

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

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

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

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

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

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

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

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