[C++] Unaufgelöste Symbole bei Vererbung(pure virtual)

FBIagent

Erfahrenes Mitglied
Guten Abend,

zur Zeit arbeite ich an einer Kommandozeilen-Verarbeitung die es mir ermöglichen soll, definierte Variablen zu
setzen und Kommandos auszuführen. Allerdings kommt zu unaufgelösten Symbolen bei der zuletzt hinzugefügten
virtuellen Methode einer Klasse.

Im folgenden code bekomme ich unaufgelöste Symbole der methode "SetDefaultValue". Diese ist als "virtual" in
der Klasse "Variable" angegeben:
Code:
Error    1    error LNK2001: unresolved external symbol "private: virtual void __thiscall CommandLine::Variable<bool>::SetDefaultValue(bool const &)" (?SetDefaultValue@?$Variable@_N@CommandLine@@EAEXAB_N@Z)    CommandLine.obj


Command.h
C++:
#pragma once

#include <cwchar>
#include <exception>

#include "../Exceptions.h"

namespace CommandLine
{

class Command
{
protected:
    Command(const wchar_t *Identifier)
    {
        try
        {
            mIdentifier = new wchar_t[wcslen(Identifier) + 1];
#pragma warning(push)
#pragma warning(disable:4996)
            wcscpy(mIdentifier, Identifier);
#pragma warning(pop)
        }
        catch (std::bad_alloc &ex)
        {
            ex;
            THROW_EXCEPTION(AllocationFailedException, L"");
        }
    }
public:
    virtual ~Command()
    {
        delete[] mIdentifier;
    }

    virtual void Execute(const int Argc, wchar_t **Argv) = 0;

    const wchar_t *GetIdentifier() const
    {
        return mIdentifier;
    }
protected:
    wchar_t *mIdentifier;
};

}

Variable.h
C++:
#pragma once

#include "Command.h"

#include "../CommandLineExceptions.h"

namespace CommandLine
{

template<class T>
class Variable : public Command
{
protected:
    Variable(const wchar_t *Identifier, const T &DefaultValue, bool Writeable)
    : Command(Identifier), mWriteable(Writeable)
    {
        SetDefaultValue(DefaultValue);
        mCurrentValue = mDefaultValue;
    }
public:
    virtual ~Variable()
    {}

    void Execute(const int Argc, wchar_t **Argv)
    {
        if (!mWriteable) THROW_EXCEPTION(CommandLineVaribaleNotWriteableException, mIdentifier);
        if (Argc != 2) THROW_EXCEPTION(CommandLineException, L"Invalid number of arguments!");
        SetCurrentValue(Argv[1]);
    }
private:
    virtual void SetDefaultValue(const T &DefaultValue) = 0;
    virtual void SetCurrentValue(const wchar_t *StrValue) = 0;
public:
    const T &GetDefaultValue() const
    {
        return mDefaultValue;
    }

    const T &GetCurrentValue() const
    {
        return mCurrentValue;
    }
private:
    bool mWriteable;
protected:
    T mDefaultValue;
    T mCurrentValue;
};

}

BoolVariable.h
C++:
#pragma once

#include "Variable.h"

namespace CommandLine
{

class BoolVariable : public Variable<bool>
{
public:
    BoolVariable(const wchar_t *Identifier, const bool &DefaultValue, bool Writeable = true)
    : Variable<bool>(Identifier, DefaultValue, Writeable)
    {
    }
private:
    void SetDefaultValue(const bool &DefaultValue)
    {
        mDefaultValue = DefaultValue;
    }

    void SetCurrentValue(const wchar_t *StrValue)
    {
        // TODO: should be case insensitive
        if (wcscmp(StrValue, L"true") == 0) mCurrentValue = true;
        else if (wcscmp(StrValue, L"false") == 0) mCurrentValue = false;
        else THROW_EXCEPTION(CommandLineException, L"Invalid value!");
    }
public:
    static const bool _TRUE = true;
    static const bool _FALSE = false;
};

}

Leider konnte ich bis jetzt keine anhaltspunke für den Fehler finden. Könnt ihr mir da weiterhelfen?
 
Zuletzt bearbeitet von einem Moderator:
Hallo,

schau dir mal die Punkte 23.5 und 23.6 der C++ FAQ Lite an.

\edit: Deinen speziellen Fall könnte man auch so lösen:

C++:
template<class T>
class Variable : public Command
{
protected:
    Variable(const wchar_t *Identifier, const T &DefaultValue, bool Writeable)
    : Command(Identifier), mWriteable(Writeable)
    {
        SetDefaultValue(DefaultValue);
        mCurrentValue = mDefaultValue;
    }
public:
    virtual ~Variable()
    {}
 
