如何在c++中返回NULL对象

How to return NULL object in C++

本文关键字:返回 NULL 对象 c++      更新时间:2023-10-16

我知道这可能是:Return a " 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 temporaryconversion 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'

我还尝试了这个:cpp文件中的Normal Sphere::*hit(Ray ray)和头文件中的Normal *hit( Ray ray);,我得到第二个返回语句的错误:cannot convert 'Normal*' to 'Normal Sphere::*' in return

进一步澄清:我不是在问指针是如何工作的。(这不是主要问题。)我想知道关于c++中指针的语法。因此,对于我上面指定的函数,我已经收集到我应该指定一个返回指针,因为c++没有空对象。明白了。但是,问题就变成了:函数原型应该是什么样子?在cpp文件中,我有Bala建议的内容(这是我最初的内容,但由于以下错误而更改了它):

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);
};

有谁知道为什么它抱怨Sphere类中没有hit的匹配原型吗?(我可能会把这个移到一个单独的问题…)

我想你需要一些像

Normal* Sphere::hit(Ray ray) {
   //stuff is done here
   if(something happens) {
       return NULL;
   }
   //other stuff
   return new Normal(something, somethingElse);
}

有几种相当标准的方法可以做到这一点。对于不同的方法有不同的权衡,我不打算在这里展开。

方法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>(使用boost或类似)

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 ...
  }
}

如果您使用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);
}
boost:: optional<

;T>是一个包装器类,它包含T的一个实例或boost::none (boost::none_t的一个实例)。

参见http://www.boost.org/doc/libs/1_47_0/libs/optional/doc/html/index.html了解更多详细信息。

在c++中没有"null对象"这种东西。不过也有空指针。您可以实现您设计的一个特殊对象,您在逻辑上将其视为"null",但它不是c++语言的一部分。

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;
};

但是,我的代码有些不同,因为星号不能解决我的问题,这是:

从你的问题看来,你期望在类名之后简单地添加*将解决你的问题。然而,这种期望来自于缺乏对指针是什么、何时使用指针以及类型系统的重要性的理解。因此,这个答案的其余部分有望澄清这些观点。

首先,c++是一种强类型语言。这意味着当将一个变量分配给另一个变量时,两个变量必须具有相同的类型。例如,假设下面的代码A和B是没有定义成员的基本类:

A a;
B b;
a = b; //compiler error here due to type mismatch

这是因为ab是不同的类型。

现在假设您使用*创建了一个指针。这也是一个不同类型的变量:

A a;
A* p_a;
a = p_a; //compiler error due to type mismatch

p_aa不是同一类型的变量。

现在错误:

请求将' int '类型转换为' Normal '非标量类型

生成

是因为表达式:

return NULL

的类型是int(你会发现,如果你查找它的#定义为0),但你的函数的返回类型是Normal

要解决这个问题,您必须将函数返回类型更改为pointer to a Normal对象。

Normal* Sphere::hit(Ray ray)

并返回一个pointer to a Normal对象:

return new Normal(something, somethingElse); 

然而,在这个阶段,尽管编译器错误应该得到解决,但您现在需要关注管理动态分配的内存。我不能在这篇文章中讨论这个问题,所以我就这样吧。

更新:

这是回答你问题的第二部分。头文件中的类声明应该以;结束

您不应该返回NULL,这是int类型的零常量,而是返回由无参数构造函数构造的Normal类的空实例,通常。

返回Normal();

一般来说,最好另外定义isEmpty()方法。

这样你就可以像这样检查Null实例:

if(obj_of_class_Normal.isEmpty())
       //do something. 

我曾经面对过这类问题。如果需要,对于非限定条件,可以只返回具有所需最低/默认值的normal对象,例如:

Normal Sphere::hit(Ray ray) {
   //stuff is done here
   if(something happens) {
       return Normal(0,0); //here I take the example by constructing the 'normal' object with all value zeros
   }
   //other stuff
   return Normal(something, somethingElse);
}

因为你的函数的返回类型是Normal,你可以只传递一个类型为Normal的最低值的对象,如例子0或其他,然后你可以进一步处理当函数的堆栈终止。