I know that this might be a duplicate of: Return a "NULL" object if search result not found
BUT, there's something different going on with my code because the asterisk doesn't solve my problem, which is this:
Normal Sphere::hit(Ray ray) {
//stuff is done here
if(something happens) {
return NULL;
}
//other stuff
return Normal(something, somethingElse);
}
But I get an error referencing the return NULL
line: conversion from ‘int’ to non-scalar type ‘Normal’ requested
And another error and warning that referencing the last return line: warning: taking address of temporary
and conversion from ‘Normal*’ to non-scalar type 'Normal' requested
I understand why I am getting this warning, but I don't know how to fix it. How do I return a Normal
object in the last line that persists after the function ends and how do I return a NULL
object that first time? (If th开发者_运维问答ere's a term for these types of returns, please let me know so I can also read up on it more.)
To clarify a commenter's question, I've tried these things:
I tried doing this: Normal *Sphere::hit(Ray ray)
in the cpp file and Normal *hit( Ray ray );
in the header file and I get this error: error: prototype for ‘Normal* Sphere::hit(Ray)’ does not match any in class 'Sphere'
I also tried this: Normal Sphere::*hit(Ray ray)
in the cpp file and Normal *hit( Ray ray);
in the header file and I get this error for the second return statement: cannot convert 'Normal*' to 'Normal Sphere::*' in return
Further clarification: I'm not asking about how pointers work. (That wasn't the main question.) I'm wondering about syntax regarding pointers in C++. So, given the function I've specified above, I've gleaned that I should specify a return a pointer because C++ doesn't have null objects. Got it. BUT, the problem then becomes: what should the function prototype look like? In the cpp file, I have what Bala suggested (which is what I had originally but changed it because of the following error):
Normal* Sphere::hit(Ray ray) {
//stuff is done here
if(something happens) {
return NULL;
}
//other stuff
return new Normal(something, somethingElse);
}
In the header file, I have Normal *hit(Ray ray)
, but I still get this message: prototype for 'Normal* Sphere::hit(Ray)' does not match any in class 'Sphere'
At this point, it is unclear to me why it can't find that function prototype. Here is the header file:
class Sphere
{
public:
Sphere();
Vector3 center;
float radius;
Normal* hit(Ray ray);
};
Can anyone see why it's complaining that there doesn't exist a matching prototype for hit
in the Sphere
class? (I might move this to a separate question...)
I think you need something like
Normal* Sphere::hit(Ray ray) {
//stuff is done here
if(something happens) {
return NULL;
}
//other stuff
return new Normal(something, somethingElse);
}
to be able to return NULL;
There are several fairly standard ways of doing this. There are different tradeoffs for the methods, which I'm not going to go into here.
Method 1: Throw an exception on failure.
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 ...
}
}
Method 2 return a pointer to a newly allocated object. (You could also use smart pointers, or auto_ptrs to make this a little neater).
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;
}
}
Method 3 is to update an existing object. (You could pass a reference, but a convention I use is that any output parameter is passed by pointer).
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 ...
}
}
Method 4: Return an optional<Normal>
(using boost or similar)
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 ...
}
}
If you use the Boost libraries, then you can use boost::optional. That gives you something that is pretty close to a null value:
boost::optional<Normal> Sphere::hit(Ray ray) {
//stuff is done here
if(something happens) {
return boost::none;
}
//other stuff
return Normal(something, somethingElse);
}
boost::optional< T> is a wrapper class that contains either an instance of T or boost::none (an instance of boost::none_t).
See http://www.boost.org/doc/libs/1_47_0/libs/optional/doc/html/index.html for more details.
In C++ there's no such thing as a "null object". There are null pointers though. You can implement a special object of your design that you logically treat as "null" but it's not part of the C++ language.
The NULL return value would only be valid if you were returning a pointer to a Normal object, NULL represents a null pointer, not a null object.
What I would do in this case is define a 'null' or invalid state for this object. Since you are working with surface normals, you can consider a normal with length == 0 an invalid state, so then you would do this:
Normal Sphere::hit(Ray ray) {
//stuff is done here
if(something happens) {
return Normal();
}
//other stuff
return Normal(something, somethingElse);
}
Then your normal class would have something like this:
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;
};
BUT, there's something different going on with my code because the asterisk doesn't solve my problem, which is this:
It seems that from your question you expect that simply adding a *
after the class name will solve your problems. However this sort of expectation comes from a lack of understanding what a pointer is, when to use pointers and the importance of the type system. So the remainder of this answer will hopefully clarify these points.
Firstly the C++ is a strongly typed lanuage. This means that when assigning one variable to another that 2 variables in question have to be of the same type. For example assume that in the code below A and B are basic classes with no members defined:
A a;
B b;
a = b; //compiler error here due to type mismatch
This is because a
and b
are different types.
Now say you created a pointer by using the *
. This is also a different type of variable:
A a;
A* p_a;
a = p_a; //compiler error due to type mismatch
p_a
is not the same type of variable as a
.
Now the error:
conversion from ‘int’ to non-scalar type ‘Normal’ requested
is generated because the expression:
return NULL
is of type int
(you'll find if you look it up it's #defined as 0) but the return type of your function is Normal
.
To resolve this you have to change the function return type to be a pointer to a Normal
object.
Normal* Sphere::hit(Ray ray)
and return a pointer to a Normal
object:
return new Normal(something, somethingElse);
However at this stage although compiler errors should be resolved, you now have the concern of managing the dynamically allocated memory. I can't cover that in this post so I'll leave it like it is.
Update:
This is to address the 2nd part in your question. Your class declaration in your header file should be terminated with a ;
.
You shouldn't return NULL
, which is a zero constant of type int
, but instead, return an empty instance of class Normal
constructed by the no arg constructor, usually.
So return Normal();
Generally, it is good practice to additionally have the method isEmpty()
defined.
That way you could check for a Null
instance like so:
if(obj_of_class_Normal.isEmpty())
//do something.
精彩评论