首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++ 11在std::map中缓存的最佳实践?

C++ 11在std::map中缓存的最佳实践?
EN

Stack Overflow用户
提问于 2018-11-06 01:34:28
回答 1查看 2.2K关注 0票数 0

( c++ 11)

我想要存储在一个地图对象(Product)中,这些对象计算起来有点昂贵。这些对象空间不便宜,所以我不想创建不必要的副本。映射属于一个Container类,它将提供对对象的访问。我做了一个代表我所拥有的东西的例子(代码也在这里找到:在线运行):

代码语言:javascript
复制
#include <iostream>
#include <map>
#include <string>
#include <utility>

class Product
{
private:
  std::string m_id, m_name;

public:
  Product () : m_id (), m_name () { std::cout << "\tProduct default constructor.\n"; }

  Product (const std::string & id, const std::string & name) : m_id (id), m_name (name)
  {
    std::cout << "\tProduct parameters constructor <" << m_id << ", " << m_name << ">.\n";
  }

  Product (const Product & copy) : m_id (copy.m_id), m_name (copy.m_name)
  {
    std::cout << "\tProduct copy constructor.\n";
  }
};

class Container
{
private:
  std::map<std::string, Product> m_products_cache;

public:
  Container () : m_products_cache () { }
  Container (const Container & copy) : m_products_cache (copy.m_products_cache) { }

  Container (const std::string & filename) : Container ()
  {
    // Simulate reading file and storing its contents in map with this:
    m_products_cache.insert (std::pair<const std::string, Product> ("A-001", Product ("A-001", "Product 1")));
    m_products_cache.insert (std::pair<const std::string, Product> ("A-002", Product ("A-002", "Product 2")));
  }

  const Product &
  CreateNewProduct (const std::string & id, const std::string & name)
  {
    std::map<std::string, Product>::iterator product_it = m_products_cache.find (id);

    if (product_it != m_products_cache.end ())
      return product_it->second; // Returns a const-reference to the Product

    std::pair<std::map<std::string, Product>::iterator, bool> inserted_it;
    inserted_it = m_products_cache.insert (std::pair<const std::string, Product> (id, Product (id, name)));

    return inserted_it.first->second; // Returns a const-reference to the Product
  }

  // Case (i)  :  Returns a const-reference.
  const Product &
  GetProductById (const std::string & id) const
  {
    std::map<std::string, Product>::const_iterator product_it = m_products_cache.find (id);

    if (product_it == m_products_cache.end ())
      throw std::out_of_range ("Error: Product ID'" + id + "' NOT found.\n");

    return product_it->second;
  }

  // Case (ii)  :  Uses an non-const reference parameter to return the Product, if found.
  bool
  GetProductById (const std::string & id, Product & product) const
  {
    std::map<std::string, Product>::const_iterator product_it = m_products_cache.find (id);

    if (product_it == m_products_cache.end ()) return false;

    product = product_it->second;
    return true;
  }

  // Case (iii)  :  Uses a const Product non-const pointer reference to expose the Product, if found.
  bool
  GetProductById (const std::string & id, const Product *& product) const
  {
    std::map<std::string, Product>::const_iterator product_it = m_products_cache.find (id);

    if (product_it == m_products_cache.end ())
      {
        product = nullptr;
        return false;
      }

    product = &product_it->second;
    return true;
  }
};

int
main (int argc, char **argv)
{
  Container container ("ignored_filename");

  const Product & created_product = container.CreateNewProduct ("B-003", "Product 3");
  std::cout << "\n - Created Product located at " << &created_product << "\n\n";

  std::cout << " - Obtain reference to Product in container:\n\n";

  const Product & product_reference = container.GetProductById ("B-003"); // Gets product at expected address.
  std::cout << "\tCase i  : product located at " << &product_reference
            << (&product_reference == &created_product ? " (Same object)" : " (Different object)") << "\n";

  Product product_param;
  container.GetProductById ("B-003", product_param); // Question 1: Gets a copy, but doesn't call copy constructor, WHY?
  std::cout << "\tCase ii : product located at " << &product_param
            << (&product_param == &created_product ? " (Same object)" : " (Different object)") << "\n";

  const Product *product_pointer_param;
  container.GetProductById ("B-003", product_pointer_param); // Gets product at expected address.
  std::cout << "\tCase iii: product located at " << product_pointer_param
            << (product_pointer_param == &created_product ? " (Same object)" : " (Different object)") << "\n";
}

它产生以下输出:

代码语言:javascript
复制
 - Created Product located at 0x6000727b8

 - Obtain reference to Product in container:

    Case i  : product located at 0x6000727b8 (Same object)
    Product default constructor.
    Case ii : product located at 0xffffcb40 (Different object)
    Case iii: product located at 0x6000727b8 (Same object)

我有以下问题:

  1. 问题1:这段代码不返回对0x6000727b8的引用,而是创建一个存储在0xffffcb40中的副本,但是没有调用Product的复制构造函数,为什么? // Case (ii):输出参考参数产品product_param;container.GetProductById ("B-003",product_param);
  2. 问题2:我想提供对映射中对象的访问,以避免创建对象的副本。我还必须用布尔值或异常来指示是否找到了Product。我更喜欢类似于case (ii)bool GetProductById (const std::string & id, Product & product)之类的东西,但这并不能保证一致性。 在我在代码中提供的三种情况中,还是从您的另一项建议中,哪个是实现它的最佳实践?
  3. 问题3:在本例中的中,存储在映射中的Product在存储后不会改变。但是,如果在将Products存储在映射中之后进行更改,那么问题2中的方法是否仍然适用?直接更改映射值对象是否是一种糟糕的做法?(我知道键一定是常量)。
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-11-06 02:19:18

  1. 您有两个对象:product_param和存储在映射中的对象。当您通过引用product_param分配给product时,它将使用赋值操作符Product::operator=(const Product&) (编译器为您提供,因为您没有告诉它不要)来分配内容。您仍然有两个对象,它们都有自己的id和名称。
  2. case (i)对于抛出异常的人来说是可以的。case (iii)有点尴尬,但很管用。另一个选择是const Product* GetProductById(const std::string& id) const。这可以返回指向对象的指针,如果不存在,则返回nullptr。不幸的是,除了返回值之外,它还具有与case (i)相同的原型,因此您需要有不同的函数名,或者想出一些其他方法来区分如何调用它们。 标准通常这样做的方式是将operator[](const Key& key)用于不抛出的版本,使用at(const Key& key)表示不抛出的版本。但是,一个返回指针,另一个返回引用会稍微有些尴尬。
  3. 这真的取决于你。没有什么可以阻止您更改映射的值,但是如果这不是您希望用户能够做的事情,那么始终可以将const引用或指向const的指针返回给用户。
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53164622

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档