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

What happens if the copy constructor that is called for the return value in the last line throws an exception when the value is returned? The popped element is not returned because of the exception, and yet count has already been decremented, so the top element you wanted is lost forever! The problem is that this function attempts to do two things at once: (1) return a value, and (2) change the state of the stack. It is better to separate these two actions into two separate member functions, which is exactly what the standard stack class does. (In other words, follow the time-worn design practice of cohesion—every function should do one thing well.) Exception-safe code leaves objects in a consistent state and does not leak resources.

You also need to be careful writing custom assignment operators. In Chapter 12 of Volume 1, you saw that operator= should adhere to the following pattern:

1.       Make sure you’re not assigning to self. If you are, go to step 6. (This is strictly an optimization.)

2.      Allocate new memory required by pointer data members.

3.      Copy data from the old memory to the new.

4.      Delete the old memory.

5.      Update the object’s state by assigning the new heap pointers to the pointer data members.

6.      Return *this.

It’s important to not change the state of your object until all the new pieces have been safely allocated and initialized. A good technique is to move all of steps 2 and 3 into a separate function, often called clone( ). The following example does this for a class that has two pointer members, theString and theInts.

//: C01:SafeAssign.cpp

// Shows an Exception-safe operator=

#include

#include        // For std::bad_alloc

#include

using namespace std;

// A class that has two pointer members using the heap

class HasPointers {

  // A Handle class to hold the data

  struct MyData {

    const char* theString;

    const int* theInts;

    size_t numInts;

    MyData(const char* pString, const int* pInts,

           size_t nInts)

    : theString(pString), theInts(pInts),

    numInts(nInts) {}

  } *theData;  // The handle

  // clone and cleanup functions

  static MyData* clone(const char* otherString,

        const int* otherInts, size_t nInts){

    char* newChars = new char[strlen(otherString)+1];

    int* newInts;

    try {

      newInts = new int[nInts];

    } catch (bad_alloc&) {

      delete [] newChars;

      throw;

    }

    try {

      // This example uses built-in types, so it won't

      // throw, but for class types it could throw, so we

      // use a try block for illustration. (This is the

      // point of the example!)

      strcpy(newChars, otherString);

      for (size_t i = 0; i < nInts; ++i)

        newInts[i] = otherInts[i];

    } catch (...) {

      delete [] newInts;

      delete [] newChars;

      throw;

    }

    return new MyData(newChars, newInts, nInts);

  }

  static MyData* clone(const MyData* otherData) {

    return clone(otherData->theString,

  otherData->theInts,

  otherData->numInts);

  }

  static void cleanup(const MyData* theData) {

    delete [] theData->theString;

    delete [] theData->theInts;

    delete theData;

  }

public:

  HasPointers(const char* someString, const int* someInts,

              size_t numInts) {

    theData = clone(someString, someInts, numInts);

  }

  HasPointers(const HasPointers& source) {

    theData = clone(source.theData);

  }

  HasPointers& operator=(const HasPointers& rhs) {

    if (this != &rhs) {

      MyData* newData =

      clone(rhs.theData->theString,

            rhs.theData->theInts,

            rhs.theData->numInts);

      cleanup(theData);

      theData = newData;

    }

    return *this;

  }

  ~HasPointers() {

    cleanup(theData);

  }

  friend ostream& operator<<(ostream& os,

              const HasPointers& obj) {

    os << obj.theData->theString << ": ";

    for (size_t i = 0; i < obj.theData->numInts; ++i)

      os << obj.theData->theInts[i] << ' ';

    return os;

  }

};

int main() {

  int someNums[] = {1, 2, 3, 4};

  size_t someCount = sizeof someNums / sizeof someNums[0];

  int someMoreNums[] = {5, 6, 7};

  size_t someMoreCount =

  sizeof someMoreNums / sizeof someMoreNums[0];

  HasPointers h1("Hello", someNums, someCount);

  HasPointers h2("Goodbye", someMoreNums, someMoreCount);

  cout << h1 << endl;  // Hello: 1 2 3 4

  h1 = h2;

  cout << h1 << endl;  // Goodbye: 5 6 7

} ///:~

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

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

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

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

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

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

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

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

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