Форум программистов, компьютерный форум CyberForum.ru

Возвращение пустого объекта. - C++

Восстановить пароль Регистрация
 
greshnikk
 Аватар для greshnikk
30 / 30 / 0
Регистрация: 05.11.2008
Сообщений: 162
04.11.2011, 21:06     Возвращение пустого объекта. #1
Описан метод поиска структуры по заданным параметрам, который, в случае нахождения этой структуры, в базе данных, возвращает ее в качестве параметра:
C++
1
command searchCommand(...)
Вопрос состоит в следующем: Как корректно и наиболее эстетично обрабатывать отсутствие заданной структуры в базе данных ?
Был опробован способ
C++
1
return NULL;
,
но, к сожалению безуспешно. Ошибка компилятора С2440 о невозможности конвертации возвращаемого значения из int в command.
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Bers
Заблокирован
04.11.2011, 22:39     Возвращение пустого объекта. #2
Цитата Сообщение от greshnikk Посмотреть сообщение
Вопрос состоит в следующем: Как корректно и наиболее эстетично обрабатывать отсутствие заданной структуры в базе данных ?
Был опробован способ
Процедура поиска по смыслу должна вернуть объект, либо особый сигнал, что объект не найден.
Как это можно организовать?

1. Возвращать не сам объект, а указатель на него (помимо прочего, это избавит от ненужного копирования). Тогда в случае, если объект не найден, можно будит вернуть NULL

2. Если так сильно нужно возвращать именно объект (а не указатель), то можно возвращать ссылку на объект, а не сам объект (это позволит избежать лишнего копирования)

В этом случае, если объект не найдён, возвращается ссылка на "нулевой_объект", что будит расцениваться вызывающей стороной, как "объект не найден".

Что такое "нулевой_объект"? Как правило, это объект, сконструированный с помощью конструктора по умолчанию.

Если "объект, символизирующий отсутствие объекта" нужно как то особенно выделить - можно условится, что допустим объект "команда", у которого поле такое то содержит такое то значение - сигнализирует о том, что это "нулевой_объект" и его "как бы не существует".

Имхо, 1 вариант лучше, а 2й переусложнный и костыльный.
CheshireCat
Эксперт С++
2907 / 1235 / 78
Регистрация: 27.05.2008
Сообщений: 3,307
04.11.2011, 23:00     Возвращение пустого объекта. #3
Если прототип функции описан именно так и никак иначе (ну, например, специфицирован внешним заказчиком.... etc) - то логичное решение я вижу только одно:
1. если требуемый объект найден, возвращаем именно его,
2. если требуемый объект отсутствует, бросаем исключение, а вот поймать его и обработать - уже задача вызывающего кода.

Если же прототип можно менять, то самый простой и оптимальный вариант описан Bers - возвращать не сам объект, а указатель на него.
greshnikk
 Аватар для greshnikk
30 / 30 / 0
Регистрация: 05.11.2008
Сообщений: 162
04.11.2011, 23:03  [ТС]     Возвращение пустого объекта. #4
Спасибо за ответ.
Второй вариант действительно очень грубый. Придется создавать внутри функции объект и возвращать на него ссылку. Это совершенно не является задачей описанного метода.
Первый вариант действительно корректный и более оптимизированный, из-за отсутствия затрат оперативной памяти на создание дополнительного объекта. Ну и соответственно, при работе с адресами, константа NULL возвращается корректно.
Bers
Заблокирован
04.11.2011, 23:45     Возвращение пустого объекта. #5
Цитата Сообщение от CheshireCat Посмотреть сообщение
2. если требуемый объект отсутствует, бросаем исключение, а вот поймать его и обработать - уже задача вызывающего кода.
Нарушение инкапсуляции detected

Добавлено через 1 минуту
Цитата Сообщение от greshnikk Посмотреть сообщение
Придется создавать внутри функции объект и возвращать на него ссылку.
Так делать нельзя. Нельзя возвращать ссылки на локальные объекты из функции.
Потому что по выходу из функции, её локальные объекты будут уничтожены, и ссылка будит указывать "в никуда".
accept
4838 / 3237 / 165
Регистрация: 10.12.2008
Сообщений: 10,682
05.11.2011, 05:28     Возвращение пустого объекта. #6
C++
1
int searchCommand(command &, ...)
функция возвращает код завершения
найденное значение записывается в переданный указатель
greshnikk
 Аватар для greshnikk
