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

时间:2011-09-15 02:31:00

标签: c++

我知道这可能与以下内容重复:Return a "NULL" object if search result not found

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

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对象? (如果这些类型的退货有一个术语,请告诉我,以便我也可以阅读更多内容。)

为了澄清评论者的问题,我尝试过这些事情:

我尝试过这样做:cpp文件中的Normal *Sphere::hit(Ray ray)和头文件中的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);我得到了第二个return语句的错误:cannot convert 'Normal*' to 'Normal Sphere::*' in return

进一步澄清:我不是在询问指针是如何工作的。 (这不是主要问题。)我想知道关于C ++指针的语法。所以,考虑到我上面指定的函数,我已经收集到了我应该指定一个返回指针,因为C ++没有null对象。得到它了。但是,问题就变成了:函数原型应该是什么样的?在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);
};

任何人都可以看到它为什么抱怨hit类中Sphere没有匹配的原型? (我可能会将此问题转移到另一个问题......)

7 个答案:

答案 0 :(得分:19)

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

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

答案 1 :(得分:8)

如果使用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);
}

升压::任选的LT; T&GT;是一个包装类,包含一个T实例或boost :: none(一个boost :: none_t实例)。

有关详细信息,请参阅http://www.boost.org/doc/libs/1_47_0/libs/optional/doc/html/index.html

答案 2 :(得分:7)

我认为你需要像

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

能够返回NULL;

答案 3 :(得分:4)

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

答案 4 :(得分:1)

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

答案 5 :(得分:0)

  

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

从您的问题看,您希望只需在课程名称后添加*即可解决您的问题。然而,这种期望来自于缺乏对指针是什么,何时使用指针以及类型系统的重要性的理解。所以这个答案的其余部分有望澄清这些要点。

首先,C ++是一种强类型语言。这意味着当将一个变量分配给另一个变量时,2个变量必须属于同一类型。例如,假设在下面的代码中,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(你会发现如果你查找它的#defined为0),但你的函数的返回类型是Normal

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

Normal* Sphere::hit(Ray ray)

并返回pointer to a Normal个对象:

return new Normal(something, somethingElse); 

然而,在此阶段虽然应该解决编译器错误,但现在您需要管理动态分配的内存。我不能在这篇文章中介绍它,所以我会保留它的样子。

更新

这是为了解决你问题的第二部分。您的标头文件中的类声明应以;终止。

答案 6 :(得分:0)

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

因此返回Normal();

通常,优良作法是另外定义方法isEmpty()

这样,您可以像这样检查Null实例:

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