- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我一直在阅读和试验标准库的智能指针,unique_ptr
和 shared_ptr
,尽管在很多情况下它们显然是很好的替代品,其中原始指针可能是被认为是危险的我不确定在实现数据结构时它们的用途。
为了进行实验,我编写了一个使用 shared_ptr
的 HashMap 示例 - 根据 Meyer 的 Effective Modern C++,它的大小大约是 unique_ptr
的两倍。出于这个原因,我想使用 unique_ptr,但由于我在 Add 函数中执行的操作、更新和复制,我有点难过。
有人对这个问题有什么建议吗?数据结构是否仍应使用原始指针编写?
#pragma once
#include "core.h"
const int TABLE_SIZE = 256;
template<typename K>
class HashKey {
public:
unsigned long operator()(const K& p_key) const {
return (p_key) % TABLE_SIZE;
}
};
template<typename K, typename T>
class HashNode {
public:
K m_key;
T m_value;
std::shared_ptr<HashNode> next = nullptr;
};
template<typename K, typename T, typename F = HashKey<K>>
class HashMap {
public:
std::array< std::shared_ptr< HashNode<K, T> >, 128 > m_table;
F m_hash_function;
int m_elem_count{ 0 };
void Add(K p_key, T p_value);
};
template<typename K, typename T, typename F = HashKey<K>>
void HashMap<K, T, F>::Add(K p_key, T p_value)
{
unsigned long key = m_hash_function(p_key);
std::shared_ptr<HashNode<K, T>> new_node = std::make_shared<HashNode<K, T>>();
new_node->m_key = p_key;
new_node->m_value = p_value;
if (m_table[key] == nullptr) {
/* only item in the bucket */
m_table[key] = std::move(new_node);
m_elem_count++;
}
else {
/* check if item exists so it is replaced */
std::shared_ptr< HashNode<K, T> > current = m_table[key];
std::shared_ptr< HashNode<K, T> > previous = m_table[key];
while (current != nullptr && p_key != current->m_key ) {
previous = current;
current = current->next;
}
if (current == nullptr) {
previous->next = new_node;
//current = new_node;
m_elem_count++;
}
else {
current->m_value = p_value;
}
}
}
void TestHashMap() {
HashMap<int, std::string> hash_map;
hash_map.Add(1, "one");
hash_map.Add(2, "does");
hash_map.Add(3, "not");
hash_map.Add(50, "simply");
hash_map.Add(11, "waltz");
hash_map.Add(11, "into");
hash_map.Add(191, "mordor");
std::cout << hash_map.m_elem_count << std::endl;
}
最佳答案
智能指针的选择取决于您的数据结构如何“拥有”堆分配的对象。
如果您需要简单地观察而不是拥有一个对象(不管它是否是堆分配的),一个原始 指针,引用 或 std::reference_wrapper
是合适的选择。
如果您需要唯一所有权(最多一个堆分配对象的所有者),那么使用std::unique_ptr
.它没有额外的时间/内存开销。
如果您需要共享所有权(堆分配对象的任意数量的所有者),则使用std::shared_ptr
.它会导致额外的时间/内存开销,因为必须存储一个额外的指针(指向引用计数元数据),而且访问它保证是线程安全的。
不需要使用 std::unique_ptr
(代替原始指针) 除非您确实需要拥有该对象。
假设您需要拥有该对象,则无需使用 std::shared_ptr
(代替 std::unique_ptr
) 除非您确实需要共享所有权语义。
在您的情况下,您的 HashMap
中似乎有最大堆节点数.因此,我假设您想要HashMap
实例成为节点的唯一所有者。
你应该使用什么类型?
template<typename K, typename T, typename F = HashKey<K>>
class HashMap {
public:
std::array</* ? */, 128 > m_table;
// ...
};
你有两个选择:
如果要使用堆间接存储对象,请使用 std::unique_ptr
,因为这些堆分配对象的唯一所有者是并且将永远是HashMap
实例。
如果你想把对象直接存入HashMap
,没有堆间接寻址,那么根本不使用任何指针。这可能会导致很大的 HashMap
实例。用于访问 next
的界面节点变得繁琐。
选项 1(在堆中存储节点):
这是最常见的,也可能是最好的选择。
template<typename K, typename T, typename F = HashKey<K>>
class HashMap {
public:
std::array<std::unique_ptr<HashNode<K, T>>, 128 > m_table;
// ...
};
这将导致更轻的(在内存占用方面) HashMap
实例。
注意:使用 std::vector
代替 std::array
将减小 HashMap
的大小显着,但会引入额外的堆间接寻址。 这是实现类似数据结构的常用方法。您通常需要 HashMap
实例尽可能轻量级,以便可以有效地复制/移动/存储。
不需要使用智能指针来连接彼此之间的节点,因为节点由 HashMap
独占。 . 原始指针就足够了。
template<typename K, typename T>
class HashNode {
public:
// ...
HashNode* next_ptr = nullptr;
auto& next()
{
assert(next_ptr != nullptr);
return *next_ptr;
}
};
上面的代码可以正常工作,假设 HashMap
访问next
时还活着.
选项 2(在 map 实例中存储节点):
template<typename K, typename T, typename F = HashKey<K>>
class HashMap {
public:
std::array<HashNode<K, T>, 128 > m_table;
// ...
};
HashMap
实例可能很大,具体取决于 HashNode<K, T>
的大小.
如果您选择将节点直接存储到 HashMap
中没有堆间接寻址,您将不得不使用索引来访问内部数组,如移动/复制 HashMap
around 会改变节点的内存地址。
template<typename K, typename T>
class HashNode {
public:
// ...
int next_index = -1;
auto& next(HashMap& map)
{
assert(next_index != -1);
return map.m_table[next_index];
}
};
关于c++ - 实现数据结构时使用智能指针还是原始指针?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34660571/
我刚接触 C 语言几周,所以对它还很陌生。 我见过这样的事情 * (variable-name) = -* (variable-name) 在讲义中,但它到底会做什么?它会否定所指向的值吗? 最佳答案
我有一个指向内存地址的void 指针。然后,我做 int 指针 = void 指针 float 指针 = void 指针 然后,取消引用它们以获取值。 { int x = 25; vo
我正在与计算机控制的泵进行一些串行端口通信,我用来通信的 createfile 函数需要将 com 端口名称解析为 wchar_t 指针。 我也在使用 QT 创建一个表单并获取 com 端口名称作为
#include "stdio.h" #include "malloc.h" int main() { char*x=(char*)malloc(1024); *(x+2)=3; --
#include #include main() { int an_int; void *void_pointer = &an_int; double *double_ptr = void
对于每个时间步长,我都有一个二维矩阵 a[ix][iz],ix 从 0 到 nx-1 和 iz 从 0 到 nz-1。 为了组装所有时间步长的矩阵,我定义了一个长度为 nx*nz*nt 的 3D 指针
我有一个函数,它接受一个指向 char ** 的指针并用字符串填充它(我猜是一个字符串数组)。 *list_of_strings* 在函数内部分配内存。 char * *list_of_strings
我试图了解当涉及到字符和字符串时,内存分配是如何工作的。 我知道声明的数组的名称就像指向数组第一个元素的指针,但该数组将驻留在内存的堆栈中。 另一方面,当我们想要使用内存堆时,我们使用 malloc,
我有一个 C 语言的 .DLL 文件。该 DLL 中所有函数所需的主要结构具有以下形式。 typedef struct { char *snsAccessID; char *
我得到了以下数组: let arr = [ { children: [ { children: [], current: tru
#include int main(void) { int i; int *ptr = (int *) malloc(5 * sizeof(int)); for (i=0;
我正在编写一个程序,它接受一个三位数整数并将其分成两个整数。 224 将变为 220 和 4。 114 将变为 110 和 4。 基本上,您可以使用模数来完成。我写了我认为应该工作的东西,编译器一直说
好吧,我对 C++ 很陌生,我确定这个问题已经在某个地方得到了回答,而且也很简单,但我似乎找不到答案.... 我有一个自定义数组类,我将其用作练习来尝试了解其工作原理,其定义如下: 标题: class
1) this 指针与其他指针有何不同?据我了解,指针指向堆中的内存。如果有指向它们的指针,这是否意味着对象总是在堆中构造? 2)我们可以在 move 构造函数或 move 赋值中窃取this指针吗?
这个问题在这里已经有了答案: 关闭 11 年前。 Possible Duplicate: C : pointer to struct in the struct definition 在我的初学者类
我有两个指向指针的结构指针 typedef struct Square { ... ... }Square; Square **s1; //Representing 2D array of say,
变量在内存中是如何定位的?我有这个代码 int w=1; int x=1; int y=1; int z=1; int main(int argc, char** argv) { printf
#include #include main() { char *q[]={"black","white","red"}; printf("%s",*q+3); getch()
我在“C”类中有以下函数 class C { template void Func1(int x); template void Func2(int x); }; template void
我在64位linux下使用c++,编译器(g++)也是64位的。当我打印某个变量的地址时,例如一个整数,它应该打印一个 64 位整数,但实际上它打印了一个 48 位整数。 int i; cout <<
我是一名优秀的程序员,十分优秀!