У меня есть два класса Edge
и Line
, которые представляют соответственно ребро между узлами на графике и линию, связанную с этим краем, которая в контексте моей программы является объектом, который действует как визуальное представление ребра и используется для рисования ' edge' с помощью библиотеки графического интерфейса. Я пытаюсь найти способ связать эти два объекта в своем коде с помощью указателей от Edge
к Line
и от Line
к Edge
, которые будут храниться в этих объектах. Я хочу сделать это, чтобы запустить некоторый алгоритм на Edge
объектах. Каждый объект Line
, соответствующий Edge
, имеет некоторые свойства, такие как цвет, толщина и т. д., Которые рисуются библиотекой графического интерфейса.
Пример использования:
Edge e = Edge(); //create an edge
Line l = Line(e); //create a line and pass it the edge
Где созданный объект edge
передается по ссылке в конструктор Line
, который затем устанавливает указатель на это ребро, а также устанавливает указатель на ребро на эту строку, так что ребро и линия имеют некоторое «знание» друг о друге:
Line::Line(Edge& e)
{
corresponding_edge_ = make_shared<Edge>(e);
e.setLine(make_shared<Line>(this));
};
Соответствующие части моего класса Edge
выглядят так:
edge.h
файл:
//===== Edge.h =====
#pragma once
#include <memory>
using namespace std;
class Line;
class Edge {
private:
weak_ptr<Line> associated_line_;
public:
Edge();
~Edge();
void setLine(shared_ptr<Line> l);
weak_ptr<Line> Line();
edge.cpp
файл:
//===== Edge.cpp =====
using namespace std;
Edge::Edge(){};
Edge::~Edge() {};
void Edge::setLine(shared_ptr<Line> l) {
associated_line_ = l;
}
weak_ptr<Line> Edge::Line() {
return associated_line_;
};
Соответствующие части моего класса Line
выглядят так:
line.h
файл:
//===== Line.h =====
#pragma once
#include <memory>
#include "edge.h"
using namespace std;
class Edge;
class Line {
private:
weak_ptr<Edge> corresponding_edge_;
public:
Line();
Line(Edge& e);
~Line();
};
line.cpp
файл:
//===== Line.cpp =====
#include "line.h"
#include <memory>
using namespace std;
Line::Line() {};
Line::Line(Edge& e)
{
corresponding_edge_ = make_shared<Edge>(e);
e.setLine(make_shared<Line>(this));
};
Line::~Line() {};
Я включил только edge.h
в заголовок line.h
и передал объявление Line
в edge.h
, чтобы избежать циклической ссылки. Однако, когда я компилирую этот код, я получаю следующую ошибку:
std::shared_ptr`:`Edge::Line` is not a valid template type argument for parameter `_Ty
Почему этот класс пытается найти тип Line
в классе Edge
, а не по определению в Line.h
?
С другой стороны, using namespace std
в заголовке — ужасная идея.
#include
— это директива препроцессора, которая копирует текст в заголовке и заменяет строку #include <your_header>
. Как только оператор using namespace std;
объявлен в файле, его нельзя исключить для любых исходных файлов, включающих этот файл.
weak_ptr<Line> Line();
в Edge
объявляет функцию-член Line
, которая скрывает одноименный тип. Эта функция-член не является допустимым аргументом шаблона.
Объявление new shared_ptrs таким образом не делает того, что вы думаете.
@MooingDuck Правильно ли я говорю, что вызов конструктора make_shared будет выделять новую память? Мне лучше использовать конструктор shared_ptr() и передать экземпляр, для которого я хочу сделать shared_ptr?
make_shared(e)
создаст новый общий объект, который является копией e
, и, следовательно, никакие Line
объекты никогда не имеют corresponding_edge_
, такого же, как любой другой Line
.
Я новичок здесь, и я надеюсь, что я не получаю много предупреждений...
Ответ на вопрос заключается в том, что прокомментировал @ChrisMM: вы должны добавить заголовки включения в файлы cpp:
#include "line.h"
#include "edge.h"
Почему этот класс пытается найти тип Line в классе Edge, а не по определению в Line.h?
Если полного объявления нет, компилятор знает только имя класса, а не полное определение. Это означает, что вы не можете получить доступ к любому элементу или размеру типа.
Все эти weak_ptr и shared_ptr — все экземпляры шаблона. Когда шаблон создается внутри функций/методов, это часто приводит к тому, что требуется полное определение.
Я нашел несколько других проблем в вашем коде: Когда вы объявляете:
weak_ptr<Line> Line();
Вызывает двусмысленность. Судя по всему, компилятор считает, что вы хотите создать преобразование Edge to Line. Обновлено: на самом деле имя функции-члена скрывает имя типа (спасибо @Davis Herring). Это заставляет компилятор использовать функцию-член Line в качестве аргумента шаблона (спасибо @molbdnilo). Просто переименуйте его в:
weak_ptr<Line> getLine();
Исправлено это и убрана двусмысленность.
Обновлено: вместо этого некоторые могут предпочесть использовать class перед Line всякий раз, когда создается экземпляр шаблона, например:
weak_ptr<class Line> Line();
Также,
e.setLine(make_shared<Line>(this));
Похоже, не работает, так как make_shared ожидает объект, а не указатель. Однако это должно работать:
e.setLine(make_shared<Line>(*this));
Изменить. Однако это плохая идея... поскольку указатель this может оказаться недостаточным для совместного использования в зависимости от того, как объект выделяется или освобождается: это может привести к двойному удалению. Вместо этого вы должны сделать так, чтобы Line наследовалась от std::enable_shared_from_this:
class Line : public std::enable_shared_from_this<Line>
И используйте для этого случая вместо make_shared следующее:
e.setLine(this->shared_from_this());
Кроме того, в edge.h отсутствует завершение класса, но я думаю, что проблема была только в вопросе. После этих изменений мне удалось его скомпилировать.
Как бы то ни было... Создание экземпляров шаблонов часто приводит к нелепому количеству ошибок и предупреждений, что часто приводит в замешательство даже лучших из нас! С наилучшими пожеланиями!
Нет (предполагаемого) «преобразования края в строку», но имя функции-члена затеняет имя типа, и IFNDR должен сделать это после того, как уже использовал такое внешнее имя. (Вы можете уточнить имя типа, чтобы избежать конфликта.)
@Gottardi, спасибо, что нашли время ответить, очень признателен! Теперь, когда я это вижу, это совершенно очевидно. Я обязательно буду использовать лучшее соглашение об именах getMember()
в будущем, чтобы избежать двусмысленности.
Нет проблем, я также хочу сообщить, что добавил несколько новых улучшений в мой ответ. Кроме того, это мой первый прием. И тебе спасибо!
Чтобы создать Edge, он должен иметь возможность видеть его полное определение. Внутри файлов cpp включите соответствующие заголовки.