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
函数,打印出上述错误。
感谢您的帮助,如果您对代码有任何疑问,请提出。
信息:
- 编译器:Visual C++ Visual Studio 2022
- 没有 class 的代码运行良好
- boost的版本class是:1.78.0
存在多个问题。
第一个是 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
改进
作用域分配器
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
异构键查找
我们正在构造MyString
的另一个地方(只是为了在at()
调用之后扔掉它......),我们可以使用boost::unordered_map
的compatible-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>>;
改为使用 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");
}
几天来我一直在努力解决这个问题。
在这个程序中有一个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
函数,打印出上述错误。
感谢您的帮助,如果您对代码有任何疑问,请提出。
信息:
- 编译器:Visual C++ Visual Studio 2022
- 没有 class 的代码运行良好
- boost的版本class是:1.78.0
存在多个问题。
第一个是 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 theValueType
.
在执行中有额外的复杂性: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 aftersegment
, e.g.
我发现 std::hash
实际上并不专门用于 MyString
实例,因为我知道我想改变它,无论如何我不会浪费时间创建一个哈希函数对象。相反,我切换到 boost::unordered_map
只是为了使用 boost::hash<>
这已经在工作了Live On Coliru
打印:
12, 1111
改进
作用域分配器
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
异构键查找
我们正在构造
MyString
的另一个地方(只是为了在at()
调用之后扔掉它......),我们可以使用boost::unordered_map
的compatible-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>>;
改为使用 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"); }