    void Execute(const int Argc, wchar_t **Argv)
    {
        if (!mWriteable) THROW_EXCEPTION(CommandLineVaribaleNotWriteableException, mIdentifier);
        if (Argc != 2) THROW_EXCEPTION(CommandLineException, L"Invalid number of arguments!");
        SetCurrentValue(Argv[1]);
    }
private:
    void SetDefaultValue(const T &DefaultValue); // Nicht mehr pure virtual
    virtual void SetCurrentValue(const wchar_t *StrValue) = 0;
public:
    const T &GetDefaultValue() const
    {
        return mDefaultValue;
    }
 
    const T &GetCurrentValue() const
    {
        return mCurrentValue;
    }
private:
    bool mWriteable;
protected:
    T mDefaultValue;
    T mCurrentValue;
};

// Templatespezialisierung für bool
void Variable<bool>::SetDefaultValue(const bool &DefaultValue) {
    mDefaultValue = DefaultValue;
}

class BoolVariable : public Variable<bool>
{
public:
    BoolVariable(const wchar_t *Identifier, const bool &DefaultValue, bool Writeable = true)
    : Variable<bool>(Identifier, DefaultValue, Writeable)
    {
    }
private:
    // Hier keine Reimplementierung von SetDefaultValue

    void SetCurrentValue(const wchar_t *StrValue)
    {
        // TODO: should be case insensitive
        if (wcscmp(StrValue, L"true") == 0) mCurrentValue = true;
        else if (wcscmp(StrValue, L"false") == 0) mCurrentValue = false;
        else THROW_EXCEPTION(CommandLineException, L"Invalid value!");
    }
public:
    static const bool _TRUE = true;
    static const bool _FALSE = false;
};

Grüße,
Matthias
 
Zuletzt bearbeitet:
Soweit ich das also richtig verstanden habe komme ich nicht drum herum es so zu realisieren wie in deinem
Beispiel. Mir stellt sich allerdings die Frage ob man das auch "sauber" nennen kann. Zumal die "SetDefaultValue"
Methode nur wichtig für die Klasse "StringVariable" ist, um Speicher zu reservieren und die Zeichenkette zu
kopieren. Da könnte ich warscheinlich besser im Konstruktor der Klasse "StringVariable" Speicher reservieren
und kopieren.

C++:
#pragma once

#include "Variable.h"

namespace CommandLine
{

class StringVariable : public Variable<wchar_t*>
{
public:
    StringVariable(const wchar_t *Identifier, wchar_t * const &DefaultValue, bool Writeable = true)
    : Variable<wchar_t*>(Identifier, DefaultValue, Writeable)
    {
        try
        {
            mDefaultValue = new wchar_t[wcslen(DefaultValue) + 1];
#pragma warning(push)
#pragma warning(disable:4996)
            wcscpy(mDefaultValue, DefaultValue);
            mCurrentValue = mDefaultValue;
#pragma warning(pop)
        }
        catch (std::bad_alloc &ex)
        {
            ex;
            THROW_EXCEPTION(AllocationFailedException, L"");
        }
    }

    ~StringVariable()
    {
        if (mCurrentValue != mDefaultValue) delete[] mCurrentValue;
    }
private:
    /*void SetDefaultValue(wchar_t * const &DefaultValue)
    {
        try
        {
            mDefaultValue = new wchar_t[wcslen(DefaultValue) + 1];
#pragma warning(push)
#pragma warning(disable:4996)
            wcscpy(mDefaultValue, DefaultValue);
#pragma warning(pop)
        }
        catch (std::bad_alloc &ex)
        {
            ex;
            THROW_EXCEPTION(AllocationFailedException, L"");
        }
    }*/

    void SetCurrentValue(const wchar_t *StrValue)
    {
        try
        {
            wchar_t *NewValue = new wchar_t[wcslen(StrValue) + 1];
#pragma warning(push)
#pragma warning(disable:4996)
            wcscpy(NewValue, StrValue);
#pragma warning(pop)

            if (mCurrentValue != mDefaultValue) delete[] mCurrentValue;
            mCurrentValue = NewValue;
        }
        catch (std::bad_alloc &ex)
        {
            ex;
            THROW_EXCEPTION(AllocationFailedException, L"");
        }
    }
};

}

Wobei dies wiederum Fehleranfälliger ist, da ich mich nicht grundsätzlich zwinge eine sichere
Initialisierungsmethode zu schreiben.

Oder gleich std::wstring nutzen.
 
Zuletzt bearbeitet von einem Moderator:
Zurück