Какой правильный конструктор для вектора списков

0

Я рассматривал некоторые примеры того, как создать правильный конструктор для векторов, и им все еще путают, как его реализовать. Используя данный класс...

class Graph
{
private:
    class Edge
    {
    public:
        Edge(string vertex, int weight)
        {
            m_vertex = vertex;
            m_weight = weight;
        }
        ~Edge(){}
        string m_vertex;
        int m_weight;
    };
    vector< list<Edge> > adjList; //vector of lists
public:
    Graph();
    ~Graph();
    vector < list < Edge > > get_adjList(){return adjList;}
    //Other functions....

};

Я все еще не уверен, что добавить в конструктор Graph::Graph() Я попытался сделать несколько вещей, таких как

Graph::Graph()
{
    new vector < list < Edge > >;
}

Но мне сказали, что это вызывает утечку памяти. Ive только когда-либо делал конструкторы для связанных списков раньше и никогда не нуждался в одном для векторов до сих пор. Я мог бы использовать некоторую ясность в том, как создать конструктор для вектора списков. Мне нужен конструктор, поэтому я могу получить доступ к элементу списка (списка векторов) в других файлах, чтобы его скопировать.

  • 1
    Ваш new оператор вызывает утечку памяти, потому что он создает vector< list< Edge > > но нигде не захватывает указатель на него. Это просто выбрасывает это. Член adjList в вашем классе выделяется для вас, когда вы выделяете Graph , поэтому нет необходимости создавать new отдельный в вашем конструкторе.
  • 2
    Я также скептически отношусь к тому, что ваш get_adjList() должен возвращать новую новую копию adjList . Похоже, что он должен возвращать ссылку на тот, что внутри Graph . То есть, похоже, что возвращаемый тип должен быть vector< list< Edge > >& .
Показать ещё 3 комментария
Теги:
oop
vector
constructor

4 ответа

3
Лучший ответ

В вашем Graph уже есть элемент-член vector< list< Edge > > adjList который выделяется и конструируется для вас при построении Graph. Вам не нужно писать какой-либо конструктор.

Вызов new здесь можно полностью удалить:

Graph::Graph()
{
    new vector < list < Edge > >;  // DELETE THIS LINE
}

Как бы то ни было, он выделяет полностью отдельный vector< list< Edge > > и затем отбрасывает указатель, возвращенный new. Но, судя по всему, вы все равно не нуждались в этом. Ваш член adjList уже существует, пуст и ждет данных. Вам не нужно было вызывать new или что-то еще.

То, что вам нужно, - это некоторый способ заполнить член adjList. Вы можете сделать это в Graph::Graph() или вне кода.

Ваша текущая функция getter, get_adjList() настоящее время возвращает копию adjList, а не ссылку. Возможно, вы захотите изменить этот метод, чтобы вернуть ссылку:

vector < list < Edge > >& get_adjList() {return adjList;}

Или указатель:

vector < list < Edge > >* get_adjList() {return &adjList;}

так что код вне класса может вызвать get_adjList() а затем манипулировать полем внутри класса.

Что-то вроде этого:

Graph my_graph;

vector< list<Edge> >& adjList = my_graph.get_adjList();

// Make a graph with two nodes, both pointing at each other, with weight 10
adjList.resize( 2 );  // make room for two nodes
adjList[0].push_back( Edge( 1, 10 ) );
adjList[1].push_back( Edge( 0, 10 ) );

Чтобы ваши пальцы не отваливались от наложения vector< list<Edge> > снова и снова, вы можете добавить typedef внутри class Graph:

class Graph
{
    public:
        typedef vector< list< Edge > > adj_type;

    // ...
};

А затем снаружи вы могли бы сказать что-то вроде:

Graph::adj_type& adjList = my_graph.get_adjList();

Это также изолирует остальную часть кода от точного определения adj_type если вы измените его позже.

Вы должны в конечном итоге рассмотреть возможность добавления методов в Graph для добавления вершин и ребер. Что-то вроде:

