【问题标题】:Can I get index directly from pointer of vector element in C++?我可以直接从 C++ 中向量元素的指针获取索引吗?
【发布时间】:2016-08-25 09:57:36
【问题描述】:

我有一个大型矢量容器,可容纳大约 300.000 个对象。我也有指向这些对象的指针。

有没有使用指针快速获取向量中对象索引的方法?

【问题讨论】:

  • 添加一些代码以便澄清。你说的是std::vector 还是老式数组?
  • 好吧,可能不是你想听到的答案......假设你在谈论 std::vector - 保持指针很容易出错。保留索引要好得多。原因是指针(或迭代器)在向量重新分配时变得无效,如果您将某些元素推入超过保留大小的内容,则会自动完成。还是您 110% 确定在确定指针后向量不会增长?这似乎是一个设计缺陷,如果不是现在,它可能会在将来咬你。

标签: c++ pointers vector


【解决方案1】:

由于向量是按顺序组织的,因此可以通过从指向元素的指针中减去指向初始元素的指针来获得索引:

std::vector<MyObject> vect;
MyObject *ptrX = ... // Pointer to element in question
ptrdiff_t index = ptrX - &vect[0];

【讨论】:

  • 假设operator&amp; 不是邪恶的过载。 std::addressof 解决了这个问题(但不确定它是否不会为空向量引入 UB ......)。
【解决方案2】:

Iterator header 在这种情况下应该很有用。

假设你有类似的东西:

using Vector = std::vector<Foo>;
using Iterator = Vector::iterator;

Vector big_vector;

现在你有了一个对象的迭代器:

Iterator p_obj = get_Theobj(big_vector);

使用distance可以轻松获取索引:

auto index = std::distance(big_vector.begin(), p_obj);
// Note: index's type is a `difference_type` aka ptrdiff_t (usually signed integer).

使用这种方法的强大之处在于其多功能性。事实上,它也适用于“类 C 向量”、std::arraystd::list

【讨论】:

    【解决方案3】:

    您可以使用std::distance

     std::vector<Object> objects = /*..*/;
     const Object *p = /* object[i] */;
     std::ptrdiff_t index = std::distance(objects.data(), p);
     // Now index == i.
    

    【讨论】:

    • 这将比较myObjects 中的每个元素,所以在线性时间内工作。
    • @BasileStarynkevitch:指针满足 RandomAccessIterator 要求,所以它是常数时间。
    • @Jarod42 你刚刚复制了我的答案:P
    • @BiagioFesta:你回答迭代器,我回答指针。 (我之前回答是看你的)。
    【解决方案4】:

    这里有很多很好的答案。将它们组合在一起,这是一个小型库套件,它允许通过指针或引用计算项目的索引。

    作为额外的好处,调用者可以选择提供一个策略对象,如果元素不在容器中,则执行该对象。

    #include <stdexcept>
    #include <cassert>
    #include <vector>
    
    
    struct exception_policy
    {
        [[noreturn]]
        std::size_t out_of_range() const
        {
            throw std::out_of_range("index_of_element");
        }
    };
    
    struct assertion_policy
    {
        std::size_t out_of_range() const
        {
            assert(!"out of range");
            return _fallback.out_of_range();
        }
    
        exception_policy _fallback {};
    };
    
    struct zero_policy
    {
        std::size_t out_of_range() const
        {
            return 0;
        }
    };
    
    template<class T, class A, class Policy = exception_policy>
    std::size_t index_of_element(std::vector<T, A> const& vec,
                                 typename std::vector<T, A>::const_pointer pitem,
                                 Policy policy = Policy{})
    {
        auto pbegin = vec.data();
        auto pend = pbegin + vec.size();
        if (pitem < pbegin or pitem >= pend)
        {
            return policy.out_of_range();
        }
        else
        {
            return std::distance(pbegin, pitem);
        }
    }
    
    template<class T, class A, class Policy = exception_policy>
    std::size_t index_of_element(std::vector<T, A> const& vec,
                          typename std::vector<T, A>::const_reference item, Policy policy = Policy{})
    {
        return index_of_element(vec, std::addressof(item), policy);
    }
    
    int main()
    {
        std::vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8 };
        auto px = std::addressof(v[5]);
        auto& rx = *px;
    
        try {
            // use default policy of throwing out_of_range...
            auto i = index_of_element(v, rx);
            assert(i == 5);
        }
        catch(...)
        {
            assert(!"should not throw");
        }
    
        try {
            auto i = index_of_element(v, px);
            assert(i == 5);
        }
        catch(...)
        {
            assert(!"should not throw");
        }
    
        auto py = v.data() + 1000;  // out of bounds
        try {
            auto i = index_of_element(v, py);
            assert(!"should have thrown");
        }
        catch(std::out_of_range const& e)
        {
            // success
        }
        catch(...)
        {
            assert(!"should not throw this");
        }
    
        // specify a custom policy
        auto i = index_of_element(v, ry, zero_policy());
        assert(i == 0);    
    }
    

    【讨论】:

    • @Jarod42 这在官方上是正确的,但我敢打赌 20 英镑,在您的系统上,它会产生对位于向量分配数据之外的对象的引用。这是一个测试夹具。我可以很容易地说 (v.data() + 1000),但我认为它不会那么富有表现力。重点(我希望)是函数 index_of_element() 捕获 out_of_bounds 元素(按地址,如此定义的行为)并用策略处理它们。
    • ...并修复了错字,以便在addressof(item) == addressof(*v.end()) 时比较正确失败
    【解决方案5】:
    int getIndex(Object* pObject) {return pObject - &(my_vewctor[0]);}
    

    【讨论】:

      【解决方案6】:

      假设至少 C++11:

      你有一些class Myclass;(希望是一个相当小的,因为你有很多实例)。

      你有一些变量std::vector&lt;Myclass&gt; bigvec;,它足够大,可以有大约一百万个对象。

      您有一些有效的指针 Myclass* ptr;,它可以指向来自 bigvec 的对象内部。

      您可能会在int ix;(或更好的是size_t ix;)中使用

      获得其索引在bigvec
      if (ptr >= bigvec.data() && ptr < bigvec.data() + bigvec.size())
        ix = ptr - bigvec.data();
      

      但是,请注意,您可以制作Myclass副本(例如,通过赋值、按值传递某个参数等...),然后获得指向此类副本的指针不要在bigvec

      【讨论】:

      • 如果我没记错的话,如果你处理std::vector&lt;Myclass*&gt;,而不是std::vector&lt;Myclass&gt;,这将起作用
      • 相反,它不适用于指针向量。你需要一个对象向量。
      • 不需要考虑sizeof(MyClass)吗? (是的,它也不适用于指针向量)
      • 不,指针运算总是以对象大小为增量进行的(在 C 或 C++ 中)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-14
      • 1970-01-01
      • 1970-01-01
      • 2010-12-27
      相关资源
      最近更新 更多