C++;并返回一个空值-在Java中工作的东西不';t在C+中工作+; 所以我有一个相当混乱的转换到爪哇+ C++的C++。尽管我觉得我理解了大部分的基础知识,但在我的理解中还是有一些巨大的漏洞 例如,考虑以下函数: Fruit& FruitBasket::getFruitByName(std::string fruitName) { std::map<std::string,Fruit>::iterator it = _fruitInTheBascit.find(fruitName); if(it != _fruitInTheBascit.end()) { return (*it).second; } else { //I would so love to just return null here } } 水果& 水果篮::GetFroothByName(标准::字符串FroothName) { std::map::iterator it=\u fruitInBascit.find(fruitName); if(it!=\u foultinBascit.end()) { 返回(*it); } 其他的 { //我很想在这里返回空值 } }

C++;并返回一个空值-在Java中工作的东西不';t在C+中工作+; 所以我有一个相当混乱的转换到爪哇+ C++的C++。尽管我觉得我理解了大部分的基础知识,但在我的理解中还是有一些巨大的漏洞 例如,考虑以下函数: Fruit& FruitBasket::getFruitByName(std::string fruitName) { std::map<std::string,Fruit>::iterator it = _fruitInTheBascit.find(fruitName); if(it != _fruitInTheBascit.end()) { return (*it).second; } else { //I would so love to just return null here } } 水果& 水果篮::GetFroothByName(标准::字符串FroothName) { std::map::iterator it=\u fruitInBascit.find(fruitName); if(it!=\u foultinBascit.end()) { 返回(*it); } 其他的 { //我很想在这里返回空值 } },c++,null,C++,Null,其中,\u foultsinthebascit是一个std::map。如果我查询getFruitByName(“金橘”)你知道它不会在那里-谁吃金橘?但我不想我的程序崩溃。在这些情况下应该怎么做 请告诉我我还没有确定的其他愚蠢之处。 < P>如果你需要NULL,你可以返回一个指针而不是引用。 < P>没有作为C++引用的空引用,所以如果函数返回引用,你就不能返回null。您有几个选择: 更改返回类型,使函数返回指针;如果找不到元素,则返回null 保留引用返回类型,但具有某种“sentinel”

其中,
\u foultsinthebascit
是一个
std::map
。如果我查询
getFruitByName(“金橘”)
你知道它不会在那里-谁吃金橘?但我不想我的程序崩溃。在这些情况下应该怎么做


请告诉我我还没有确定的其他愚蠢之处。

< P>如果你需要NULL,你可以返回一个指针而不是引用。

