Вопрос

Я получаю ошибки привязки следующего типа:

Фестиваль.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, чтобы они были скомпилированы там.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top