30 / 30 / 0
Регистрация: 05.11.2008
Сообщений: 162
05.11.2011, 12:26  [ТС]     Возвращение пустого объекта. #7
Цитата Сообщение от Greshnikk Посмотреть сообщение
Придется создавать внутри функции объект и возвращать на него ссылку.
Цитата Сообщение от Bers Посмотреть сообщение
Так делать нельзя. Нельзя возвращать ссылки на локальные объекты из функции.
Создание объекта будет производится динамическим путем.

Цитата Сообщение от accept Посмотреть сообщение
функция возвращает код завершения
Ваш вариант мне понравился больше всех. Но у меня возник вопрос о действиях с наличием несоответствия типов. Приходится возвращать
C++
1
(int)command*
.
На мой взгляд, более изящно будет возвращать void* в качестве результата, это избавит от преобразования типов внутри функции.
accept
4838 / 3237 / 165
Регистрация: 10.12.2008
Сообщений: 10,682
05.11.2011, 12:39     Возвращение пустого объекта. #8
не, возвращаться должен код ошибки
а получаемое значение сохраняется в переданный по ссылке указатель

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
 
using namespace std;
 
struct command {
    int b, c;
};
 
int searchCommand(command *&p, int a);
 
int main()
{
    command *p;
    
    if (searchCommand(p, 4) == 0) {
        cout << p->b << " " << p->c << endl;
        delete p;
    }
    if (searchCommand(p, 8) == 0) {
        cout << p->b << " " << p->c << endl;
        delete p;
    }
    
    return 0;
}
 
int searchCommand(command *&p, int a)
{
    command *tmp = new command;
    tmp->b = 1 + a;
    tmp->c = 2 + a;
    p = tmp;
    return 0;
}
Код
[guest@localhost tests]$ .iso++ t.cpp -o t
[guest@localhost tests]$ ./t
5 6
9 10
[guest@localhost tests]$
greshnikk
 Аватар для greshnikk
30 / 30 / 0
Регистрация: 05.11.2008
Сообщений: 162
05.11.2011, 13:30  [ТС]     Возвращение пустого объекта. #9
Цитата Сообщение от accept Посмотреть сообщение
получаемое значение сохраняется в переданный по ссылке указатель
Ваш вариант более приемлем, если существует несколько видов ошибок. В данном, конкретном случае, на мой взгляд, по-скольку может быть только одна ошибка, это отсутствие элемента, то лучше отдавать указатель или NULL, в случае его отсутствия. Для этого не нужно будет передавать переменную для хранения, в качестве параметра.
Bers
Заблокирован
05.11.2011, 15:50     Возвращение пустого объекта. #10
Цитата Сообщение от greshnikk Посмотреть сообщение
Создание объекта будет производится динамическим путем.
Вы похоже не отдаёте себе отчет в том, что именно должна делать процедура поиска.
Процедура поиска ищет объекты. Она их не создаёт. Тем более динамически.


Цитата Сообщение от greshnikk Посмотреть сообщение
Ваш вариант более приемлем, если существует несколько видов ошибок. В данном, конкретном случае, на мой взгляд, по-скольку может быть только одна ошибка, это отсутствие элемента, то лучше отдавать указатель или NULL, в случае его отсутствия. Для этого не нужно будет передавать переменную для хранения, в качестве параметра.
Вариант accept идеологически нужно понимать так:

C++
1
2
3
4
5
6
7
8
9
int Find( TObject& result, const TParam& target); 
 
//вернёт код ошибки, или ноль.
//Если объект не найден - вернётся соотвествующий код ошибки
//Если Параметры поиска некорректные - вернётся соответствующий код ошибки.
//Если завершилась успехом, результатом будит ссылка на найденный объект
//Поиск ведётся согласно параметрам цели.
//Функция не создаёт внутри себя никаких объектов
//И не нуждается в костылях типа "нулевые объекты"
ForEveR
Модератор
Эксперт C++
 Аватар для ForEveR