< P>没有作为C++引用的空引用,所以如果函数返回引用,你就不能返回null。您有几个选择:

  • 更改返回类型,使函数返回指针;如果找不到元素,则返回null

  • 保留引用返回类型,但具有某种“sentinel”水果对象,如果找不到该对象,则返回对该对象的引用

  • 保留引用返回类型,如果在映射中找不到水果,则抛出异常(例如,
    FruitNotFoundException

  • 我倾向于使用(1)如果失败是可能的,(3)如果失败是不可能的,其中“可能”是一个完全主观的衡量标准。我认为(2)有点像黑客,但我见过它在某些情况下被巧妙地使用


    举一个“不太可能”失败的例子:在我当前的项目中,我有一个管理对象的类,有一个返回对象是否存在的函数
    is\u object\u present
    ,还有一个返回对象的函数
    get\u object
    。我总是希望调用方在调用
    get\u object
    之前通过调用
    is\u object\u present
    来验证对象的存在,因此在这种情况下失败的可能性很小。

    这不起作用的原因是因为您的函数返回了一个引用。引用必须始终是实际实例。java不是C++。 解决此问题的一种方法是更改函数以返回指针,该指针的工作方式更像java使用的引用。在这种情况下,您可以
    返回null

    Fruit*
    FruitBasket::getFruitByName(std::string fruitName)
    {
        std::map<std::string,Fruit>::iterator it = _fruitInTheBascit.find(fruitName);
        if(it != _fruitInTheBascit.end()) 
        {
            return &(*it).second;
        }
        else
        {
               return NULL;
        }
    
    }
    
    水果*
    水果篮::GetFroothByName(标准::字符串FroothName)
    {
    std::map::iterator it=\u fruitInBascit.find(fruitName);
    if(it!=\u foultinBascit.end())
    {
    返回&(*it)。第二;
    }
    其他的
    {
    返回NULL;
    }
    }
    
    如果出于某种原因希望避免这样做,可以定义一个sentinel对象并返回它。像这样的

    Fruit NullFruit;
    
    Fruit&
    FruitBasket::getFruitByName(std::string fruitName)
    {
        std::map<std::string,Fruit>::iterator it = _fruitInTheBascit.find(fruitName);
        if(it != _fruitInTheBascit.end()) 
        {
            return (*it).second;
        }
        else
        {
            return NullFruit;
        }
    
    }
    
    水果和水果;
    果&
    水果篮::GetFroothByName(标准::字符串FroothName)
    {
    std::map::iterator it=\u fruitInBascit.find(fruitName);
    if(it!=\u foultinBascit.end())
    {
    返回(*it);
    }
    其他的
    {
    还果;
    }
    }
    
    另一个选择是根本不返回。提出例外

    class NullFruitException: public std::exception {};
    
    Fruit&
    FruitBasket::getFruitByName(std::string fruitName)
    {
        std::map<std::string,Fruit>::iterator it = _fruitInTheBascit.find(fruitName);
        if(it != _fruitInTheBascit.end()) 
        {
            return (*it).second;
        }
        else
        {
            throw NullFruitException;
        }
    
    }
    
    类异常:公共std::异常{};
    果&
    水果篮::GetFroothByName(标准::字符串FroothName)
    {
    std::map::iterator it=\u fruitInBascit.find(fruitName);
    if(it!=\u foultinBascit.end())
    {
    返回(*it);
    }
    其他的
    {
    抛出一个异常;
    }
    }
    
    引用不能为空。它们在异常情况下工作得最好——您可以抛出错误代码,而不是返回错误代码

    或者,您可以使用带有错误代码返回值的“out”参数:

    bool FruitBasket::getFruitByName(const std::string& fruitName, Fruit& fruit)
    {
        std::map<std::string,Fruit>::iterator it = _fruitInTheBascit.find(fruitName);
        if(it != _fruitInTheBascit.end()) 
        {
            fruit = (*it).second;
            return true;
        }
        else
        {
            return false;
        }
    }
    

    我在C++系里有点生疏了。但是有没有可能用水果来代替水果&

    如果进行了此更改,则可以说“returnnull;”(或returnnull,或return0…无论C++的语法是什么)

    好的。有很多解决方案。
    詹姆斯·麦克内利斯已经涵盖了所有显而易见的问题。
    就个人而言,我更喜欢他的解决方案(1),但缺少很多细节

    另一种方法(我把它作为一种方法扔掉)是创建一个水果引用类型,它知道对象是否有效。然后,您可以从getFroothByName()方法返回:

    基本上,它与返回指针相同;但是没有与指针关联的所有权符号,因此很难判断是否应该删除指针。通过使用FROUT引用类型,您不会公开指针,因此不会导致所有权混淆

    class FruitReference
    {
        public:
            FruitReference()  // When nothing was found use this.
                :data(NULL)
            {}
            FruitReference(Fruit& fruit)  // When you fidn data.
                :data(&fruit)
            {}
            bool   isValid() const { return data != NULL;}
            Fruit& getRef()  const { return *data; }
        private:
            Fruit*   data; //(not owned)
    };
    
    FruitReference const& FruitBasket::getFruitByName(std::string fruitName)   
    {   
      std::map<std::string,Fruit>::iterator it = _fruitInTheBascit.find(fruitName);   
      if(it != _fruitInTheBascit.end())    
      {   
        return FruitReference((*it).second);   
      }   
      else   
      {   
        return FruitReference();
      }
    }
    
    类引用
    {
    公众:
    FruitReference()//未找到任何内容时,请使用此选项。
    :数据(空)
    {}
    水果参考(水果和水果)//当您fidn数据时。
    :数据(&F)
    {}
    bool isValid()常量{返回数据!=NULL;}
    水果&getRef()常量{return*data;}
    私人:
    水果*数据;//(非自有)
    };
    水果引用常量和水果篮::GetFroothByName(标准::字符串水果名)
    {   
    std::map::iterator it=\u fruitInBascit.find(fruitName);
    if(it!=\u foultinBascit.end())
    {   
    返回引用((*it.second);
    }   
    其他的
    {   
    返回引用();
    }
    }
    

    我确信boost也有类似的东西,但我在20秒的搜索中找不到。

    詹姆斯·麦克内利斯的答案恰到好处。然而,我要指出的是,您应该将C++的指针视为类似于Java的引用,而不是将C++的引用视为类似于Java的引用。如果您试图返回一个不能为null的基元类型,那么您在这种情况下所做的与在Java中所做的类似。在这一点上,你基本上是按照詹姆斯的建议做的
    class FruitReference
    {
        public:
            FruitReference()  // When nothing was found use this.
                :data(NULL)
            {}
            FruitReference(Fruit& fruit)  // When you fidn data.
                :data(&fruit)
            {}
            bool   isValid() const { return data != NULL;}
            Fruit& getRef()  const { return *data; }
        private:
            Fruit*   data; //(not owned)
    };
    
    FruitReference const& FruitBasket::getFruitByName(std::string fruitName)   
    {   
      std::map<std::string,Fruit>::iterator it = _fruitInTheBascit.find(fruitName);   
      if(it != _fruitInTheBascit.end())    
      {   
        return FruitReference((*it).second);   
      }   
      else   
      {   
        return FruitReference();
      }
    }