【问题标题】:Preventing getting the address of items in a container in C++防止在 C++ 中获取容器中项目的地址
【发布时间】:2021-01-28 13:24:16
【问题描述】:

我正在编写一个可以动态增长和收缩的 C++ 类数组容器。我想阻止此容器的用户获取其项目的地址,因为当容器需要重新分配自身时,它们可能会被重新分配。使用此容器的唯一正确方法是跟踪容器的地址和每个项目的索引(是的,我将在文档中指定它,但如果我可以让编译器触发会更好如果容器的用户尝试获取项目的地址,则会出错?

这可以以某种方式完成吗?我搜索并发现了一些关于将“操作员地址”设为私有的问题,但似乎不能保证它可以工作,也不是推荐的做法。所以,我想知道是否有任何替代技术可以防止访问指向项目的指针......

【问题讨论】:

  • 不。 address-of 运算符的神秘重载将应用于容器中的类,而不是容器本身。如果容器中的对象没有重载它们的地址操作符,则容器对此无能为力。 C++ 不能以这种方式工作。
  • 只要您返回对所包含项目的引用,用户就能够获得所述项目的地址。
  • 正如其他人所提到的,除非您始终将副本返回到容器元素,否则实际上不可能实现它,但没有人会使用这样的容器。没有真正的好方法可以让每个人都满意来解决这个问题,您可以在元素被重定位并且指针失效时包含在文档中,或者您可以将元素保留在std::shared_ptr 中并按原样返回,但这会极大地影响性能。
  • 我想阻止此容器的用户获取其项目的地址 -- 您可以通过在容器中放入文档来阻止用户:危险.取容器中元素的地址是有风险的。如果用户不想注意记录的内容,那么如果事情没有解决,那就是他们的错。
  • 我建议阅读这个Q&A about iterator invalidation rules。 C++ 程序员习惯于指向容器中元素的指针和迭代器在某些操作下变得无效。通知用户何时以及哪些指针失效,这或多或少是你能做的。

标签: c++


【解决方案1】:

在 C++ 中,分配给程序的所有内存或多或少都是公平的游戏。因此,没有真正的方法可以阻止您的数组类型的用户获取或计算您的数组中的地址。

STL 甚至做了很多努力来保证迭代器不会因此而突然变得无效。

在现实世界中,您在 API 描述中写道,使用容器中的地址是非常不明智的,因为它可能随时变得无效,然后用户有责任遵守该规则,恕我直言。

【讨论】:

  • STL 确实试图不使迭代器失效,但它也准确地记录了它可能发生的位置。声明“可能随时失效”的“现实世界”API 完全无法使用。你甚至不能从容器中复制元素,因为在复制 ctor 运行时地址可能会变得无效!
【解决方案2】:

您想要做的事情并不容易(如果有的话)。 如果您的容器在某些情况下使指向元素的指针无效,那不会使您的容器“特别”。让我们考虑一下其他容器如何缓解这个问题。

std::vector:

 // THIS CODE IS BROKEN !! DO NOT WRITE CODE LIKE THIS !!
 std::vector<int> x(24,0);

 int* ptr = &x[0];
 auto it = x.begin();

 x.push_back(42);
 x.insert(it, 100);     // *
 *ptr = 5;
 *it = 7;

从标有* 的行开始,这里的所有内容都使用了无效的指针或迭代器。来自cppreferencepush_back

如果新的 size() 大于 capacity() 则所有迭代器和引用(包括过去的迭代器)都将失效。否则只有过去的迭代器无效。

实际上x.insert(it, 100); 可能使用了一个有效的迭代器,但是代码并没有检查push_back 是否必须增加容量,所以我们不得不假设itptr 在调用@ 之后是无效的987654333@.

insert:

如果新的 size() 大于旧的 capacity(),则导致重新分配。如果新的 size() 大于 capacity(),则所有迭代器和引用都无效。否则,只有插入点之前的迭代器和引用保持有效。过去的迭代器也失效了。

标准容器的用户必须了解迭代器失效规则(请参阅Iterator invalidation rules),否则他们将编写像上面那样严重损坏的代码。

一般而言,您无法保护自己免受用户可能犯的所有错误。记录前置条件和后置条件,如果用户忽略它们,他们只会得到他们应得的。

请注意,您可以尝试重载 &amp; 运算符,但是您无法阻止某人通过 std::addressof 获取地址,这正是为此而设计的:获取对象的地址以防万一对象试图通过重载&amp; 来阻止它。

【讨论】:

    猜你喜欢
    • 2017-11-21
    • 2016-07-13
    • 2016-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    相关资源
    最近更新 更多