class Graph
{
    //...
    public:
        void add_vertex( unsigned id )
        {
            if (id > adjList.size() )
                adjList.resize( id );
        }

        void add_edge( unsigned from_id, unsigned to_id, int weight )
        {
            adjList[fromId].push_back( Edge( to_id, weight ) );
        }
 }

Непроверенный код, но вы получите эту идею, надеюсь.

Последняя мысль: Почему m_vertext строка в Edge? Я просто заметил это, и это кажется очень странным.

Если ваши вершины не являются числовыми, вам следует использовать map< string, list<Edge> > вместо vector для вашего adjList. Или добавьте map<string, unsigned> чтобы сопоставить строки с идентификаторами. В противном случае вы не сможете эффективно преследовать края до вершин.

  • 0
    Просто добавив это, у меня уже есть функции в Graph.cpp, которые отлично заполняют adjList и могут даже распечатать весь вектор списков без ошибок сегмента.
  • 1
    @ user3040019: Учитывая вершину, вы можете пройти через ребра, а затем посетить вершины, названные этими ребрами? Как вы конвертируете из m_vertex который является строкой, в то, что вы можете индексировать adjList ? Вы делаете кучу строк в целочисленные преобразования?
Показать ещё 6 комментариев
0

Я согласен с @joe-z. Однако, я думаю, проблема может заключаться в том, что ему нужен вектор определенного размера.

struct Edge {  // you could template this to make more generic.
    int v2;
    double weight;
};
class Graph {
public:
    Graph(size_t size) : m_adj_list(size) {}
    void AddEdge(int v1, int v2, double weight) {
        m_adj_list[v1].emplace_back(v2, weight);
    }
    void Resize(size_t size) {
        m_adj_list.resize(size, std::vector<Edge>());
    }
private:
    // You can use a list but in general a vector might be a better choice
    std::vector<std::vector<Edge>> m_adj_list;
};

К тому же. Не просто используйте голые указатели. Используйте std :: unique_ptr или std :: shared_ptr. В общем случае вы можете использовать правило (в этом порядке):

  1. не используйте указатели (std :: vector уже заботится о вашем управлении памятью)
  2. используйте unique_ptr
  3. использовать shared_ptr
  4. используйте голый указатель, когда вы не принимаете/не представляете собственность, и вы не можете передать shared_ptr вокруг
0

Если ваш класс графа может читать из файла, то ваш конструктор для класса может выглядеть примерно так:

Graph::Graph(istream &iss)
{
    // number of vertices in the graph
    // where each vertex has a list of edges
    size_t vertices;
    iss >> vertices;
    adjList.resize(vertices);
    ...
}

Помимо установки размера вектора, нет необходимости динамически выделять пространство для вектора, если вы не объявите вектор как указатель в своем классе.

Теперь, чтобы получить доступ к вектору, вы можете создать метод getter в своем классе, который возвращает копию вектора (если вы не хотите, чтобы оригинал был мутирован), или getter может вернуть ссылку/указатель на фактический объект

Я вернул класс графа некоторое время назад, а другой вариант, который вы, возможно, захотите изучить, - это конструктор, который может создать случайный объект графика, заданный для density, maxedgeLength и numberofvertices.

0

Вам не нужно использовать new, adjList уже является vector переменной в стеке, если вы создаете экземпляр Graph (это не Java), и я думаю, что векторный конструктор по умолчанию достаточно, не нужно его явно называть. Вам нужно вызывать new только если ваша переменная указатель.

Но вам может потребоваться изменить возвращаемый тип get_adjList чтобы просто вернуть ссылку adjList, а не новую копию adjList.

class Graph
{
private:
    vector<list<Edge> > adjList; //vector of lists
public:
    Graph();
    ~Graph();
    vector<list<Edge> > &get_adjList(){return adjList;}
};

Или, может быть, если вам нужен vector элемент, лучше не возвращать vector, просто верните элемент на основе индекса запроса, например:

list<Edge> &get_adjListElmt(int idx){return adjList[idx];}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню