class 构造中的 C++ 指针

C++ pointers in class costruction

几天来我一直在努力解决这个问题。

在这个程序中有一个unordered_map从字符串键到结构值。这个想法可行,但是当我把 unordered_map 放在 class 中然后更容易地访问它时,我得到一个错误:

Eliminare.exe!std::_Hash<std::_Umap_traits<std::string,Data,std::_Uhash_compare<std::string,std::hash<std::string>,std::equal_to<std::string>>,boost::interprocess::allocator<std::pair<std::string,Data>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>,0>>::_Find_last<std::string>(const std::string & _Keyval, const unsigned __int64 _Hashval) Row 1510    C++
    Eliminare.exe!std::unordered_map<std::string,Data,std::hash<std::string>,std::equal_to<std::string>,boost::interprocess::allocator<std::pair<std::string,Data>,boost::interprocess::segment_manager<char,boost::interprocess::rbtree_best_fit<boost::interprocess::mutex_family,boost::interprocess::offset_ptr<void,__int64,unsigned __int64,0>,0>,boost::interprocess::iset_index>>>::at(const std::string & _Keyval) Riga 387    C++
Eliminare.exe!MapCreator::getValue(std::string Key) Row 37  C++
Eliminare.exe!main() Row 7  C++

这是主要的:

#include "MapCreator.h"

int main() {
    MapCreator mappaClass;

    auto values = mappaClass.getValue("value");
}

虽然这是在 MapCreator.h:

#pragma once
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <unordered_map>
#include<iostream>
typedef struct Data
{
    int a;
    int b;
}dataClass;

namespace bost = boost::interprocess;
typedef std::string    KeyType;
typedef dataClass MappedType;
typedef std::pair<std::string, dataClass> ValueType;
typedef bost::allocator<ValueType, bost::managed_shared_memory::segment_manager> ShmemAllocator;
typedef std::unordered_map<KeyType, MappedType, std::hash<KeyType>, std::equal_to<KeyType>, ShmemAllocator> MySHMMap;

class MapCreator
{
public:
    MySHMMap* mappa = nullptr;
    MapCreator() {
        bost::shared_memory_object::remove(nameMemory);
        bost::managed_shared_memory segment(bost::create_only, nameMemory, sizeDeclared);
        ShmemAllocator alloc_inst(segment.get_segment_manager());

        mappa = segment.construct<MySHMMap>("MySHMMapName")(alloc_inst);
        dataClass value;
        value.a = 12;
        value.b = 1111;
        mappa->insert(std::pair<std::string, dataClass>("value", value));
        std::cout << mappa->at("value").a;

    }
    dataClass getValue(std::string Key) {
        return mappa->at(Key);
    }
private:
    const int sizeDeclared = 65536;
    const char nameMemory[20] = "SharedMemoryName";
};

MapCreator构造函数中的“cout”正确打印“12”,但使用getValue函数,打印出上述错误。

感谢您的帮助,如果您对代码有任何疑问,请提出。

信息:

存在多个问题。

第一个是 segment 是构造函数中的局部变量,这意味着 mappa 是构造函数 returns 之后的悬空指针。

其次,更大的问题是您的地图正确使用了共享内存分配器,但字符串却没有。字符串也是动态分配的,因此除非您使字符串也使用共享内存,否则您的解决方案将无法工作。

开始修复:

namespace bip = boost::interprocess;
namespace bc  = boost::container;
using Segment = bip::managed_shared_memory;
using Mgr     = Segment::segment_manager;

template <typename T> using Alloc = bip::allocator<T, Mgr>;
using MyString   = bc::basic_string<char, std::char_traits<char>, Alloc<char>>;
using KeyType    = MyString;
using MappedType = dataClass;
using ValueType  = std::pair<KeyType const, MappedType>;
using MySHMMap   = std::unordered_map<KeyType, MappedType, std::hash<MyString>,
                                    std::equal_to<MyString>, Alloc<ValueType>>;

Note that on my compiler it's also required to use KeyType const in the ValueType.

在执行中有额外的复杂性:segment 必须是一个字段,但它只能从初始化列表构造(或使用 NSMI)。这意味着我们必须让 remove 也出现在初始化列表中(在那之前)。这是我的初步尝试:

