Как вернуть объект NULL в С++
Я знаю, что это может быть дубликат: Возврат "NULL" объект, если результат поиска не найден
НО, что-то другое происходит с моим кодом, потому что звездочка не решает мою проблему, а именно:
Normal Sphere::hit(Ray ray) {
//stuff is done here
if(something happens) {
return NULL;
}
//other stuff
return Normal(something, somethingElse);
}
Но я получаю сообщение об ошибке в строке return NULL
: conversion from ‘int’ to non-scalar type ‘Normal’ requested
И еще одна ошибка и предупреждение, ссылающиеся на последнюю обратную линию: warning: taking address of temporary
и conversion from ‘Normal*’ to non-scalar type 'Normal' requested
Я понимаю, почему я получаю это предупреждение, но я не знаю, как это исправить. Как вернуть объект Normal
в последней строке, которая сохраняется после завершения функции и как я могу вернуть объект NULL
в первый раз? (Если у вас есть термин для этих типов возвратов, пожалуйста, дайте мне знать, чтобы я мог также прочитать об этом больше.)
Чтобы прояснить вопрос комментатора, я пробовал следующие вещи:
Я попытался сделать это: Normal *Sphere::hit(Ray ray)
в файле cpp и Normal *hit( Ray ray );
в файле заголовка, и я получаю эту ошибку: error: prototype for ‘Normal* Sphere::hit(Ray)’ does not match any in class 'Sphere'
Я также пробовал это: Normal Sphere::*hit(Ray ray)
в файле cpp и Normal *hit( Ray ray);
в файле заголовка, и я получаю эту ошибку для второго оператора return: cannot convert 'Normal*' to 'Normal Sphere::*' in return
Дальнейшие разъяснения: Я не спрашиваю, как работают указатели. (Это был не главный вопрос.) Мне интересно, как синтаксис относительно указателей в С++. Итак, учитывая указанную выше функцию, я понял, что должен указывать возвращаемый указатель, потому что у С++ нет нулевых объектов. Понял. НО, тогда возникает проблема: как должен выглядеть прототип функции? В файле cpp у меня есть то, что предложил Бала (это то, что я изначально, но изменил его из-за следующей ошибки):
Normal* Sphere::hit(Ray ray) {
//stuff is done here
if(something happens) {
return NULL;
}
//other stuff
return new Normal(something, somethingElse);
}
В файле заголовка у меня есть Normal *hit(Ray ray)
, но я все еще получаю это сообщение: prototype for 'Normal* Sphere::hit(Ray)' does not match any in class 'Sphere'
В этот момент мне непонятно, почему он не может найти этот прототип функции. Вот заголовочный файл:
class Sphere
{
public:
Sphere();
Vector3 center;
float radius;
Normal* hit(Ray ray);
};
Может ли кто-нибудь понять, почему он жалуется, что не существует подходящего прототипа для hit
в классе Sphere
? (Я мог бы переместить это на отдельный вопрос...)
Ответы
Ответ 1
Я думаю, вам нужно что-то вроде
Normal* Sphere::hit(Ray ray) {
//stuff is done here
if(something happens) {
return NULL;
}
//other stuff
return new Normal(something, somethingElse);
}
чтобы иметь возможность возвращать NULL;
Ответ 2
Есть несколько довольно стандартных способов сделать это. Существуют различные компромиссы для методов, которые я не буду здесь обсуждать.
Способ 1. Создайте исключение при сбое.
Normal Sphere::hit(Ray ray)
{
//stuff is done here
if(something happens) {
throw InvalidIntersection;
}
//other stuff
return Normal(something, somethingElse);
}
void example(Ray r)
{
try {
Normal n = s.hit(r);
... SUCCESS CASE ...
}
catch( InvalidIntersection& )
{
... FAILURE CASE ...
}
}
Метод 2 возвращает указатель на вновь выделенный объект. (Вы также можете использовать умные указатели или auto_ptrs, чтобы сделать это немного лучше).
Normal* Sphere::hit(Ray ray)
{
//stuff is done here
if(something happens) {
return NULL
}
//other stuff
return new Normal(something, somethingElse);
}
void example(Ray ray)
{
Normal * n = s.hit(ray);
if(!n) {
... FAILURE CASE ...
} else {
... SUCCESS CASE ...
delete n;
}
}
Метод 3 - обновить существующий объект. (Вы можете передать ссылку, но я использую соглашение, что любой выходной параметр передается по указателю).
bool Sphere::hit(Ray ray, Normal* n)
{
//stuff is done here
if(something happens) {
return false
}
//other stuff
if(n) *n = Normal(something, somethingElse);
return true;
}
void example(Ray ray)
{
Normal n;
if( s.hit(ray, &n) ) {
... SUCCESS CASE ...
} else {
... FAILURE CASE ...
}
}
Метод 4: вернуть optional<Normal>
(используя повышение или аналогичное)
optional<Normal> Sphere::hit(Ray ray)
{
//stuff is done here
if(something happens) {
return optional<Normal>();
}
//other stuff
return optional<Normal>(Normal(something, somethingElse));
}
void example(Ray ray)
{
optional<Normal> n = s.hit(ray);
if( n ) {
... SUCCESS CASE (use *n)...
} else {
... FAILURE CASE ...
}
}
Ответ 3
Если вы используете библиотеки Boost, вы можете использовать boost:: optional. Это дает вам нечто, близкое к нулевому значению:
boost::optional<Normal> Sphere::hit(Ray ray) {
//stuff is done here
if(something happens) {
return boost::none;
}
//other stuff
return Normal(something, somethingElse);
}
подталкивание:: опционально <T> - это класс оболочки, содержащий либо экземпляр T, либо boost:: none (экземпляр boost:: none_t).
Подробнее см. http://www.boost.org/doc/libs/1_47_0/libs/optional/doc/html/index.html.
Ответ 4
В С++ нет такой вещи, как "нулевой объект". Однако есть нулевые указатели. Вы можете реализовать специальный объект вашего дизайна, который вы логически считаете "нулевым", но не являетесь частью языка С++.
Ответ 5
Возвращаемое значение NULL будет действительным только в том случае, если вы возвращали указатель на объект Normal, NULL представляет нулевой указатель, а не нулевой объект.
Что бы я делал в этом случае, это определить 'null' или недопустимое состояние для этого объекта. Поскольку вы работаете с нормалями поверхности, вы можете считать нормальный с длиной == 0 недопустимым состоянием, поэтому тогда вы сделаете следующее:
Normal Sphere::hit(Ray ray) {
//stuff is done here
if(something happens) {
return Normal();
}
//other stuff
return Normal(something, somethingElse);
}
Тогда ваш обычный класс будет иметь что-то вроде этого:
class Normal {
public:
Normal() : x(0), y(0), z(0), len(0) {}
// ... other constructors here ...
bool isValid() const { return (len != 0) };
// ... other methods here ...
private:
float x, y, z, len;
};
Ответ 6
НО, что-то другое происходит с моим кодом, потому что Звездочка не решает мою проблему, вот что:
Кажется, что из вашего вопроса вы ожидаете, что просто добавив *
после того, как имя класса решит ваши проблемы. Однако такое ожидание происходит из-за недостаточного понимания того, что такое указатель, когда использовать указатели и важность системы типов. Поэтому оставшаяся часть этого ответа, мы надеемся, прояснит эти моменты.
Во-первых, С++ является строго типизированным lanuage. Это означает, что при присвоении одной переменной другой переменной 2 переменные должны быть одного типа. Например, предположим, что в приведенном ниже коде A и B являются базовыми классами без определенных членов:
A a;
B b;
a = b; //compiler error here due to type mismatch
Это связано с тем, что a
и b
являются разными типами.
Теперь скажите, что вы создали указатель, используя *
. Это также другой тип переменной:
A a;
A* p_a;
a = p_a; //compiler error due to type mismatch
p_a
не является тем же типом переменной, что и a
.
Теперь ошибка:
преобразование из 'int в нескалярный тип' Нормальный запрошенный
создается потому, что выражение:
return NULL
имеет тип int
(вы найдете, если вы посмотрите его #defined как 0), но возвращаемый тип вашей функции - Normal
.
Чтобы решить эту проблему, вы должны изменить возвращаемый тип функции как объект pointer to a Normal
.
Normal* Sphere::hit(Ray ray)
и вернуть объект pointer to a Normal
:
return new Normal(something, somethingElse);
Однако на этом этапе, хотя ошибки компилятора должны быть устранены, теперь у вас есть проблема управления динамически распределенной памятью. Я не могу покрыть это в этом посте, поэтому я оставлю его так, как есть.
Обновление:
Это касается второй части вашего вопроса. Ваше объявление класса в файле заголовка должно быть завершено с помощью ;
.
Ответ 7
Вы не должны возвращать NULL
, который является нулевой константой типа int
, но вместо этого возвращать пустой экземпляр класса Normal
, созданный конструктором no arg, обычно.
Так что вернись Normal();
Как правило, рекомендуется дополнительно определять метод isEmpty()
.
Таким образом, вы можете проверить наличие экземпляра Null
следующим образом:
if(obj_of_class_Normal.isEmpty())
//do something.