Оператор = перегрузка в C ++ - PullRequest
11 голосов
/ 28 марта 2011

В книге C ++ Primer он содержит код для символьных массивов в стиле C и показывает, как перегрузить оператор = в статье 15.3 Operator = .

String& String::operator=( const char *sobj )
{
   // sobj is the null pointer,
   if ( ! sobj ) {
      _size = 0;
      delete[] _string;
      _string = 0;
   }
   else {
      _size = strlen( sobj );
      delete[] _string;
      _string = new char[ _size + 1 ];
      strcpy( _string, sobj );
   }
   return *this;
}

Теперь я хотел бы знать, почему существует необходимость возвращать ссылку String &, когда этот код, приведенный ниже, выполняет ту же работу без проблем:

void String::operator=( const char *sobj )
{
   // sobj is the null pointer,
   if ( ! sobj ) {
      _size = 0;
      delete[] _string;
      _string = 0;
   }
   else {
      _size = strlen( sobj );
      delete[] _string;
      _string = new char[ _size + 1 ];
      strcpy( _string, sobj );
   }

}
  • пожалуйста, помогите.

Ответы [ 5 ]

20 голосов
/ 28 марта 2011

Поддерживается следующая идиома:

String a, b;
const char *c;

// set c to something interesting

a = b = c;

Чтобы это работало, b = c должен вернуть соответствующий объект или ссылку для присвоения a;на самом деле это a = (b = c) в соответствии с правилами приоритета оператора C ++.

Если вы вернете указатель this, вам придется написать a = *(b = c), что не передает предполагаемого значения.

8 голосов
/ 28 марта 2011

@ larsmans уже ответили на ваш точный вопрос, так что я действительно отступлю: Это какой-то дерьмовый код!

Проблема здесь в три раза:

  1. Вы только что продублировали код конструктора копирования (несколько)
  2. strcpy может быть лучше заменено на strncpy, что делает некоторую связанную проверку
  3. Это не исключение безопасно

1) и 2) более стилистичны, чем что-либо еще, но 3) вызывает большую обеспокоенность

РЕДАКТИРОВАТЬ: , как указал @Jerry Coffin, это не защищает от самостоятельного назначения. То есть, если sobj и _string указывают на один и тот же массив символов, у вас большие проблемы. Простое решение в конце этого поста также охватывает эту ситуацию.

Не исключение безопасно

Давайте посмотрим на часть кода, а именно на else часть:

  _size = strlen( sobj );
  delete[] _string;
  _string = new char[ _size + 1 ];
  strcpy( _string, sobj );

Что произойдет, если по какой-то причине new бросит?

  • _size имеет значение новой строки
  • _string указывает на старый указатель ... который был освобожден

Поэтому не только объект остается в непригодном для использования состоянии (половина его данных от нового объекта, половина от старого), но он даже не может быть уничтожен (если деструктор не утекает ...?)

Добавление исключений безопасности, трудный путь

  _size = strlen( sobj );
  delete[] _string;

  try {
    _string = new char[ _size + 1 ];
  } catch(...) {
    _size = 0; _string = 0;
    throw;
  }
  strcpy( _string, sobj );

Хорошо, это действительно практический результат, но он дает нам Базовую гарантию исключений : нет функциональной гарантии, но гарантия, что код технически корректен (без сбоев, без утечек).

Добавление безопасности исключений, простой способ: идиома Копировать-и-Поменять

Найти более полное описание можно по адресу: Что такое идиома копирования и обмена?

void swap(String& lhs, String& rhs) {
  using std::swap;
  swap(lhs._size, rhs._size);
  swap(lhs._string, rhs._string);
}

String& String::operator=(String other) { // pass-by-value
  swap(*this, other);
  return *this;
}

Как это работает?

  • мы повторно используем конструктор копии для фактической копии
  • мы используем функцию обмена для обмена значениями
  • мы повторно используем деструктор для очистки

И это даже лучше, чем в предыдущей версии, так как теперь у нас есть Сильная гарантия исключения : она транзакционная, поэтому, если она терпит неудачу, назначенная нам строка остается неизменной (как будто ничего не произошло) .

Подробнее о гарантиях исключений .

Я немного испугался, что учебник C ++ будет продвигать такой сомнительный код, скажите, пожалуйста, что это пример того, чего не следует делать: /

3 голосов
/ 28 марта 2011

Конвенция. Это делают встроенные типы, это делают автоматически сгенерированные операторы присваивания, это позволяет вам сделать это:

a = b = c;

или это:

if ( foo = come_function() ) {
   // use foo here
}
0 голосов
/ 28 марта 2011

По договоренности вы можете сделать, например:

int x, y, z;
x = y = z = 0;

if ((x = 1) != 0) { ... }

Чтобы сохранить эту семантику для экземпляров вашего класса, вам нужно вернуть *this - разрешив цепочечное присваивание. Если вы вернете только this, вы вернете указатель на ваш экземпляр, и цепочечное присвоение не сработает - нет operator= для String *, есть один для String

0 голосов
/ 28 марта 2011

Возвращение от оператора =, чтобы вы могли связать вещи.Смотрите здесь без строк:

x = y = 2;

Часть y=2 этой строки возвращает 2. Вот как x получает значение.Если op = возвращено void, вы не можете выполнить цепочку = операций.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...