7927 / 4709 / 318
Регистрация: 24.06.2010
Сообщений: 10,524
Завершенные тесты: 3
05.11.2011, 15:55     Возвращение пустого объекта. #11
Bers,
Нарушение инкапсуляции detected
Это с какой радости?
Bers
Заблокирован
05.11.2011, 16:39     Возвращение пустого объекта. #12
Цитата Сообщение от ForEveR Посмотреть сообщение
Это с какой радости?
Закон об инвариантности класса гласит: класс должен сохранять свою работоспособность независимо от того, насколько хорошо вызывающая сторона осведомлена о работе этого класса.

Это значит, что даже если клиент не читал документацию, и запихал в метод класса самые кривые аргументы, и забил болт на все возвращаемые коды ошибок, класс обязан сохранить работоспособность.

Он может отказаться выполнять тупой приказ, но сохранить работоспособность он обязан.

Работоспособность (считай отказоустойчивость класса, а значит и всей системы в этом конкретном узле нити) не должна зависеть от вызывающей стороны.

Работа над ошибками класса - личные трудности класса, и больше никого другого. Это его персональная ответственность.

То есть, класс вернул код ошибки. Что с этим кодом будит делать вызывающая сторона - это её личные трудности. Класс свою работу сделал - сохранил работоспособность системы, и выполнил требуемую от него процедуру согласно своему контракту ( см документацию класса)

Если класс бросил исключение - считай переложил ответственность за работу над ошибками на вызывающую сторону, которая может ничего не знать о природе возникшей ошибки, и инкапсуляция гарантирует вызывающей стороне, что она не обязана что либо знать, и что либо делать для поддержания работоспособности класса, который использует.

Получается, что если я попытаюсь работать с таким классом, я вынужден буду знать обо всех возможных ситуациях, которые могут привести к аварии. Все они должны быть отражены в документации класса. А класс за свою собственную работу тип того не отвечает.

Это - нарушение инкапсуляции (что бы пользоваться, мне приходится учить как работает класс, и что может привести к ошибкам), и передергивание ответственности (класс должен облегчать мой труд, принимать на себя ответственность за работу некоторых процедур. А не наоборот - на меня это все сваливать. Зачем мне вообще нужен класс, который будит геммороить меня?)

Короче говоря - пример ущербной архитектуры.

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

Правильный класс кидает исключения тогда, и только тогда, когда произошла исключительная ситуация.

Исключительная ситуация - нештатная ситуация, спрогнозировать которую заранее было невозможно. То есть, это ситуация, которая происходит во_время выполнения процедуры, и предотвратить её ещё до_начала выполнения процедуры класс никак не мог.

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

Ну так вот, правильный класс передаёт эту ответственность своей системе, а не клиентам.
Поясню на примере:

Клиент ---- (Ловушка ---- Исполнитель).

Клиент обращается к ловушке, думая что ловушка - это и есть исполнитель (он ничего не знает о внутренностях)

Ловушка переадресует запрос непосредственно Исполнителю. Поскольку Ловушка-Исполнитель части одной системы, то знание Ловушки об особенностях Исполнителя не нарушают инкапсуляцию. Её просто нету на таком низком уровне.

Таким образом, Ловушку разрабатывают те же люди, которые разрабатывали Исполнителя, и она знает обо всех возможных исключения, которые может выбросить Исполнитель. Задача Ловушки - выловить эти исключения, и предотвратить аварию. Вернуть пошатнувшуюся работу узла обратно в штатный режим.

Если Ловушке это удастся, то Клиенты даже подозревать не будут о том, что система только что была на волоске.

Если Ловушке это не удастся, она может принять решение об аварийной остановке всей системы, и не допустить порчи/потери каких либо ценных данных.
В любом случае, она сделала все что могла зная внутреннее устройство своего Исполнителя.

Во всяком случае, если ловушке не удалось предотвратить аварию полностью, то клиентам этого не удастся тем более. Но так, у системы хотя бы будит шанс.

