【问题标题】:How Do I Deal With This C++ Multimap Issue?我该如何处理这个 C++ Multimap 问题?
【发布时间】:2020-02-09 17:57:01
【问题描述】:

我是 C++ 的初学者,正在研究简单的程序,但遇到了一个让我很困惑的问题...基本上我创建了一个多重映射,它接受字符串人名的一部分和一个人对象的指针。

所以我使用的第一个函数是“Add()”函数。我基本上用名字和年龄构造了一个新人,然后设置一个指向该对象的指针。然后我将名称和对象作为一对插入到多重映射中。

问题发生在我运行 find() 函数时...由于我是初学者,我很确定我正在做一些非常简单但非常愚蠢的事情。我认为您不需要运行代码,因为它非常简单。感谢所有帮助。谢谢!

我的可运行文件:

#include "Person.h"
#include <map>
using namespace std;

multimap<string, Person*> personMap;
multimap<string, Person*>::iterator counter;


void add()
{
  string name;
  int age;
  cout << "Please enter the name of the person: ",  cin >> name;
  cout << "Please enter the age of the person: ", cin >> age;
  Person generatedPerson(name, age);

  // Avoid dynamic class pointers for memory optimization.
  // Avoided: Person* pointer = new Person(name, age).
  Person *pointer = &generatedPerson;
  personMap.insert({name, pointer});

}

void find()
{
  string personToBeFound;
  cout << "Who do you wish to find, friend: ", cin >> personToBeFound;


  for (counter = personMap.begin(); counter != personMap.end(); counter++)
  {
    if (counter->first == personToBeFound)
    {
      cout << "\nName: " << counter->first << " Age: " << counter->second->getAge() << endl;
    }
    else if (counter->first != personToBeFound)
    {
      cout << "Error 404, person does not exist..." << endl;
    }
  }
}

// Experimental....
int main(int argc, char* argv[])
{
  int menuChoice = -1;

  while (menuChoice != 0)
  {
    cout << "\nPlease enter: "
            "\n1 - to add a person "
            "\n2 - to find a person"
            "\n0 - to quit\n" << endl;
    cin >> menuChoice;
    switch(menuChoice)
    {
      case 1: add();
        break;
      case 2: find();
        break;
      case 0: menuChoice = 0;
    }
  }
}

头文件:

#ifndef PERSON_H
#define PERSON_H

#include <string>
#include <vector>
#include <iostream>

class Person {
 public:
  // Constructors

  /**
   * Create a Person with the given name and age.
   *
   * @param name name of the person
   * @param age age of the person - defaults to 0
   */
  Person(const std::string& name, unsigned short age = 0);


  // No explicit destructor necessary


  // Mutators

  /**
   * Set the name attribute
   *
   * @param name name of the person
   */
  void setName(const std::string& name);

  /**
   * Set the age attribute
   *
   * @param age age of the person
   */
  void setAge(unsigned short age);

  /**
   * Increment the age attribute
   */
  void growOlder();

  /**
   * Add a person to our list of children
   *
   * @param child Person to add as a child
   */
  void addChild(const Person& child);


  // Accessors

  /**
   * @return the Person's name
   */
  const std::string& getName() const;

  /**
   * @return the Person's age
   */
  unsigned short getAge() const;

  /**
   * @return a list of this Person's children
   */
  const std::vector<const Person *>& getChildren() const;

  /**
   * Define the ostream's << operator as a friend of this class
   * to allow this object to be printed to an output stream
   *
   * @param output the stream to print to
   * @param p the Person to print
   *
   * @return the output stream printed to
   */
  friend std::ostream& operator<< (std::ostream& output, const Person& p);


 private:

  // 0-arg Constructor
  Person();

  // Private attributes
  std::string _name;
  unsigned short _age;
  std::vector<const Person *> _children;

}; // Person

#endif

方法定义:

#include "Person.h"

Person::Person(const std::string& name, unsigned short age) :
    _name(name) , _age(age) {
}


void Person::setName(const std::string& name) {
  _name = name;
}


void Person::setAge(unsigned short age) {
  _age = age;
}


void Person::growOlder() {
  _age++;
}


void Person::addChild(const Person& child) {
  _children.push_back(&child);
}


const std::string& Person::getName() const {
  return _name;
}


unsigned short Person::getAge() const {
  return _age;
}


const std::vector<const Person *>& Person::getChildren() const {
  return _children;
}


std::ostream& operator<< (std::ostream& output, const Person& aPerson) {
  // Print our attributes to the output stream
  return output << "Name: '" << aPerson._name << "', Age: " << aPerson._age;
}

