1

我目前正在通过实现一个简单的地址簿应用程序来学习 C++ 并练习我的知识。
我从一个Entry类和一个AddressBook实现 STL Map 的类开始,以通过人员的姓氏访问条目。
现在我得到了以下代码:

Entry AddressBook::get_by_last_name(string last_name){
    if(this->addr_map.count(last_name) != 0){
        //What can I do here?
    } else {
        return addr_map[last_name];
    }

在脚本语言中,我只会返回类似-1, Error Message(Python 中的列表)来指示函数失败。我不想抛出异常,因为它是应用程序逻辑的一部分。调用类应该能够通过在控制台上打印某些内容或打开消息框来响应请求。
现在我考虑通过在 Class 中引入某种 Invalid State 来实现 C++ 中的脚本语言方法Entry。但是在 C++ 中这不是不好的做法吗?会不会是我的全班设计不合适?我很感激任何帮助。请记住,我仍在学习 C++。

4

7 回答 7

2

关于您的代码的一些快速说明:

if(this->addr_map.count(last_name) != 0){
    //What can I do here?

你可能想要它的另一种方式:

if(this->addr_map.count(last_name) == 0){
    //handle error

但你真正的问题在于:

return addr_map[last_name];

这里需要注意两点:

  1. for map 可以做两operator[]件事:如果元素存在,则返回它;如果元素不存在,它会pair使用指定的键和值的 default创建一个新的 (key,value) constructor。可能不是你想要的。但是,如果您if之前的陈述是正确的方式,那么后者将永远不会发生,因为我们会事先知道密钥存在。
    1. 在调用count()之前,您有效地告诉map尝试查找元素。通过调用operator[],您是在告诉您map再次找到它。因此,您正在做两次工作来检索单个值。

一种更好(更快)的方法涉及迭代器和find方法:

YourMap::iterator it = addr_map.find(last_name); //find the element (once)
if (it == addr_map.end()) //element not found
{
    //handle error
}
return *it.second; //return element

现在,回到手头的问题。如果last_name找不到怎么办?正如其他答案指出的那样:

  • 最简单的解决方案是返回一个指针(如果没有找到则为 NULL)
  • 使用boost::optional.
  • 只需返回,YourMap::iterator但似乎您正试图向map用户“隐藏”,AddressBook所以这可能是一个坏主意。
  • throw一个exception。但是等等,现在您必须首先检查调用此方法是否“安全”(或exception在适当的时候处理)。此检查需要一个布尔方法,例如lastNameExists在调用之前必须调用该方法get_by_last_name。当然,然后我们回到方块 1。我们正在执行 2 次查找操作来检索单个值。它是安全的,但是如果您要进行大量调用,get_by_last_name那么这可能是一个使用不同解决方案进行优化的好地方(此外,可以说例外不是很有建设性:搜索不存在的东西有什么问题,嗯?)。
  • 创建一个dummy成员来Entry表明这不是一个真实的 Entry但那是非常糟糕的设计(难以管理、反直觉、浪费——你可以说出它的名字)。

如您所见,前两种解决方案是更可取的。

于 2013-02-18T12:40:55.453 回答
1

其他答案给出了各种方法,其中大多数是有效的。我还没有看到这个:

您可以添加具有默认值的第二个参数:

Entry AddressBook::get_by_last_name(string last_name, const Entry& default_value){
    if(this->addr_map.count(last_name) == 0){
        return default_value; 
    } else {
        return addr_map[last_name];
    }

在这种特殊情况下,对于不存在的姓氏可能没有合理的默认值,但在许多情况下是有的。

于 2013-02-18T12:47:55.487 回答
1

根据您的函数返回类型,抛出异常绝对是“正确”的 C++ 事情。

不过,您可能需要这样的功能来帮助您:

bool AddressBook::lastNameExists(const string &last_name)
{
    return addr_map.count(last_name) > 0;
}

请注意,您当前的代码会“按值”返回条目,因此修改返回的条目不会更新地图。不知道这是意外还是设计...

于 2013-02-18T12:30:56.110 回答
1

一个非常简单的选择是将返回类型更改为Entry*(or const Entry*),然后如果找到则返回 Entry 的地址,否则返回 NULL。

如果您使用 Boost,您可以返回一个boost::optional<Entry>,在这种情况下您的成功代码将是相同的,但在未找到时您会说return boost::none. 这更漂亮,但与使用指针返回类型的作用大致相同。

于 2013-02-18T12:06:02.017 回答
0

你可以做 std::map (和其他容器做的)。

您从搜索函数返回一个迭代器。
如果搜索未找到有用的值,则将迭代器返回到 end()。

class AddressBook
{
        typedef  <Your Container Type>  Container;
    public:
        typedef  Container::iterator   iterator;


        iterator get_by_last_name(std::string const& lastName) {return addr_map.find[lastName];}

        iterator end()                                         {return addr_map.end();}
};

您的通讯录是一个容器,如对象。
可能会在搜索中找不到项目,但它没有足够的上下文来包含错误处理代码(因为地址簿可以在很多地方使用,并且每个地方都有不同的错误处理想法)。

因此,您必须将未找到状态的测试从地址簿中移出。
就像“Python”一样,我们返回一个标记。在 C++ 中,这通常是 end() 的迭代器,调用代码可以检查并采取适当的操作。

 AddressBook&  ab = getAddressBookRef();
 AddressBook::iterator find = ab.get_by_last_name("cpp_hobbyist");
 if (find != ab.end())
 {
     Entity&  person *find;  // Here you have a reference to your entity.
     // you can now manipulate as you want.
 }
 else
 {
     // Display appropriate error message
 }
于 2013-02-18T15:01:27.477 回答
0

除了每个姓氏可以有多个条目之外。

消除getter,您就解决了问题,或者至少将其转移到其他地方。

告诉AddressBook显示具有给定姓氏的人。如果没有,它什么也做不了。

AddressBookRenderer renderer;
AddressBook contacts;

contacts.renderSurnames("smith", renderer);
contacts.renderCompletions("sm", renderer);
//etc
于 2013-02-18T12:41:16.583 回答
0

在 C++ 中,您有几种方法可以表明您的函数中发生了问题。

您可以返回一个特殊值,调用代码会将其识别为无效值。如果函数应该返回一个指针,这可以是一个NULL指针,或者如果你的函数返回数组中的索引,则可以是一个负值,或者,在自定义类(例如你的Entry类)的情况下,你可以定义一个特殊Entry::invalid值或类似的东西可以通过调用函数检测到。

您的调用代码可能看起来像

if ( entryInstance->get_by_last_name("foobar") != Entry::invalid) 
{
  // here goes the code for the case where the name is valid
} else {
  // here goes the code for the case where the name is invalid
}

另一方面,您可以使用 C++ 异常机制并使您的函数抛出异常。为此,您可以创建自己的异常类(或使用标准库中定义的一个,派生自std::exception)。您的函数将throw出现异常,并且您的调用代码必须使用try...catch语句来捕获它。

try
  {
    entryInstance->get_by_last_name("foobar")
  }
  catch (Exception e)
  {
    // here goes the code for the case where the name is invalid
  }
  // here goes the code for the case where the name is valid
于 2013-02-18T12:18:41.563 回答