Ловушки не нужны для ситуаций, когда Исполнитель самостоятельно в состоянии спрогнозировать, сможет ли он со 100% вероятностью выполнить операцию успешно.

Они нужны только в тех ключевых моментах, когда велик риск, что процедура провалится по независящим от Исполнителя причинам (например, new бросила исключение, или физически место на диске закончилось, как раз в тот момент, когда надо записывать, и тп)

Ситуация, когда объект не найден - вообще не является исключительной, а является нормальной частью штатной работы. Городить ловушки тут - излишнее.
ForEveR
Модератор
Эксперт C++
 Аватар для ForEveR
7927 / 4709 / 318
Регистрация: 24.06.2010
Сообщений: 10,524
Завершенные тесты: 3
05.11.2011, 17:17     Возвращение пустого объекта. #13
Bers, Все это хорошо конечно. Но есть гарантии. Строгая, базовая и гарантия отсутствия.

Так вот. STL кидает исключения при неправильном использовании класса. boost тоже. Плохая архитектура? В бусте? В stl? Уверен, что нет.

Но на тему отсутствия объекта вообщем-то согласен.
Bers
Заблокирован
05.11.2011, 18:37     Возвращение пустого объекта. #14
Цитата Сообщение от ForEveR Посмотреть сообщение
STL кидает исключения при неправильном использовании класса.
Сравнение с STL не корректное. И вот почему:

Есть условно есть 3 типа ошибок:

- штатные (их появление не способно навредить системе, а обработка - нормальная часть работы исполнителя. Более того, исполнитель априори ожидает возникновения штатных ошибок, и легко без посторонней помощи способен адекватно на них реагировать).
Пример: кривые входные аргументы метода.

- исключительные ситуации (приводят к аварийной ситуации по независящим от исполнителя причинам)
Пример: В момент записи физически закончилось место на диске.

- программные ошибки:
- программные ошибки управляющей логики клиентского кода.
- программные ошибки управляющей логики самого исполнителя.

Главное свойство программных ошибок: их быть не должно Их в принципе не должно быть.

Понятно, что сам класс никак не рассчитан на то, что бы "успешно работать" в условиях, когда он "забагован". И никак изнутри себя не сможет адекватно отреагировать на собственную ущербность. Баг может создать аварийную ситуацию.

Программные ошибки как правило вызывают аварийные ситуации, либо порчу данных (что ещё хуже, и именно по этой причине STL услужливо тыкает программистов носом в такие ошибки)

Нужно различать, какие аварийные ситуации вызваны исключительными ситуациями, а какие - программными ошибками.

Если разработчик класса (системы) может предположить вероятность, что места на жестком диске не хватит во время записи, и предусмотреть Ловушку данной исключительной ситуации, то соответственно Ловушка такую проблему разрулит.

Но выход за пределы массива - красноречивая и однозначная ошибка самого этого программиста.
Ставить Ловушки на собственный быдлокод... это не очень продуктивная техника работы над ошибками. Гораздо эффективнее не допускать ошибок, а не приляпывать заплатки, пытаясь удержать на плаву систему, которая разваливается тупо по халатности программиста.
Тем не менее, программные ошибки случаются, и их тоже приходится учитывать.

В режиме отладки, Ловушка, отловившая "баг" в исполнителе, должна громко-громко начать ругаться матом, и требовать к пульту программиста, который накосячил.
По сути, такая ловушка - всего лишь подстраховка самого программиста от собственных глупостей. (Лично я в таких случаях вместо исключений, пользуюсь густо-густо намазанными ассертами)

В боевом же режиме, система должна проявлять чудеса живучести.
Ловушка должна пофиксить баг, отправить разработчикам репорт, но при этом сделать все, что бы "теоретически не рабочий модуль все равно хоть как то, но сработал". Например - откатить операцию назад, и попробовать перезапустить модуль (вдруг ошибка не всегда проявляется, и повторная попытка завершится удачей).

STL кидает исключения в двух типовых ситуациях:

