( c++ 11)
我想要存储在一个地图对象(Product)中,这些对象计算起来有点昂贵。这些对象空间不便宜,所以我不想创建不必要的副本。映射属于一个Container类,它将提供对对象的访问。我做了一个代表我所拥有的东西的例子(代码也在这里找到:在线运行):
#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";
}它产生以下输出:
- 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)我有以下问题:
0x6000727b8的引用,而是创建一个存储在0xffffcb40中的副本,但是没有调用Product的复制构造函数,为什么?
// Case (ii):输出参考参数产品product_param;container.GetProductById ("B-003",product_param);Product。我更喜欢类似于case (ii):bool GetProductById (const std::string & id, Product & product)之类的东西,但这并不能保证一致性。
在我在代码中提供的三种情况中,还是从您的另一项建议中,哪个是实现它的最佳实践?Product在存储后不会改变。但是,如果在将Products存储在映射中之后进行更改,那么问题2中的方法是否仍然适用?直接更改映射值对象是否是一种糟糕的做法?(我知道键一定是常量)。发布于 2018-11-06 02:19:18
product_param和存储在映射中的对象。当您通过引用product_param分配给product时,它将使用赋值操作符Product::operator=(const Product&) (编译器为您提供,因为您没有告诉它不要)来分配内容。您仍然有两个对象,它们都有自己的id和名称。const Product* GetProductById(const std::string& id) const。这可以返回指向对象的指针,如果不存在,则返回nullptr。不幸的是,除了返回值之外,它还具有与case (i)相同的原型,因此您需要有不同的函数名,或者想出一些其他方法来区分如何调用它们。
标准通常这样做的方式是将operator[](const Key& key)用于不抛出的版本,使用at(const Key& key)表示不抛出的版本。但是,一个返回指针,另一个返回引用会稍微有些尴尬。https://stackoverflow.com/questions/53164622
复制相似问题