C ++中相邻节点地址的私有数组

时间:2014-12-20 14:04:21

标签: c++ graph edges

////编辑#2:删除了所有以前的信息,现在只发布工作代码。以前的问题变得过于冗长:

#include <iostream>
#include <vector>
using namespace std;

template<class T>
class Node{
    T data;
    vector<Node<T>*> adjacent;
    friend class Graph;
    public:
        int n;  
        Node(T initData) : data(initData), n(0){}
        void addAdjacent(Node<T>& other){
            adjacent.push_back(&other);
            n++;
        }
        T getData(){
            return data;
        }
        Node<T>* getEdge(int edgeNum){
            return adjacent[edgeNum];
        }
};
template<class T>
class GraphCl{
    int n;
    vector<Node<T>*> nodes;
    T input;
    public:
        GraphCl(int size): n(size){
            for (int i=0;i<n;i++){
                cout << "Enter data for node " << i << ": ";
                cin >> input; 
                nodes.push_back(new Node<T>(input)) ;
            }
        }
    void addEdge(int baseNode, int edgeNode){
        nodes[baseNode]->addAdjacent(*nodes[edgeNode]);
    }
    void printGraph(){
        for (int i=0;i<n;i++){
            Node<T> *base = nodes[i];
            cout << "Data of node " << i <<": "<< base->getData() <<endl;
            for (int j=0;j<base->n;j++){
                cout << "Edge #"<< j+1 << " of node " << i << ": " << base->getEdge(j) <<endl;
            }
        }
    }
};

int main(){
    GraphCl<int> *myGraph = new GraphCl<int>(5);
    myGraph->addEdge(0,1);
    myGraph->addEdge(0,2);
    myGraph->addEdge(0,3);
    myGraph->addEdge(0,4);
    myGraph->addEdge(3,1);
    myGraph->addEdge(3,0);
    myGraph->printGraph();
    return 0;
}

输出:

Enter data for node 0: -34
Enter data for node 1: 12
Enter data for node 2: 56
Enter data for node 3: 3
Enter data for node 4: 23
Data of node 0: -34
Edge #1 of node 0: 0x7fbeebd00040
Edge #2 of node 0: 0x7fbeebd00080
Edge #3 of node 0: 0x7fbeebe00000
Edge #4 of node 0: 0x7fbeebd000d0
Data of node 1: 12
Data of node 2: 56
Data of node 3: 3
Edge #1 of node 3: 0x7fbeebd00040
Edge #2 of node 3: 0x7fbeebd00000
Data of node 4: 23

正如您所看到的,这个简单的实现正在发挥作用。我决定删除所有复杂的东西,并通过动态变化的向量保持简单。显然效率较低,但我可以从这里开始工作。由于我是C ++的新手,之前的实现只是让我的头脑旋转360度,思考所有指向指针的位置,甚至没有考虑内存分配。上面的代码基本上是一个对输入错误非常敏感的有向图,所以我还是继续研究它。 谢谢你的帮助!

2 个答案:

答案 0 :(得分:1)

<强>辅助

关于数组对Graph的可访问性,与当前实现最接近的是将Graph声明为Node的朋友。只需添加:

friend Graph;

Node类声明的末尾。

也就是说,将课程设为friend有时候表明,如果课程需要对彼此了解太多,那么您定义的API并不完全正确。&#39;实施细节。您也可以为Node提供界面,例如:

void AddAdjacent(Node* other);

管理相邻节点

如果您希望adjacent指针数组可以增长,那么您基本上是在重新创建std::vector,因此我建议您使用std::vector<Node*>。使用默认(空)构造函数初始化向量会处理它,nodes[baseNode]->adjacent.push_back(...)中只需要addEdges

如果内存不是考虑因素,并且图中有最大数量的节点,则可以实例化一个恒定大小的数组。

