1

我们有一个存储类,类Person的定义如下:

class Person
{
public:
    string m_name;
    string m_address;
    string m_phone_number;
};

我们希望有一个存储类 ,PersonPool它存储所有 Person 的实例。

班级要求:

  1. 应该包含所有 Person 的实例。
  2. 应该提供将 Person 添加到池中的方法。
  3. 应提供快速访问方法以按地址删除人员
  4. 应按地址为人员提供快速的非常量获取器

我们建议以下课程:

class PersonPool
{
public:
    void add_person(const Person& p)   { m_persons.insert(p.m_address, p); }
    bool person_exists(string address) { return m_persons.has_key(address);     }
    Person& get_person(string address) { return m_persons[address];             }
    void remove_person(string address) { m_persons.erase(address);              }

private:
    map<String,Person> m_persons;  ///> key: person address; value: Person's instance
};

用法示例

说我有这段代码:

  1. 个人池 p_pool;
  2. 人 p1;
  3. p1.m_address = "x";
  4. p_pool.add_person(p1);
  5. 人& p2 = p_pool.get_person("x");
  6. p2.m_address = "y";
  7. p_pool.get_person("y");

问题

示例中的第 6 行修改了Person.
当我想得到一个Person基于新地址(“y”)的时候,PersonPool将无法返回这个Person
它不“知道”地址已被修改,但仍保留旧地址“x”作为该 Person 实例的键。

PersonPool增强建议:

  1. 地图的关键不应该是地址。
    问题:
    • 什么是正确的钥匙?请记住,我们需要通过地址快速访问。
    • 如果我们选择的新密钥也被Person的用户修改了怎么办(甚至 m_name 也可能被修改)
  2. 将函数添加到PersonPool
    void update_person(string old_address, string new_address)
    问题:
    • 丑陋的。用户应该为我糟糕的设计而烦恼。
    • 如果用户不使用此方法怎么办
  3. 仅提供 const getter。对存储在 中的Person的任何修改都PersonPool必须使用问题提供的功能来完成PersonPool
    • 打破班级要求。我们需要非常量的 getter
    • 即使我们放弃该要求,这也意味着我们必须PersonPersonPool. 我们当然不想这样做。

问题:

你能想出一个更好的PersonPool实现吗。是否可以调整我的建议并摆脱问题。

谢谢你的时间!

4

2 回答 2

1

我真的不知道这是否是一项学术活动。无论如何,很难确定具体的存储实现会给您带来最佳性能。这将取决于许多因素,包括(但不限于):

  • 数据集的大小(有多少人)
  • 数据集的内容(字符串的格式和大小)
  • 您正在使用的特定库的效率
  • 典型用法(针对查找与更新进行优化)

我会采取的方法是设计类接口以满足所有使用需求,构建一些简单的性能测试,然后开始比较不同存储实现的相对性能。从最简单的实现开始,如果需要,继续进行更复杂的优化(避免过早的优化!)。

这就是封装的好处:您可以自由更改内部实现细节,而不会影响接口的用户。

此外,使用地址作为唯一键似乎不起作用。如果您实际上是在对现实世界的数据进行建模,难道不止一个人有相同的地址(或姓名或电话号码)吗?我可能还会使用内部函数来封装唯一键的详细信息。

对类接口的一些建议:

// use typedefs to make changes easier
typedef string KEY_TYPE;
typedef map<KEY_TYPE, Person> PERSON_POOL;
typedef vector<Person> PERSONS;

class PersonPool
{
public:
    void add_person(const Person& p);
    void update_person(const Person& p);
    Person get_person(string name, string address);
    void remove_person(string name, string address);
    bool person_exists(string name, string address);

    // find zero or more persons
    PERSONS get_persons_by_name(string name);
    PERSONS get_persons_by_address(string address);
    PERSONS get_persons_by_number(string number);

private:
    KEY_TYPE get_key(string name, string address);
    KEY_TYPE get_key(const Person &p);
    PERSON_POOL m_persons;
};

示例实现:

void Person::add_person(const Person& p)
{
    m_persons.insert(get_key(p), p);
}

Person Person::get_person(const Person& p)
{
    PERSON_POOL::iterator i = find(m_persons.begin(), m_persons.end(), get_key(p));
    if (i != m_persons.end())
        return i->second;
    throw "person not found";
}

无论如何,祝你的项目好运。

于 2013-10-22T13:08:09.273 回答
0

使用带有地址作为键的 std::map 不是一个好主意。像这样使用向量怎么样:

class Person
{
public:
    string m_name;
    string m_address;
    string m_phone_number;
    bool operator == (const Person& other)
    {
        return m_address == other.m_address;
    }
};

class PersonPool
{
public:
    void add_person(const Person& p)   { m_persons.push_back(p); }
    bool person_exists(string address) { return (getPerson(address) != m_persons.end());     }
    Person& get_person(string address) { return (*getPerson(address)); }
    void remove_person(string address) { m_persons.erase(getPerson(address)); }

private:
    vector<Person> m_persons; 
    vector<Person>::iterator getPerson(string address)
    {
        return find(m_persons.begin(), m_persons.end(), address);
    }
};
于 2013-10-22T10:57:17.037 回答