У меня есть класс, похожий на vector
это в первую очередь динамически изменяемый массив. Я пишу это для платформы с ограниченными ресурсами, поэтому я обязан не использовать исключения.
Стало ясно, что для использования перегрузки операторов для упрощения интерфейса для этого класса динамическое распределение должно выполняться в некоторых функциях перегрузки операторов. Оператор присваивания (=) является одним из примеров.
Тем не менее, без исключений становится довольно сложно информировать вызывающего абонента об ошибке неправильного распределения разумным способом, в то же время повторяя попытку обеспечения безопасности при серьезных ошибках. Я мог бы иметь свойство error класса, которое вызывающая сторона должна проверять после каждого вызова, который включает динамическое распределение, но это кажется не очень оптимальным решением.
РЕДАКТИРОВАТЬ:
Это лучшая идея, которую я получил на данный момент (выделено как не очень оптимальное решение в параграфе выше), любые улучшения будут высоко оценены:
dyn_arr & dyn_arr::operator=(dyn_arr const & rhs) {
if (reallocate(rhs.length)) // this does not destroy data on bad alloc
error |= bad_alloc; // set flag indicating the allocate has failed
else {
size_t i;
for (i = 0; i < rhs.length; ++i) // coppy the array
arr[i] = rhs.arr[i]; // assume this wont throw an exceptions and it wont fail
}
return *this;
}
затем позвонить:
dyn_arr a = b;
if (a.error)
// handle it...
Я не скомпилировал это, поэтому могут быть опечатки, но, надеюсь, вы поняли идею.
Здесь есть две отдельные проблемы.
Первый связан с перегрузкой операторов. Как отмечает CashCow, перегруженные операторы в C ++ являются просто синтаксическим сахаром для вызовов функций. В частности, операторы не обязательно, чтобы return *this
, Это просто соглашение по программированию, созданное с целью облегчить цепочку операторов.
Теперь цепочки назначение операторы (a = b = c = ...
) довольно сложный случай в приложениях C ++. Так что возможно, что вам лучше, явно запретив пользователям вашего dyn_arr
класс для когда-либо цепочки операторов присваивания. Это даст вам свободу вместо того, чтобы возвращать код ошибки от оператора, как из обычной функции:
error_t operator = (dyn_arr const & rhs) {
void *mem = realloc(...);
if (mem == NULL) {
return ERR_BAD_ALLOC; // memory allocation failed
}
...
return ERR_SUCCESS; // all ok
}
И тогда в коде вызывающего абонента:
dyn_arr a, b;
if ((a = b) != ERR_SUCCESS) {
// handle error
}
Вторая проблема связана с реальным примером, который вы приводите:
dyn_arr a = b;
Этот пример будет НЕ позвоните перегруженному оператору присвоения! Вместо этого это означает «построить dyn_arr
объект a
с b
в качестве аргумента для конструктора «. Таким образом, эта строка фактически вызывает конструктор копирования dyn_arr
, Если вам интересно понять почему, подумайте об эффективности. Если семантика этой строки включает в себя вызов оператора присваивания, система времени выполнения должна сделать две вещи в результате этой строки: конструкция a
с некоторым состоянием по умолчанию, а затем немедленно уничтожить это состояние, назначив a
Штат b
, Вместо этого достаточно сделать одну вещь — вызвать конструкцию копирования. (И приводит к той же семантике, предполагая любые разумные реализации конструктора копирования и оператора присваивания.)
К сожалению, вы правы, признавая, что с этой проблемой трудно разобраться. Кажется, не существует действительно элегантного способа обработки сбоев в конструкторе, кроме генерации исключения. Если вы не можете сделать это, либо:
Для получения дополнительной информации см. Как справиться с ошибкой в конструкторе в C ++?
Перегрузка операторов не имеет ничего общего с исключениями, она просто позволяет вызывать «функцию» посредством использования операторов.
например если бы вы писали свой собственный вектор, вы могли бы реализовать + для объединения двух векторов или добавления одного элемента в вектор (в качестве псевдонима для push_back()
)
Конечно, любая операция, требующая выделения большего объема памяти, может закончиться (и вы получите bad_alloc
и нужно управлять этим, если вы не можете его выбросить, устанавливая какое-то состояние ошибки).