如果你真的不想使用std::vector,但实际上你想要一个可增长的指针数组,那么你必须管理自己的malloc和{{1调用。我会写一些相关的东西,但我的建议是继续free

如果你很好奇,数组方法看起来像:

vector

插入:

template<class T>
class Node : public Graph{
    Node **adjacent; //pointer to array of POINTERS TO adjacent Nodes
    int n;
    size_t capacity;
    T data;
    friend Graph;

public:
    Node(T initData) : data(initData), capacity(8) {
        n = 0;
        adjacent = reinterpret_cast<Node**>(malloc(capacity * sizeof(Node**)));
    }
    ~Node() {
        free(adjacent);
    }
    void Grow() {
        size_t new_cap = base.capacity * 2;
        Node<int> **copy = reinterpret_cast<Node<int>**>(malloc(new_cap * sizeof(Node**)));
        memcpy(copy, base.adjacent, base.capacity); // copy and adjacent are non-overlapping, we can use memcpy
        free(base.adjacent);
        base.adjacent = copy;
        base.capacity = new_cap;
    }
};

答案 1 :(得分:0)

回答更新的问题

Node直接放在std::vector的情况下,存在一些问题。

使用std::vector对很多事情都很有用,但如果你这样做,你应该确保不要指向矢量。请记住,指针指的是存储对象存储位置的确切地址。 vector是一个可增长的元素容器。为了连续存储元素,向量分配一堆内存,将对象放在那里,如果必须增长,它将分配更多内存并移动对象。它本质上是在做Node中你正在做的事情并且增长(除非在它的情况下,它在释放旧内存之前明确地销毁了对象)。

请注意,您的Grow函数会分配新内存并复制指针。同时,矢量可以分配新内存并复制数据。这意味着保持指向向量中数据的指针是不好的。 vector给你的唯一保证是它的数据将继续使用数组样式的索引,查找,迭代等来访问,而不是数据将永远存在于同一个内存位置


解释您看到的确切错误

向量正在调用copy constructor。默认的复制构造函数逐个复制每个字段。在Node的情况下,这不是您想要的,因为那时您有两个向量认为它们拥有Node** adjacent内存位置。当第一个节点(旧副本)被销毁时,它将释放其相邻节点(与副本的相邻节点相同)。当新副本被销毁时,它将尝试释放相同的内存位置,但它已被释放。你在这里也有一个问题,如果你试图在第一个节点中销毁内存之后访问内存,你就会遇到麻烦。

当您只添加节点时,为什么会出现此错误?

当矢量增长到一定量时,需要调整大小。在大多数实现中,过程大致如下:

  1. 分配更多内存(通常是旧容量的两倍)
  2. 调用复制构造函数将元素从旧位置复制到新位置
  3. 销毁旧位置中的元素(例如,通过显式调用析构函数)
  4. 将新元素插入新位置
  5. 由于步骤2和3,您的错误正在显现。

    修复此特定错误

    对于您的情况,默认的复制构造函数并不好,因为复制节点应该符合所有数据的副本。 C ++中的常规副本将复制类或结构本身的所有数据。如果数据是指针,则复制指针,而不是指向它的东西。

    覆盖复制构造函数和赋值运算符:

        Node(const Node<T>& other) : data(other.data), capacity(other.capacity), n(other.n) {
            adjacent = reinterpret_cast<Node**>(malloc(capacity * sizeof(Node**)));
            memcpy(adjacent, other.adjacent, capacity * sizeof(Node**));
        }
    
        Node<T>& operator= (const Node<T>& other) {
            data = other.data;
            capacity = other.capacity;
            n = other.n;
            adjacent = reinterpret_cast<Node**>(malloc(capacity * sizeof(Node**)));
            memcpy(adjacent, other.adjacent, capacity * sizeof(Node**));
        }
    

    更大的问题

    代码的一个更大问题是使用std::vector 指向其元素的指针。选择以下之一:

    • 使用固定大小的数组(在内存中稳定),并指向这些对象
    • 完全忘记指针,并使你的相邻列表成为向量中的索引列表(每次你需要通过向量时性能较低,但这可能不会成为你现在的瓶颈)< / LI>