bool is_something_ok(int param,SomeStruct* p)
{
bool is_ok = false;
// check if is_ok
if(is_ok)
// set p to some valid value
else
// set p to NULL
return is_ok;
}
эта функция возвращает true и установила p на допустимое значение, если "что-то в порядке", иначе возвращают false и устанавливают p в NULL
Это - хороший или плохой дизайн? лично, я чувствую себя неловко, когда я использую его. Если нет никакого документа и комментария, я действительно, Дон знает, как использовать его.
BTW:Is там некоторая авторитетная книга/статья о дизайне API?
Поскольку вы пометили вопрос как C ++, а не C, я предлагаю вам:
Но это лишь некоторые общие подсказки. Лучший способ всегда зависит от конкретной проблемы ...
Я склонен это делать. Альтернативой в примере является либо кодирование двух вещей в одно возвращаемое значение (например, использование NULL в качестве специального значения), либо возврат структуры.
Кодирование двух вещей иногда может быть невозможным и немного подвержено ошибкам. Возвращение конструкции — это много дополнительной работы и беспорядка. Поэтому я склонен делать то, что сделали вы. Я склонен предполагать, что «сырые» указатели и ссылки в списке параметров предназначены для возврата значений, и они были бы «const», если бы они были только для передачи данных.
Но, честно говоря, я забываю это правило так часто, как я его помню, поэтому, возможно, оно не очень хорошее.
В библиотеке boost есть класс «необязательный», который может соответствовать вашим потребностям, но мне самому он никогда не нравился, возможно, без очень веской причины.
Я бы сказал, это зависит от обстоятельств. Насколько дорого стоит копировать конструкцию вашего типа? Можете ли вы написать свою функцию RVO -дружелюбной? По крайней мере, пока у нас не будет C ++ 0x с ссылками на rvalue , я бы рекомендовал не возвращать «дорогие» типы (такие как std :: vector
). , а лучше передать их как ссылки, например используйте:
void split(const std::string &txt, char sep, std::vector<std::string> &out);
, а не:
std::vector<std::string> split(const std::string &txt, char sep);
В зависимости от того, как вы пишете свою функцию, возможно, сработает RVO, но, по моему опыту, это не то, на что вы обычно можете положиться.
Я думаю вернуть NULL, если что-то не в порядке, и действительный SomeStruct, если в этом случае лучше
SomeStruct* is_something_ok(int param);
В этом случае, кроме проверки логического значения, вы должны проверить, является ли оно NULL, и если не использовать его.
Но бывают случаи, когда нужно возвращать значение по параметру. Это зависит от количества возвращаемых значений, и можно использовать контекстную функцию.
Зависит от того, как вы хотите обрабатывать «ошибки».
Например, возьмем стандартную функцию atoi. Он преобразует строку в целое число, но если строка не содержит числа, что она должна вернуть? В этом случае среда выполнения C / C ++ установит глобальную переменную errno. Альтернативой было бы создание исключения.
Лично мне не нравятся обе эти альтернативы. Поэтому, если я обычно использую следующие правила:
Вы можете сделать следующее:
bool is_something_ok(int param,SomeStruct* p);
или
// return NULL if the operation failed.
Somestruct* do_work(int param);
Трудно сказать, хорош или плох дизайн/API, нет ничего черного или белого ... (серый?!?!?)
Вы должны выбрать API/стандарт, с которым вам будет проще кодить. И будьте последовательны, если вы выбрали первый тип метода, делайте это и для остальной части вашего проекта.
Не забывайте также документировать свой код, так будет проще понять, как использовать ваш API.
Вам стоит обратить внимание на boost::optional
когда вы хотите возвращать значение только при успехе.
Я бы предложил возвращать непосредственно Result-Type, как например:
SomeStruct doSomething(int param) {...}
и бросать исключение в случаях, когда функция не может справиться (tux21b уже упоминал этот способ). В качестве альтернативы можно возвращать два типа с помощью std::pair
без выброса исключения, как например:
pair<SomeStruct, bool> doSomething(int param) {...}
И третье, мне нравится объявлять выходные параметры как указатели, а не как ссылки (как вы упомянули), потому что в вызывающем коде я вижу разницу между входными и выходными параметрами. Дана функция:
void doSomething(const Somestruct& in, Somestruct* out) {...}
Тогда в вызывающем коде видно (не глядя на объявление функции), что является входным, а что выходным параметром (если я правильно применяю эту концепцию).
SomeStruct a;
SomeStruct b;
doSomething(a, &b); // you see a is input, b is output