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()) { ... }
也
std::string\u视图
您的备选方案概述如下
将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()) { ... }
也
std::string\u视图
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