C++ map vs map performance (I know, “again?”)

前端 未结 6 1930
北恋
北恋 2020-12-24 11:05

I was using a map with a std::string key and while everything was working fine I wasn\'t getting the performance I expected. I searched for places to optimize

6条回答
  •  無奈伤痛
    2020-12-24 11:33

    One solution to this is use a custom key class that acts as a cross between a const char * and a std::string, but has a boolean to tell at run time if it is "owning" or "non-owning". That way you can insert a key into the map which owns it's data (and will free it on destruction), and then compare with a key that does not own it's data. (This is a similar concept to the rust Cow<'a, str> type).

    The below example also inherits from boost's string_ref to avoid having to re-implement hash functions etc.

    NOTE this has the dangerous effect that if you accidentally insert into the map with the non-owning version, and the string you are pointing at goes out of scope, the key will point at already freed memory. The non-owning version can only be used for lookups.

    #include 
    #include 
    #include 
    
    #include 
    
    class MaybeOwned: public boost::string_ref {
    public:
      // owning constructor, takes a std::string and copies the data
      // deletes it's copy on destruction
      MaybeOwned(const std::string& string):
        boost::string_ref(
          (char *)malloc(string.size() * sizeof(char)),
          string.size()
        ),
        owned(true)
      {
        memcpy((void *)data(), (void *)string.data(), string.size());
      }
    
      // non-owning constructor, takes a string ref and points to the same data
      // does not delete it's data on destruction
      MaybeOwned(boost::string_ref string):
        boost::string_ref(string),
        owned(false)
      {
      }
    
      // non-owning constructor, takes a c string and points to the same data
      // does not delete it's data on destruction
      MaybeOwned(const char * string):
        boost::string_ref(string),
        owned(false)
      {
      }
    
      // move constructor, tells source that it no longer owns the data if it did
      // to avoid double free
      MaybeOwned(MaybeOwned&& other):
        boost::string_ref(other),
        owned(other.owned)
      {
        other.owned = false;
      }
    
      // I was to lazy to write a proper copy constructor
      // (it would need to malloc and memcpy again if it owned the data)
      MaybeOwned(const MaybeOwned& other) = delete;
    
      // free owned data if it has any
      ~MaybeOwned() {
        if (owned) {
          free((void *)data());
        }
      }
    
    private:
      bool owned;
    };
    
    int main()
    {
      std::map map;
      map.emplace(std::string("key"), "value");
      map["key"] += " here";
      std::cout << map["key"] << "\n";
    }
    

提交回复
热议问题