- программные ошибки управляющей логики клиентского кода (STL уведомляет клиентов, что они написали косячный код. Она говорит клиентам: "вы накосячили, вы и разруливайте".
При этом, STL писалась для массового потребителя, и по дефолту полагает, что выход за пределы массива - программная ошибка (а вовсе не штатная, как может предположить конкретный программист-пользователь). Этот момент отражен в документации по STL, и мотивирован тем, что в 90% всех случаев выход за пределы массива - действительно программная ошибка, а не задумка автора кода.

Так же, STL услужливо сообщает клиенту, что он накосячил (а не пытается замалчивать проблему, проявляя чудеса живучести), что позволяет разработчикам ещё на этапе разработки и тестирования отлавливать все подобные ошибки.

И правильно делает. Исполнитель никак не может нести ответственности за программные ошибки клиента. Таких вещей, как выход за пределы массива быть не должно никогда, и ни за что.
STL напротив оказывает большую услугу программистам клиентского кода, тыкая их носом в их ошибки. Что бы они знали, и смогли исправить.

-исключительная ситуация. Она говорит клиентам "произошёл сбой по независящим от меня причинам. Я ничего не могу с этим сделать".

Здесь, сам по себе контейнер насколько мог, настолько удерживал инвариант своего класса.
Скажем так, он сделал все, что мог сделать.

По опыту скажу, что... у меня ни разу не падал контейнер на пустом месте. Если он и падал - то это всегда были либо программные ошибки вызывающей стороны, либо особенности объектов, которые хранил внутри контейнер (они сбоили, и рекошетило на сам контейнер. Но контейнер и не должен нести ответственность за нарушения инвариантов объектов, которые он содержит)

Как видите STL в этом смысле грамотная и адекватная.

И потом, поддержка инварианта класса задача не простая. И требует ресурсов. STL же ориентирована на производительность: уведомляет о проблемах (не пытаясь замалчивать, и проявлять чудеса живучести), а именно об исключительных ситуациях, и об ошибках самих программистов. Штатная работа контейнеров STL стабильна на все 100%
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
05.11.2011, 19:17     Возвращение пустого объекта.
Еще ссылки по теме:

Возвращение из функции динамически созданного объекта C++
C++ Возвращение функцией объекта
Возвращение временного объекта из метода C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
greshnikk
 Аватар для greshnikk
30 / 30 / 0
Регистрация: 05.11.2008
Сообщений: 162
05.11.2011, 19:17  [ТС]     Возвращение пустого объекта. #15
1.
Цитата Сообщение от Bers Посмотреть сообщение
Вы похоже не отдаёте себе отчет в том, что именно должна делать процедура поиска.
Процедура поиска ищет объекты. Она их не создаёт. Тем более динамически.
Скорее всего Вы невнимательно читаете тему, указывая мне на то, на что я сам указывал -
Цитата Сообщение от Greshnikk Посмотреть сообщение
Второй вариант действительно очень грубый. Придется создавать внутри функции объект и возвращать на него ссылку. Это совершенно не является задачей описанного метода.
2. Мой ответ являлся опровержением Вашего сообщения -
Цитата Сообщение от Bers Посмотреть сообщение
Так делать нельзя.
Я привел пример, что такой вариант возможен. Я не писал, что решил использовать этот метод. Более того, я написал, что вариант пользователя accept мне больше всех подходит
Цитата Сообщение от greshnikk Посмотреть сообщение
Ваш вариант мне понравился больше всех.
(с моей правкой на тип возвращаемого значения).
3.
Цитата Сообщение от Bers Посмотреть сообщение
Вариант accept идеологически нужно понимать так
Я все именно так и понял. Но ввиду того, что ошибка у меня может быть одна единственная, решил не передавать лишнюю переменную в метод. Отлавливать ошибку буду по возвращаемому значению NULL.
4. Попрошу Вас и пользователя ForEveR обсуждать тему нарушения инкапсуляции в отдельном топике. Тема данного топика отличается от обсуждаемой вами.
Yandex
Объявления
05.11.2011, 19:17     Возвращение пустого объекта.
Ответ Создать тему

Метки
null, возвращаемое значение, пустой объект
Опции темы

Текущее время: 03:53. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru