С++, вызывающий виртуальный метод в конструкторе

Я использую родительский класс, который имеет несколько виртуальных методов. Когда я вызываю виртуальный метод в конструкторе, он выдает ошибки LNK2019 и LNK1120 с сообщениями "error LNK2019: unresolved external symbol "protected: virtual int ... referenced in function "public: __thiscall ..." и "...\Debug\8puzzleProject.exe : fatal error LNK1120: 1 unresolved externals".

Есть ли способ решить эту проблему или я не должен вызывать виртуальный метод в конструкторе?

Благодарю вас!

Вот коды:

Класс с ошибками:

#ifndef HEURISTICSEARCH_H
#define HEURISTICSEARCH_H

#include "BruteSearch.h"

class HeuristicSearch: public BruteSearch
{
public:

    HeuristicSearch( int initial[BOARD_LIMIT][BOARD_LIMIT] );
    bool search();

protected:

    virtual int calculateUtility() = 0;
    virtual int calculateUtility( Node* ) = 0;

    bool check4Goal();
    void checkNmove();
    int findMin(int* values );

    int utilityCost;

};

#endif

HeuristicSearch::HeuristicSearch( int initial[BOARD_LIMIT][BOARD_LIMIT] )
    :BruteSearch( initial )
{
    utilityCost = calculateUtility(); //After deleting this line, the error's gone
}

Родительский класс родительского класса (ошибки нет)

#ifndef BRUTESEARCH_H
#define BRUTESEARCH_H

#include <iostream>
#include <queue>

#include "Constants.h"
#include "Node.h"

class BruteSearch
{
public:

    BruteSearch( int initial[BOARD_LIMIT][BOARD_LIMIT] );
    virtual bool search(){ return false; }

protected:

    bool check4Goal();
    void printBoard();

    int turn;
    int goalBoard[BOARD_LIMIT][BOARD_LIMIT] ;

    Node *currentPtr;

};

#endif

person ciyo    schedule 22.11.2013    source источник
comment
не могли бы вы опубликовать свой код? Вы используете только свой родительский класс? Это не будет работать без реализации виртуальных методов.   -  person nio    schedule 22.11.2013
comment
возможный дубликат вызова виртуальных функций внутри конструкторов   -  person Kiril Kirov    schedule 22.11.2013


Ответы (2)


разве я не должен вызывать виртуальный метод в конструкторе?

Это вызовет переопределение в создаваемом классе, не обязательно окончательное переопределение. Это, вероятно, не то, что вам нужно, и ошибка, если функция чисто виртуальная в текущем классе, поэтому, как правило, вы не должны этого делать.

В этом случае ошибка предполагает, что он чисто виртуальный, а не реализованный в этом классе, поэтому его вызов определенно является ошибкой. К счастью, это дало вам довольно удобную ошибку компоновщика, а не неопределенное поведение во время выполнения. (ОБНОВЛЕНИЕ: опубликованный вами код подтверждает это предположение — вы не можете вызывать функцию из конструктора).

Есть ли способ решить эту проблему

Многие проблемы можно решить с помощью дополнительного уровня косвенности. Я бы реализовал эвристику в отдельном классе делегата, а не в подклассе:

struct Heuristic {
    virtual ~Heuristic() {}  // Don't forget this on a polymorphic base class
    virtual int calculateUtility() = 0;
    virtual int calculateUtility( Node* ) = 0;
};

class HeuristicSearch: public BruteSearch {
public:
    HeuristicSearch(Heuristic & h, int initial[BOARD_LIMIT][BOARD_LIMIT]) : 
        BruteSearch(initial), 
        heuristic(h),
        utilityCost(heuristic.calculateUtility()) // No problem calling this
    {}

private:
    Heuristic & heuristic;
    int utilityCost;
    // and so on
};

Делегат полностью сконструирован до того, как мы начнем создавать класс Search, поэтому нет проблем с доступом к нему из конструктора.

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

template <class Heuristic>
class HeuristicSearch: public BruteSearch {
public:
    HeuristicSearch(int initial[BOARD_LIMIT][BOARD_LIMIT]) : 
        BruteSearch(initial), 
        utilityCost(heuristic.calculateUtility()) // Doesn't have to be virtual
    {}

private:
    Heuristic heuristic;
    int utilityCost;
    // and so on
};
person Mike Seymour    schedule 22.11.2013
comment
Да, это чисто виртуальная функция, и я реализую ее в дочерних классах. Итак, я думаю, я получил ответ. - person ciyo; 22.11.2013

Когда вы создаете объект производного класса, сначала создается объект базового класса. Этот объект не знает, что он станет объектом производного класса. Следовательно, любые функции, вызываемые из конструктора базового класса, разрешаются только для базового класса, независимо от того, является ли функция виртуальной или нет.

В вашем случае HeuristicSearch не обеспечивает реализацию calculateUtility, но HeuristicSearch::calculateUtility() вызывается из конструктора HeuristicSearch независимо от того, является ли HeuristicSearch::calculateUtility() виртуальным или нет.

person Oswald    schedule 22.11.2013