template <typename T> using Alloc = bip::allocator<T, Mgr>;
using MyString   = bc::basic_string<char, std::char_traits<char>, Alloc<char>>;
using KeyType    = MyString;
using MappedType = dataClass;
using ValueType  = std::pair<KeyType const, MappedType>;
using MySHMMap = boost::unordered_map<KeyType, MappedType, boost::hash<MyString>,
                         std::equal_to<MyString>, Alloc<ValueType>>;

class MapCreator {
  public:
    MapCreator()
        : mappa(segment.find_or_construct<MySHMMap>("MySHMMapName")(
              segment.get_segment_manager()))
    {
        mappa->emplace(MyString("value", segment.get_segment_manager()),
                       dataClass{12, 1111});
    }

    dataClass getValue(std::string key) const {
        return mappa->at(MyString(key.c_str(), segment.get_segment_manager()));
    }

  private:
    static constexpr int sizeDeclared = 65536;

    // note: declaration order defines initialization order!
    std::string_view nameMemory = "SharedMemoryName";

    struct pre_remover {
        pre_remover(const char* name)
        {
            bip::shared_memory_object::remove(name);
        }
    } pre_remove{nameMemory.data()};

    Segment segment{bip::create_only, nameMemory.data(), sizeDeclared};
    MySHMMap* mappa = nullptr;
};

Notes

  • there was also no reason at all to allocate a fixed size buffer of 20 chars for the name.
  • The order in which members are declared is the order in which they're initalized. That's important because mappa needs to come after segment, e.g.

我发现 std::hash 实际上并不专门用于 MyString 实例,因为我知道我想改变它,无论如何我不会浪费时间创建一个哈希函数对象。相反,我切换到 boost::unordered_map 只是为了使用 boost::hash<>

这已经在工作了Live On Coliru

打印:

12, 1111

改进

  1. 作用域分配器

    allocator/get_segment_manager() 周围的东西相当笨拙。使用 scoped-allocators 你可以缓解:

    template <typename T>
    using Alloc      = bc::scoped_allocator_adaptor<bip::allocator<T, Mgr>>;
    using MyString   = bc::basic_string<char, std::char_traits<char>, Alloc<char>>;
    using KeyType    = MyString;
    
    template <typename K, typename V, typename H = boost::hash<K>,
              typename Eq = std::equal_to<K>>
    using SHMap = boost::unordered_map<K, V, H, Eq, Alloc<std::pair<K const, V>>>;
    
    using MySHMMap = SHMap<MyString, Data>;
    

    现在你可以“简单地”写:

    mappa->emplace("value", Data{12, 1111});
    

    代替之前复杂的版本。 Live On Coliru

  2. 异构键查找

    我们正在构造MyString的另一个地方(只是为了在at()调用之后扔掉它......),我们可以使用boost::unordered_mapcompatible-key查找:

    Data getValue(std::string_view key) const {
        auto it = mappa->find(key, mappa->hash_function(), mappa->key_eq());
        return it != mappa->end() ? it->second
                                  : throw std::range_error("getValue");
    }
    

    这要求散列和相等性兼容:

    using MySHMMap = SHMap<MyString, Data, std::hash<std::string_view>,
                           std::equal_to<std::string_view>>;
    

    看到了Live On Coliru

  3. 改为使用 C++20 标准 unordered_map(参见 https://en.cppreference.com/w/cpp/container/unordered_map/find):

    struct Hash : std::hash<std::string_view> {
        using is_transparent = std::true_type;
    };
    struct Eq : std::equal_to<std::string_view> {
        using is_transparent = std::true_type;
    };
    using MySHMMap = SHMap<MyString, Data, Hash, Eq>;
    

    然后:

        Data getValue(std::string_view key) const {
    #ifdef __cpp_lib_generic_unordered_lookup
            auto it = mappa->find(key);
    #else
            auto it = mappa->find(
                MyString(key.data(), key.size(), segment.get_segment_manager()));
    
    #endif
            return it != mappa->end() ? it->second
                                      : throw std::range_error("getValue");
        }
    

    也看看Live On Coliru