【问题标题】:Create a vector of pairs, where the second element of a pair points to the next pair in the vector创建一个对的向量,其中一对的第二个元素指向向量中的下一个对
【发布时间】:2021-03-24 07:37:42
【问题描述】:

我需要创建一个向量或类似的配对列表,其中配对的第一个元素属于 T 类,第二个元素是指向下一个配对的指针。 Illustration

template<class T>
std::vector<std::pair<T, T*>> createPointingVector(std::vector<T> vec) {
    std::vector<std::pair<T, T*>> new_vec;
    for (int i=0; i<vec.size(); i++){
        new_vec.push_back(std::make_pair(vec[i], &(vec[i - 1])));
    }
    return new_vec;
}

我知道 std::vector&lt;std::pair&lt;T, T*&gt;&gt; 不正确,因为该对的第二个元素不应该是 *T 类型,而是递归 *std::pair&lt;T, *std::pair&lt;T, *std::pair&lt;T, ...&gt;&gt;&gt;

是否可以修复我的代码或实现目标的其他方法(如果有)?

【问题讨论】:

  • new_vec.push_back(std::make_pair(vec[i], &amp;(vec[i - 1]))); -- 存储指向向量元素的指针是一场等待发生的灾难,因为在向量上调用push_back 会使指向元素的指针无效。在我看来,那张图片只不过是一个简单的链表,而这对只是隐藏了这一点,即
  • 你为什么要这个?我的意思是用例是什么?可能有更好的解决方案。
  • @PaulMcKenzie 我尝试创建 size = vec.size() 的 new_vector 并且不使用 push_back,但这并没有解决主要问题。 “链表”?会调查的。 JHBonarius 我不知道,我不会那样做。但这是外教给的任务。
  • 图示只是变相的单链表:template &lt;typename T&gt; struct Node (T data; Node&lt;T&gt;* next; };,在C++中封装在std::forward_list&lt;T&gt;中。
  • 危险,蚂蚁澳大利亚!危险!!如果发生重新分配,所有与容器相关的迭代器、指针和引用都将失效。参见Iterator invalidation rules

标签: c++ pointers templates vector std-pair


【解决方案1】:

我强烈建议重新考虑使用裸 vector
我这样做的原因是您需要保证永远不会重新分配向量的内存。请注意,在任何情况下,您还应确保您的向量确保从一开始就分配所有需要的内存,方法是使用空元素初始化或使用std::vector::reserve

否则,如果您已经设置了指针,然后更改了向量的容量,则指针将变为无效,如果您想要未定义的行为,这是一个很好的设置。

因此,我强烈建议您在向量周围使用包装类,以确保不会调用任何容量更改。


现在,如果您这样做,问题是,您为什么要使用实际指针?
考虑使用 std::vector&lt;std::pair&lt;T, size_t&gt; &gt; 类型的数据,第二个条目实际上存储向量中的位置,而不是实际的指针:

template<class T>
class PointingVector
{
    public:
        PointingVector(const std::vector<T>& vec);
    private:
        std::vector<std::pair<T, size_t> > data;
};

template<class T>
PointingVector<T>::PointingVector(const std::vector<T>& vec)
{
    for (int i=0; i<vec.size()-1; i++)
    {
        data.push_back(std::make_pair(vec[i], i+1));
    }
    data.push_back(std::make_pair(vec.back(), 0)); // assuming that the last points to the first
}

之后,确保您添加的每个附加方法都保持指向一致。就像你应该写类似erase 的东西一样,确保所有对都相应地更新。

与取消引用的类比是微不足道的:

template<class T>
std::pair<T, size_t>& PointingVector<T>::get(size_t index)
{
    return data[index];
}

关于我的解决方案的重要一点是,您可以排除与悬空指针有关的可能错误。这些真的很糟糕,尤其是考虑到未定义行为的性质,它们可能不会在测试执行中导致错误。我的解决方案中最糟糕的是,在调用有错误的方法后索引是错误的。 如果你想引入任何改变向量容量的东西,没问题,不需要重做任何指针。只需确保相应地更改索引即可。如果您使用指针执行此操作,那么您的第一步可能是无论如何创建一个索引列表,那么为什么不直接使用一个。
另外,由于该解决方案根本没有(可见的)指针,因此您不需要进行任何内存管理。


另一种解决方案:抛弃 std::pair 并定义自己的类型:

template<class T>
struct Node
{
    T data;
    Node* next; // or a smart pointer type
    Node(const T& data, Node* next) : data(data), next(next) {}
};

然后像这样构建你的向量:

template<class T>
std::vector<Node<T>*> createPointingVector(const std::vector<T>& vec) 
{
    std::vector<Node<T>*> new_vec;
    for (int i=0; i<vec.size(); i++)
    {
        new_vec.push_back(new Node<T>(vec[i], nullptr));
    }
    for (int i=0; i<vec.size()-1; i++)
    {
        new_vec[i]->next = new_vec[i+1];
    }
    new_vec[vec.size()-1]->next = new_vec[0];

    return new_vec;
}

请注意,如果没有智能指针,您需要进行内存管理。我会考虑将next 设为weak_ptr&lt;Node&gt;,并让向量超过shared_ptr&lt;Node&gt;。这样,一旦向量被删除,内存就会自动释放(假设您没有其他活动指针)。

【讨论】:

    【解决方案2】:

    您提出的问题是可行的,但根据您的答案中链接的插图,指针应在输入 vector 内循环指向一个向上,而不是像您的代码示例中那样指向一个向下。我的意思是:

    new_vec[0] = {vec[0], &vec[1]}

    new_vec[1] = {vec[1], &vec[2]}

    ...

    new_vec[N-1] = {vec[N-1], &vec[0]}

    以上,N = vec.size()

    我附上一个最小的工作示例:

    #include <iostream>
    #include <vector>
    #include <utility>  // std::pair, std::make_pair
    
    template<class T>
    std::vector<std::pair<T, T*> > createPointingVector(std::vector<T>& vec) { // important: make the parameter a reference
        std::vector<std::pair<T, T*> > new_vec;
        int vec_size = vec.size();
    
        for (int i = 0; i < vec_size-1; i++)
            new_vec.push_back(  std::make_pair( vec[i], &(vec[i + 1]) )  );    // pointers assigned according to linked picture
        new_vec.push_back(  std::make_pair( vec[vec_size-1], &vec[0] )  );
    
        return new_vec;
    }
    
    
    int main()
    {
        std::vector<int> input = {1,2,3,4};
        std::vector<std::pair<int,int*> > sol = createPointingVector(input);
        for (auto i : sol)
            std::cout << i.first << " -> " << *(i.second) << std::endl;
    
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-09-25
      • 1970-01-01
      • 1970-01-01
      • 2019-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多