实现存储类以存储某个类的多个实例

Implement storage class to store multiple instances of some class

本文关键字:存储 实例 实现      更新时间:2023-10-16

我们有一个存储类,类Person具有以下定义:

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

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

班级要求:

  1. 应包含人员的所有实例。
  2. 应提供将人员添加到池的方法。
  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. Person&p2 = p_pool.get_person("x");
  6. p2.m_address = "y";
  7. p_pool.get_person("y");

问题

示例中的第 6 行修改了 Person 的地址。
当我想根据新地址("y")获取Person时,PersonPool将无法返回此Person
它不会"知道"地址已被修改,并且仍然保留旧地址"x"作为该 Person 实例的键。

增强PersonPool的建议:

  1. 地图的键不应是地址。
    问题:
    • 什么是正确的钥匙? 请记住,我们需要按地址快速访问。
    • 如果我们选择的新密钥也被Person的用户修改了怎么办(即使m_name可能会被修改)
  2. PersonPool添加函数:
    无效update_person(字符串old_address,字符串new_address)
    问题:
    • 丑。用户不应该为我的糟糕设计而烦恼。
    • 如果用户不使用此方法怎么办
  3. 仅提供常量吸气剂。 对存储在 PersonPool 中的 Person 的任何修改都必须使用PersonPool
    提供的新功能来完成问题:
    • 打破类要求。 我们需要非常量吸气剂
    • 即使我们放弃了这个要求,这也意味着我们必须在PersonPool中复制Person界面。 我们当然不想这样做。

问题:

你能想到更好的PersonPool实现吗?是否有可能调整我的建议并摆脱问题。

谢谢你的时间!

我真的不知道这是否是一项学术练习。 无论如何,很难确定哪种特定的存储实施将为您提供最佳性能。 这将取决于许多因素,包括(但不限于):

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

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

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

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

对类接口的一些建议:

// 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";
}

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

使用 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);
    }
};