Бросок-перехват приводит к ошибкам привязки
-
08-07-2019 - |
Вопрос
Я получаю ошибки привязки следующего типа:
Фестиваль.obj :ошибка LNK2019:неразрешенный внешний символ "public:void __это дерево вызовов::добавить (класс Price &)" (?add@?$Tree@VPrice@@@@QAEXAAVPrice@@@Z) ссылка в функции __catch $?Добавить группу@Фестиваль@@QAE?AW4StatusType@@HHH@Z $0
Раньше я думал, что это связано с механизмом try-catch, но с тех пор мне сказали обратное.Это обновленная версия вопроса.
Я использую Visual Studio 2008, но у меня похожие проблемы в g ++.
Соответствующий код:
В Festival.cpp
#include "Tree.h"
#include <exception>
using namespace std;
class Band{
public:
Band(int bandID, int price, int votes=0): bandID(bandID), price(price), votes(votes){};
...
private:
...
};
class Festival{
public:
Festival(int budget): budget(budget), minPrice(0), maxNeededBudget(0), priceOffset(0), bandCounter(0){};
~Festival();
StatusType AddBand(int bandID, int price, int votes=0);
...
private:
Tree<Band> bandTree;
...
};
StatusType Festival::AddBand(int bandID, int price, int votes){
if ((price<0)||(bandID<0)){
return INVALID_INPUT;
}
Band* newBand=NULL;
try{
newBand=new Band(bandID,price-priceOffset,votes);
}
catch(bad_alloc&){return ALLOCATION_ERROR;}
if (bandTree.find(*newBand)!=NULL){
delete newBand;
return FAILURE;
}
bandTree.add(*newBand);
....
}
В дереве.h:
template<class T>
class Tree{
public:
Tree(T* initialData=NULL, Tree<T>* initialFather=NULL);
void add(T& newData);
....
private:
....
};
Интересно, что у меня нет ошибок привязки, когда я пытаюсь использовать древовидные функции, когда тип T является примитивным типом, таким как int.
Решение
Есть ли Tree.cpp? Если есть, может быть, вы забыли связать это? Где реализация Tree :: add? Кроме того, я не вижу, где вы вызываете Tree :: add. Я думаю, это должно быть внутри оператора try, сразу после нового?
Просто напоминание:
Для большинства компиляторов (то есть тех, которые практикуют отдельную компиляцию) реализация функций-членов класса шаблона должна быть видимой во время компиляции исходного файла, который использует класс шаблона. Обычно люди следуют этому правилу, помещая реализацию функций-членов в заголовочный файл.
Может, Tree :: add не находится внутри заголовка? Тогда возможным решением в обсуждаемом случае будет поместить реализацию Tree :: add в заголовочный файл.
Различие между обычными классами и шаблонными классами существует, потому что шаблонные классы не являются " real " занятия - это, ну, шаблон. Если бы вы определили свой класс Tree как обычный класс, компилятор мог бы использовать ваш код сразу. В случае шаблона компилятор сначала & Quot; пишет & Quot; для вас реальный класс, заменив параметры шаблона указанными вами типами. Теперь компилятор компилирует файлы cpp один за другим. Он не знает о других файлах cpp и ничего не может использовать из других файлов cpp. Допустим, ваша реализация Tree: add выглядит следующим образом:
void Tree::add(T& newData)
{
newData.destroyEverything();
}
Это полностью законно, если у вашего T есть метод destroyEverything. Когда компилятор компилирует Class.cpp, он хочет быть уверен, что вы ничего не делаете с T, чего он не знает. Например, Tree<int>
не будет работать, потому что int не имеет destroyEverything. Компилятор попытается написать ваш код с использованием int вместо T и обнаружит, что код не компилируется. Но так как компилятор & Quot; видит & Quot; только текущий cpp и все, что он включает, он не сможет проверить функцию добавления, поскольку он находится в отдельном cpp.
Там не будет никаких проблем с
void Tree::add(int& newData)
{
newData.destroyEverything();
}
реализован в отдельном cpp, потому что компилятор знает, что int является единственным допустимым типом и может & рассчитывать на себя " что, когда он доберется до компиляции Tree.cpp, он обнаружит ошибку.
Другие советы
Вы уверены, что try
/ catch
имеет к этому какое-то отношение? Что произойдет, если вы просто закомментируете строки Tree::add(class Price &)
и <=>, оставите остальную часть кода без изменений и создадите ее?
Возможно, вам просто не хватает библиотеки, которая определяет <=> в строке ссылки.
Обновление: использование функций дерева с примитивным типом не приводит к ошибке компоновки. Я обновил свой вопрос в свете некоторых вещей, которые были сказаны. Р>
Как уже говорили другие, вам нужно показать реализацию Treee :: add () и рассказать нам, как вы ее связываете. Р>
В несвязанной точке, если вы используете такие конструкции, как:
Band* newBand=NULL;
try{
newBand=new Band(bandID,price-priceOffset,votes);
}
catch(bad_alloc&){return ALLOCATION_ERROR;}
во всем коде вы откровенно тратите время. Шансы на то, что вы достигнете точки исчерпания памяти в современной ОС, малы, а шансы на то, что вы сделаете что-нибудь полезное после того, как это произошло, примерно равны нулю. Вам будет намного лучше просто сказать:
Band * newBand = new Band ( bandID, price - priceOffset, votes );
не возможно:
Band newBand( bandID, price - priceOffset, votes );
и забывать об обработке исключений в этом случае.
Вы написали в комментарии:
Я рассматривал это, но функция является частью Tree.h, и я включаю ее.Определенная функция является:дерево пустоты шаблона::добавить(T& newData);Мы называем это следующим образом:priceTree.добавить(*newPriceNode);принимая во внимание, что priceTree - это дерево, оба из которых определены в рассматриваемом cpp-файле.
вместо того, чтобы:
priceTree.add(*newPriceNode);
попробуй:
priceTree.add(newPriceNode); //no "*" before "newPriceNode"
add() принимает ссылку на узел, а не указатель на узел (согласно вашему определению дерева).
Вы получаете ошибки компоновки, а не ошибки компилятора. Это говорит нам о том, что компилятор знал, что это за функция Tree::add()
, но не имел определения. В Tree.h я вижу объявление функции add (), но не определение. Это выглядит странно для меня; Кто-нибудь знает, откуда появился Tree.h?
Обычно шаблонный класс поставляется с определениями функций-членов во включаемом файле, так как функции должны быть где-то созданы, и самое простое - создать экземпляр компилятора при использовании и позволить компоновщику разобраться с ним. Если бы определения были в Tree.h, я бы ожидал, что все будет работать как запланировано.
Итак, я собираюсь выйти на конечность и предположить, что определения находятся в отдельном файле, не связаны между собой, и что в других местах есть положения для создания экземпляров для базовых типов, таких как Tree<int>
. Это, по-видимому, упрощает компиляцию, так как обычно эти вещи компилируются в нескольких местах, и это требует времени.
В этом случае вам нужно найти, где создается экземпляр Tree<Band>
, и добавить экземпляр для вашего класса.
Я мог бы быть далеко от основания здесь, но мое объяснение действительно соответствует фактам, которые вы дали.
Изменить после первых комментариев :
Шаблоны несколько сложнее, чем обычные функции, что обычно не является реальной проблемой. Если бы определения всех вызовов были в Tree.h, то Festival.cpp мог бы создать экземпляр Tree<Band>::add()
, и все было бы круто. Это обычная техника, и вы столкнулись с этой проблемой, потому что вы ее не используете.
Когда вы пишете функцию, она компилируется, и компоновщик найдет ее. Любая подпрограмма, вызывающая эту функцию, должна знать прототип функции, поэтому она будет знать, как ее вызвать. Когда вы пишете шаблон, вы не пишете ничего, что попадет непосредственно в программу, но любое использование шаблона считается записью всех функций.
Следовательно, где-то в вашей программе должно быть какое-то использование Tree<T>::add
, чтобы была скомпилированная функция Tree<T>
. Определение Band
должно быть доступно компилятору при создании экземпляра <=>, потому что в противном случае компилятор не имеет представления о том, что компилировать. В этом случае он генерирует вызов функции, уверен, что вы убедитесь, что функция скомпилирована в другом месте.
Следовательно, вам нужно создать экземпляр <=> внутри файла, который имеет доступ к обоим определениям <=> и <=>. Это, вероятно, означает файл, который является или включает Tree.cpp и включает Festival.h.
Компоновщик уже использует Tree.cpp, но Tree.cpp не определил в нем <=>, поэтому для компоновщика это бессмысленно. Шаблоны полезны только для компилятора, а компоновщик работает только с тем, что компилятор сгенерировал из шаблонов.
Самый быстрый способ решить эту проблему - взять определения из Tree.cpp и поместить их в Tree.h. К сожалению, это может увеличить время компиляции и ссылки. Другой метод заключается в создании экземпляров всех шаблонов, используемых в Tree.cpp, чтобы они были скомпилированы там.