C++ 如何处理不保证返回任何内容的函数?

C++ 如何处理不保证返回任何内容的函数?,c++,c++11,C++,C++11,我有一个类,它存储并管理一个包含许多对象的向量 我发现自己编写了许多类似于以下内容的函数: Object* ObjectManager::getObject(std::string name){ for(auto it = object_store.begin(); it != object_store.end(); ++it){ if(it->isCalled(name)) return &(*it) } return nullptr; } 我想

我有一个类,它存储并管理一个包含许多对象的向量

我发现自己编写了许多类似于以下内容的函数:

Object* ObjectManager::getObject(std::string name){
  for(auto it = object_store.begin(); it != object_store.end(); ++it){
    if(it->isCalled(name))
      return &(*it)
  }
  return nullptr;
}

我想我宁愿通过引用返回,因为在这里调用方必须记住检查null!有什么方法可以改变我的设计来更好地处理这个问题吗?

下面列出了您的备选方案


将API更改为以下内容

object_manager.execute_if_has_object("something", [](auto& object) {
    use_object(object);
});
此API更易于使用,完美地传达了意图,并从用户的头脑中消除了错误处理、返回类型等思维过程


抛出异常

Object& ObjectManager::getObject(const std::string& name){
  for(auto& object : object_store){
    if(object.isCalled(name))
      return object;
  }

  // throw an exception
  throw std::runtime_error{"Object not found"};
}

返回bool,通过引用传递
对象
,并获取副本

bool ObjectManager::getObject(const std::string& name, Object& object_out){
  for(auto& object : object_store){
    if(object.isCalled(name)) {
      object_out = object;
      return true;
    }
  }

  return false;
}

让用户进行查找

auto iter = std::find(object_store.begin(), object_store.end(), [&name](auto& element) {
    return element.isCalled(name);
}
if (iter != object_store.end()) { ... } 

  • 通过常量引用传递该字符串。当C++17可用时,将常量引用更改为
    std::string\u视图
  • 使用基于范围的for循环在这种情况下,对于您正在做的事情,它们是一个更可读的替代方法

  • 您的备选方案概述如下


    将API更改为以下内容

    object_manager.execute_if_has_object("something", [](auto& object) {
        use_object(object);
    });
    
    此API更易于使用,完美地传达了意图,并从用户的头脑中消除了错误处理、返回类型等思维过程


    抛出异常

    Object& ObjectManager::getObject(const std::string& name){
      for(auto& object : object_store){
        if(object.isCalled(name))
          return object;
      }
    
      // throw an exception
      throw std::runtime_error{"Object not found"};
    }
    

    返回bool,通过引用传递
    对象
    ,并获取副本

    bool ObjectManager::getObject(const std::string& name, Object& object_out){
      for(auto& object : object_store){
        if(object.isCalled(name)) {
          object_out = object;
          return true;
        }
      }
    
      return false;
    }
    

    让用户进行查找

    auto iter = std::find(object_store.begin(), object_store.end(), [&name](auto& element) {
        return element.isCalled(name);
    }
    if (iter != object_store.end()) { ... } 
    

  • 通过常量引用传递该字符串。当C++17可用时,将常量引用更改为
    std::string\u视图
  • 使用基于范围的for循环在这种情况下,对于您正在做的事情,它们是一个更可读的替代方法
  • 看看STL的设计(例如,
    find
    函数),返回刚才搜索到的迭代器并返回
    .end()
    ,否则返回
    .end()

    auto ObjectManager::getObject(std::string name){
      for(auto it = object_store.begin(); it != object_store.end(); ++it){
        if(it->isCalled(name))
          return it;
      }
      return object_store.end();
    }
    

    更多:当然,
    object\u store.end()
    可能无法从类外访问,但这不是借口,因为您可以这样做(请注意更流畅的代码)

    代码越少越好。您可以这样使用它:

    auto result = *om.getObject("pizza"); // search, not check (if you know what you are doing)
    


    当然,在这一点上,最好调用函数
    findOject
    noposObject
    ,并询问为什么不直接在
    对象存储
    容器上使用
    std::find

    看看STL的设计(例如
    find
    函数),返回刚才搜索到的迭代器,然后返回
    .end()
    ,这一点都不坏

    auto ObjectManager::getObject(std::string name){
      for(auto it = object_store.begin(); it != object_store.end(); ++it){
        if(it->isCalled(name))
          return it;
      }
      return object_store.end();
    }
    

    更多:当然,
    object\u store.end()
    可能无法从类外访问,但这不是借口,因为您可以这样做(请注意更流畅的代码)

    代码越少越好。您可以这样使用它:

    auto result = *om.getObject("pizza"); // search, not check (if you know what you are doing)
    



    当然,在这一点上,最好调用函数
    findOject
    noposObject
    ,并询问为什么不直接在
    object\u store
    容器上使用
    std::find

    异常或可选的解决方案是实现一个“-,它可以用作常规对象,但将”视情况而定,有时它可以按原样使用,不需要(明确地)检查-特别是在忽略“未找到”情况是可以接受的情况下

    (空对象可以是静态全局对象,因此也可以返回对它的引用)

    即使需要检查,也可以实现
    isNull()
    方法,该方法对于null对象返回true,对于有效对象返回false(或者可以存在
    isValid()
    方法等)


    例如:

    class Object {
    public:
        virtual void doSomething();
    };
    
    class NullObject: public Object {
    public:
        virtual void doSomething() {
            // doing nothing - ignoring the null object
        }
    };
    
    class ObjectManager {
    public:
        Object& getObject(const std::string& name);
    private:
        static NullObject s_nullObject;
    };
    
    Object& ObjectManager::getObject(const std::string& name){
      for(auto it = object_store.begin(); it != object_store.end(); ++it){
        if(it->isCalled(name))
          return *it;
      }
      return s_nullObject;
    }
    
    ObjectManager mgr;
    Object& obj = mgr.getObject(name);
    obj.doSomething(); // does nothing if the object is NullObject
                       // (without having to check!)
    

    替代异常或可选的解决方案是实现“-”,它可以用作常规对象,但“什么也不做”。视情况而定,有时它可以按原样使用,不需要(明确地)检查-特别是在忽略“未找到”情况是可以接受的情况下

    (空对象可以是静态全局对象,因此也可以返回对它的引用)

    即使需要检查,也可以实现
    isNull()
    方法,该方法对于null对象返回true,对于有效对象返回false(或者可以存在
    isValid()
    方法等)


    例如:

    class Object {
    public:
        virtual void doSomething();
    };
    
    class NullObject: public Object {
    public:
        virtual void doSomething() {
            // doing nothing - ignoring the null object
        }
    };
    
    class ObjectManager {
    public:
        Object& getObject(const std::string& name);
    private:
        static NullObject s_nullObject;
    };
    
    Object& ObjectManager::getObject(const std::string& name){
      for(auto it = object_store.begin(); it != object_store.end(); ++it){
        if(it->isCalled(name))
          return *it;
      }
      return s_nullObject;
    }
    
    ObjectManager mgr;
    Object& obj = mgr.getObject(name);
    obj.doSomething(); // does nothing if the object is NullObject
                       // (without having to check!)
    

    我认为您已经正确地处理了返回值,并且您当前的解决方案是最佳的

    事实上,为了发现查找操作是否成功,您无法避免检查某些内容。如果抛出异常,则您的
    try{}catch{}
    是您的检查。当找不到项目是合法结果时,也不应使用
    异常。如果您返回一个
    bool
    并使用out参数,则执行相同的工作会使情况更加复杂。与返回迭代器相同。
    std::可选的
    返回值


    因此,在我看来,返回指针并不能提高性能,只能使同一项工作变得更加复杂。

    我认为您已经正确地处理了返回值,当前的解决方案是最佳的

    事实上,为了发现查找操作是否成功,您无法避免检查某些内容。如果抛出异常,则您的
    try{}catch{}
    是您的检查。当找不到项目是合法结果时,也不应使用
    异常。如果您返回一个
    bool
    并使用out参数,则执行相同的工作会使情况更加复杂。与返回迭代器相同。
    std::可选的
    返回值


    因此,在我看来,返回指针是无法改进的,只需将相同的工作变得更加复杂。

    当C++17出现时,如果您不喜欢异常,请使用
    std::optional
    )@raket1111可以
    std::optional
    返回引用吗?我以为它返回了值?@Galik-True,但就像容器一样,y