c++ - std::map 和 boost::ptr_map 模板和继承的返回值变坏了

标签 c++ stdmap boost-ptr-container

在我工作的公司,我们创建了一个名为“RestrictedMap”的类。这提供与常规 std::map 相同的接口(interface),但不允许您使用 [] 运算符。还提供了一些其他功能来舒适地与类一起工作。该类在内部包装了一个 std::map。

我现在正在尝试创建一个类似的类,它对 boost::ptr_map 执行相同的操作,称为“RestrictedPointerMap”。为此,我创建了 RestrictedMapBase,它接受作为模板参数的 map 类型,它应该包装并包含大部分实现。两个类派生它并指定要包装的 map 类型:

  • RestrictedMap 与我们以前拥有的功能相同并包装 std::map
  • RestrictedPointerMap 是新的并且包装了 boost::ptr_map。

这是代码,为了完整性我没有简化类,但稍后我会命名相关函数。

RestrictedMap.h

    #pragma once

    #include <boost/ptr_container/ptr_map.hpp>
    #include <boost/static_assert.hpp>
    #include <boost/assign/list_of.hpp>
    #include <boost/foreach.hpp>
    #include <map>

    /**
    * Class that has the benefits of a map, but does not add an entry if it does not exists. 
    * Do not use RestrictedMapBase directly but use one of the derived classes (RestrictedMap or RestrictedPointerMap).
    */
    template <typename MAP>
    class RestrictedMapBase
    {
    public:
       RestrictedMapBase(const MAP& map): 
          m_map(map)
       {}

       template<class InputIterator>
       RestrictedMapBase(InputIterator first, InputIterator last): 
          m_map(first, last)
       {}

       RestrictedMapBase()
       {}

       /************************************************************************/
       /* std::map interface                                                   */
       /************************************************************************/

       typedef typename MAP::iterator iterator;
       typedef typename MAP::const_iterator const_iterator;
       typedef typename MAP::value_type value_type;
       typedef typename MAP::key_type key_type;
       typedef typename MAP::mapped_type mapped_type;
       typedef typename MAP::size_type size_type;

       iterator begin() { return m_map.begin(); }
       iterator end() { return m_map.end(); }
       const_iterator begin() const { return m_map.begin(); }
       const_iterator end() const { return m_map.end(); }
       bool empty() const { return m_map.empty(); }
       size_type size() const { return m_map.size(); }
       iterator find(const key_type& key) { return m_map.find(key); } 
       const_iterator find(const key_type& key) const { return m_map.find(key); }
       void clear() { m_map.clear(); }
       void erase(iterator where) { m_map.erase(where); }

       bool operator==(const typename RestrictedMapBase<MAP>& other) const { return m_map == other.m_map; }
       bool operator!=(const typename RestrictedMapBase<MAP>& other) const { return m_map != other.m_map; }
       bool operator<(const typename RestrictedMapBase<MAP>& other) const { return m_map < other.m_map; }

       /************************************************************************/
       /* extra                                                                */
       /************************************************************************/

       void erase(const key_type& key)
       {
          iterator iter(find(key));
          assert(found(iter));
          erase(iter);
       }

       void eraseIfExists(const key_type& key)
       {
          m_map.erase(key);
       }

       bool exists(const key_type& key) const
       {
          return found(find(key));
       }

       mapped_type& getValue(const key_type& key)
       {
          return const_cast<mapped_type&>(static_cast<const RestrictedMapBase<MAP>&> (*this).getValue(key));
       }

       const mapped_type& getValue(const key_type& key) const
       {
          const_iterator iter(find(key));
          assert(found(iter));
          return getData(iter);
       }

       mapped_type getValueIfExists(const key_type& key) const
       {
          BOOST_STATIC_ASSERT(boost::is_pointer<mapped_type>::value);
          const_iterator iter(find(key));
          if (found(iter)) {
             return getData(iter);
          } else {
             return 0;
          }
       }

       void setValue(const key_type& key, const mapped_type& value)
       {
          iterator iter(find(key));
          assert(found(iter));
          setData(iter, value);
       }

       void add(const key_type& key, const mapped_type& value)
       {
          assert(!exists(key));
          insert(key, value);
       }

       void add(const RestrictedMapBase<MAP>& mapToAdd)
       {
          BOOST_FOREACH(value_type element, mapToAdd.m_map)
          {
             add(element.first, element.second);
          }
       }

       void addOrReplace(const key_type& key, const mapped_type& value)
       {
          iterator iter(find(key));
          if (found(iter)) {
             setData(iter, value);
          } else {
             insert(key, value);
          }
       }

       mapped_type* addDefaultConstructed(const key_type& key)
       {
          assert(!exists(key));
          return &m_map[key];
       }

    private:
       bool found(const const_iterator& iter) const 
       {
          return iter != end();
       }

       const mapped_type& getData(const const_iterator& iter) const
       {
          return const_cast<const mapped_type&>(iter->second);
       }

       mapped_type& getData(const iterator& iter)
       {
          return const_cast<mapped_type&>(static_cast<const RestrictedMapBase<MAP>&>(*this).getData(iter));
       }

       void setData(const iterator& iter, const mapped_type& value)
       {
          getData(iter) = value;
       }

       virtual void insert(const key_type& key, const mapped_type& value) = 0;

    protected:
       MAP& getMap()
       {
          return m_map;
       }

    private:
       MAP m_map;
    };

    template <typename KEYTYPE, typename DATATYPE>
    class RestrictedMap: public RestrictedMapBase<std::map<KEYTYPE, DATATYPE> >
    {
    public:
       RestrictedMap(const std::map<typename KEYTYPE, typename DATATYPE>& map): RestrictedMapBase(map)
       {}

       template<class InputIterator>
       RestrictedMap(InputIterator first, InputIterator last): RestrictedMapBase(first, last)
       {}

       RestrictedMap()
       {}

       virtual void insert(const KEYTYPE& key, const DATATYPE& value)
       {
          getMap().insert(std::make_pair(key, value));
       }
    };

    template <typename KEYTYPE, typename DATATYPE>
    class RestrictedPointerMap: public RestrictedMapBase<boost::ptr_map<KEYTYPE, DATATYPE> >
    {
    public:
       RestrictedPointerMap(const boost::ptr_map<typename KEYTYPE, typename DATATYPE>& map): RestrictedMapBase(map)
       {}

       template<class InputIterator>
       RestrictedPointerMap(InputIterator first, InputIterator last): RestrictedMapBase(first, last)
       {}

       RestrictedPointerMap()
       {}

       virtual void insert(const KEYTYPE& key, DATATYPE* const& value)
       {
          /* boost::ptr_map::mapped_type does not equal the DATATYPE template parameter passed to it. Therefore this 
           * functions signature *looks* different from the RestrictedMapBase::insert signature */
          getMap().insert(key, std::auto_ptr<DATATYPE>(value));
       }
    };

这主要起作用,除非我想在 RestrictedPointerMap 上调用 getValue。函数 getData 返回正确的值,但之后它在 getValue 函数中出错。它返回一个错误的指针(因为指针是错误的)。

下面是一些重现问题的代码:

测试类.h

    #pragma once

    class SomeClass
    {
    public:
       SomeClass();
       virtual ~SomeClass();
    };

测试类.cpp

    #include "stdafx.h"
    #include "TestClass.h"
    #include <iostream>

    SomeClass::SomeClass()
    {
       std::cout << "TestClass[" << this << "] created." << std::endl;
    }

    SomeClass::~SomeClass()
    {
       std::cout << "TestClass[" << this << "] deleted." << std::endl;
    }

TestRestrictedPtrMap.cpp(主)

    #include "stdafx.h"

    #include "RestrictedMap.h"
    #include "TestClass.h"
    #include <boost/foreach.hpp>

    int _tmain(int argc, _TCHAR* argv[])
    {
       typedef RestrictedPointerMap<int, SomeClass> MapType;
       MapType theMap;
       theMap.add(1, new SomeClass());
       theMap.add(2, new SomeClass());

       BOOST_FOREACH(MapType::value_type mapEntry, theMap) {
          std::cout << mapEntry.first << " = " << mapEntry.second << std::endl;
       }

       SomeClass* oneClass = theMap.getValue(1);
       std::cout << oneClass << std::endl;
       SomeClass* twoClass = theMap.getValue(2);
       std::cout << twoClass << std::endl;

       std::cin.get();
        return 0;
    }

这个的输出是:

    TestClass[0078A318] created.
    TestClass[0078A448] created.
    1 = 0078A318
    2 = 0078A448
    0018FBD4
    0018FBD4
    TestClass[0078A318] deleted.
    TestClass[0078A448] deleted.

我不知道为什么会出错。据我所知,返回值变坏了。

在此先感谢您的帮助,

汤姆

最佳答案

您有一个悬空引用。

当您取消引用 boost::ptr_map<Key, T>::iterator 时它即时构建一个 boost::ptr_container_detail::ref_pair<Key, T *>实际 底层迭代器(std::map<Key, void *>::iterator)初始化。这意味着 T *& (或 const T *& )从 getData 返回正在引用本地临时成员(seconditer->second 成员):

   const mapped_type& getData(const const_iterator& iter) const
   {
      return const_cast<const mapped_type&>(iter->second); // reference to a temporary
   }
                                            ^^^^^^ *iter is a temporary value

这不同于正常的 std::map , 其中*iter给出对映射的二叉树中节点的值子对象的引用。

如果不显着改变您的界面,就没有简单的解决方案,因为没有实际的 T *对象在内存中的任何地方引用。您最好更改 RestrictedPointerMap 的签名返回 T通过值指针或什至通过直接引用映射值:

T *getValue(const key_type& key);               // not T *&
const T *getValue(const key_type& key) const;   // not const T *const &
// or
T &getValue(const key_type& key);               // not T *&
const T &getValue(const key_type& key) const;   // not const T *const &

关于c++ - std::map 和 boost::ptr_map 模板和继承的返回值变坏了,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13533042/

相关文章:

c++ - 克隆分配器和 boost::ptr_container 中的多态性

c++ - std::set 和 boost::ptr_set 之间的区别?

c++ - 为什么我的删除节点功能不起作用?

C++,引用 "constructor"

c++ - 如何对小数位进行分组?

C++:将元素插入 std::map<MyStruct> ,其中 MyStruct 只能聚合初始化并包含 const 唯一指针

c++ - 像数组一样访问 std::map 的 std::map

c++ - STL算法删除容器中的所有对象?

c++ - GetComputerNameW 导致 Windows 10 中的缓冲区溢出

c++ - std::for_each 是按值传递还是按引用传递?