【问题讨论】:

  • 您在映射中存储了一个指向局部变量的指针,因此当您稍后尝试访问它时,指向的对象已被销毁(当add 返回时)。
  • 看起来更高级一点,您可能应该在数据容器中存储 值类型 而不是指针 - 例如class Person。地图将变为 std::multimap<:string person>.
  • 批评#2:使用std::multimap 而不是std::vector 的好处是按关键字搜索具有O(log(n)) 时间复杂度。通过遍历地图,它需要 'O(n)` - 探索 std::multi_map::equal_range()
  • @1201ProgramAlarm 这有很大帮助,但想了解为什么会这样,以及如何在未来解决这个问题的最佳实践。从逻辑上讲,地图正在接受指向人员对象的指针。为什么当我创建一个实际的指针并指向一个人对象然后将它输入到它破坏的地图中?感谢您花时间最初发表评论!我很感激!
  • 如果可以的话,最好不要有一个指针容器。如果无法避免指针,请使用std::unique_ptr&lt;Person&gt; 而不是Person *

标签: c++ c++11 pointers


【解决方案1】:

您好@1201ProgramAlarm 的评论是绝对正确的,但是,我自己想,我将如何去“拯救”程序(a)以最小的努力,(b)获得正确的版本和(c)非常与原始版本相比。

所以这是一个新的main.cc 版本(Person.hPerson.cc 无需更改):

#include "Person.h"
#include <map>

using namespace std;

multimap <string, Person *> personMap;
multimap <string, Person *>::iterator counter;


void add()
{
    string name;
    int    age;

    cout << "Please enter the name of the person: ", cin >> name;
    cout << "Please enter the age  of the person: ", cin >> age;

    personMap.insert({ name, new Person(name, age) });
}


void find()
{
    string personToBeFound;

    cout << "Who do you wish to find, friend: ", cin >> personToBeFound;

    for (counter = personMap.begin(); counter != personMap.end(); counter++)
    {
        if (counter->first == personToBeFound)
        {
            cout << "\nName: " << counter->first
                 <<   " Age: " << counter->second->getAge() << endl;
        }
        else
        {
            cout << "Error 404, person does not exist..." << endl;
        }
    }
}


int main(int argc, char* argv[])
{
    int    menuChoice = -1;
    while (menuChoice != 0)
    {
        cout << "\nPlease enter: "
            "\n1 - to add a person "
            "\n2 - to find a person"
            "\n0 - to quit\n" << endl;

        cin >> menuChoice;
        switch(menuChoice)
        {
        case 1:
            add();
            break;

        case 2:
            find();
            break;

        case 0:
            menuChoice = 0;
            break;
        }
    }

    // -- How to go about clearing the heap? --

    for (counter = personMap.begin(); counter != personMap.end(); counter++)
    {
        if (counter->second) {
            delete   counter->second;
            counter->second = nullptr;
        }
    }
}

我使用g++ -o main Person.cc main.cc 编译。问候,M。

此问题后更新

为什么我不应该只删除键而不是只删除 multimap &lt;string, Person *&gt; 条目的值?

回答

我只是想展示当multimap 超出(本地)范围并因此会被@ 自动销毁时,可以采取哪些措施来防止堆上的孤立 Person 对象987654329@系统。更具体地说:

(a) 和C一样,malloc-ed 后面又需要free-ed,C++ 中的等价物是:new-ed 是什么需要@ 987654335@-ed 稍后。从长远来看,这是防止内存泄漏的最佳做法。

(b) 现在,回到我们的multimap &lt;string, Person *&gt;。这里,键是strings,值是指向Person 对象的指针。这些Person 指针 是在调用add() 函数时获得堆上分配的相应Person 对象 的唯一方法。

(c) 在我们的例子中,在multimap 被自动销毁后,整个程序无论如何都会停止,所以delete-ing Person 对象使用额外的for-loop 不是这里很重要,因为在相应的进程终止后,堆上剩余的 Person 对象会被操作系统进行垃圾回收。

【讨论】:

  • 您的回答在固定的 add() 方法中是有意义的,所以谢谢您。您能否解释一下为什么我不应该只删除键而不仅仅是删除值。如果我一路删除重复项也没关系。只是删除键而不是使键的值归零不是很好吗? @micha
  • 哇!精彩的回答和解释!非常感谢您花时间解释所有这些!!! @micha
  • @XOR 不客气,很高兴我能贡献一些东西。顺便说一句,当涉及到 std::shared_ptr &lt;X&gt;std::unique_ptr &lt;X&gt; 时,您可能想回到这个 Person 演示,这通常是 C++11 的方式。它位于#include &lt;memory&gt;。问候,M.
猜你喜欢
  • 1970-01-01
  • 2020-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多