Абстрактный объект не может объявить - PullRequest
3 голосов
/ 22 ноября 2010

У меня проблема с абстрактными / виртуальными классами, повторение проблемы здесь:

#include <iostream>

class A
{
protected:
    virtual std::string getDateTime() = 0;
    virtual void Write(std::string data, bool addDate) = 0;
    virtual bool CheckFile() = 0;
    virtual bool OpenFile(std::string path) = 0;
    virtual void CloseFile() = 0;
};

class B
    : public A
{
public:
    virtual std::string ToString() { return ""; };
    virtual void Write(std::string data) { };
};

class C
    : public A
{
protected:
    std::string getDateTime()
    {
        return "TODAY";
    };

    void Write(std::string data, bool addDate)
    {
        std::cout << "BasicClassA Write" << std::endl;
    };

    bool CheckFile()
    {
        std::cout << "BasicClassA CheckFile" << std::endl;
        return true;
    };

    bool OpenFile(std::string path)
    {
        std::cout << "BasicClassA OpenFile" << std::endl;
        return true;
    };

    void CloseFile()
    {
        std::cout << "BasicClassA CloseFile" << std::endl;
    };
};

class D
    : public B,
      public C
{
public:
    BasicClassB();
    virtual ~BasicClassB();

    std::string ToString()
    {
        return "BasicClassB tostring";
    };

    void Write(std::string data)
    {
        std::cout << "BasicClassB Write" << std::endl;
    };
};

int main(int ac, char *av[])
{
    BasicClassB b;
    std::cout << b.ToString() << std::endl;
    b.Write("");
    return 0;
}

Это ошибка компиляции:

.. / src / main.cpp: В функции «int main (int, char **)»:
../src/main.cpp:82: ошибка: невозможно объявить переменную "b" абстрактного типа "BasicClassB"
../src/main.cpp:64: note: потому что следующие виртуальные функции являются чистыми в BasicClassB:
../src/main.cpp:13: note: virtual std :: string BaseClassA :: getDateTime ()
../src/main.cpp:14: примечание: виртуальная пустота BaseClassA :: Write (std :: string, bool)
../src/main.cpp:15: примечание: виртуальный bool BaseClassA :: CheckFile ()
../src/main.cpp:16: примечание: виртуальный bool BaseClassA :: OpenFile (std :: string)
../src/main.cpp:17: примечание: виртуальная пустота BaseClassA :: CloseFile ()

Возможно, я здесь упускаю суть, но реализация BaseClassA (будучи BasicClassA) должна содержать эти функции, и, поскольку BasicClassB также является подклассом от BasicClassA, он также должен содержать эти функции?

Что мне не хватает? Что я должен сделать, чтобы сделать эту компиляцию?

[ редактировать ] I updated the class names as suggested by the comment
Для пояснения: я использовал чистый виртуальный в классе A, чтобы принудительно заставить дочерних элементов реализовывать функции.

Кажется, виртуальное наследование - это то, что мне нужно, однако я, похоже, не понимаю, как это сделать в моем случае ...

Цель состоит в том, чтобы иметь несколько «базовых» классов, вроде интерфейсов, вынуждающих дочерние элементы реализовывать функции, но любые дочерние классы должны наследовать переопределенную функцию (так же, как виртуальное наследование)

Однако, используя любую комбинацию класс Any: общедоступный виртуальный Anyother {} не получается и всегда выдает одну и ту же ошибку компиляции (ту, что выше). Возможно, мне нужно изменить больше, чем просто виртуальный в наследство?

Ответы [ 4 ]

4 голосов
/ 22 ноября 2010

По умолчанию это не работает в C ++ - вам нужен шаблон наследования ромбов, но в C ++ вы получаете отдельные корни: поэтому у BasicClassA и BaseClassB есть свои собственные BaseClassA (переменные vtable и instance).

Вы, вероятно, хотите использовать Виртуальное наследование .

Для более ясного представления о не виртуальном наследовании:

#include <iostream>


class A
{
    public:
        A(int x) {m_a = x;}
        virtual ~A() {}
        int m_a;
        virtual int getA() {return m_a;}
};

class B : public A
{
    public:
        B() : A(1) {}
};

class C : public A
{
    public:
        C() : A(2) {}
};

class D : public B,
          public C
{
};

void useB(B* b)
{
    std::cout << "useB:" << b->getA() << std::endl;
}

void useC(C* c)
{
    std::cout << "useC:" << c->getA() << std::endl;
}

int main()
{
    D* d = new D();
    useB(d);
    useC(d);

    return 0;
}

Это приводит к выводу:

useB:1
useC:2

В этом примере показано виртуальное наследование и тип требуемого поведения при добавлении.

#include <iostream>


class A
{
    public:
        A(int x) {m_a = x;}
        virtual ~A() {}
        int m_a;
        virtual int getA() {return m_a;}
        virtual int virt() = 0;
};

class B : virtual public A
{
    public:
        B() : A(1) {}
};

class C : virtual public A
{
    public:
        C() : A(2) {}
        virtual int virt() {return 42;}
};

class D : public B,
          public C
{
    public:
        D() : A(3) {}
};



void useB(B* b)
{
    std::cout << "useB:" << b->getA() << std::endl;
}

void useC(C* c)
{
    std::cout << "useC:" << c->getA() << std::endl;
    std::cout << "useC-virt:" << c->virt() << std::endl;
}

int main()
{
    D* d = new D();
    useB(d);
    useC(d);

    return 0;
}

Выход:

useB:3
useC:3
useC-virt:42

Примечание: конструкторы из C и B не получают права голоса при установке m_a, который контролируется списком инициализации конструктора D ().

EDIT: Применение виртуального к вашему коду:

#include <iostream>

class A
{
protected:
    virtual std::string getDateTime() = 0;
    virtual void Write(std::string data, bool addDate) = 0;
    virtual bool CheckFile() = 0;
    virtual bool OpenFile(std::string path) = 0;
    virtual void CloseFile() = 0;
};

class B
    : virtual public A
{
public:
    virtual std::string ToString() { return ""; };
    virtual void Write(std::string data) { };
};

class C
    : virtual public A
{
protected:
    std::string getDateTime()
    {
        return "TODAY";
    };

    void Write(std::string data, bool addDate)
    {
        std::cout << "C Write" << std::endl;
    };

    bool CheckFile()
    {
        std::cout << "C CheckFile" << std::endl;
        return true;
    };

    bool OpenFile(std::string path)
    {
        std::cout << "C OpenFile" << std::endl;
        return true;
    };

    void CloseFile()
    {
        std::cout << "C CloseFile" << std::endl;
    };
};

class D
    : public B,
      public C
{
public:
    std::string ToString()
    {
        return "D tostring";
    };

    void Write(std::string data)
    {
        std::cout << "D Write" << std::endl;
    };
};

int main(int ac, char *av[])
{
    D b;
    std::cout << b.ToString() << std::endl;
    b.Write("");
    return 0;
}
1 голос
/ 22 ноября 2010

BaseClassA имеет 5 чисто виртуальных функций. Класс с хотя бы одной чисто виртуальной функцией - это «Абстрактный класс». Цель чисто виртуальных функций (вкратце) - запретить создание объектов абстрактного класса.

Чтобы создать экземпляр BaseClassB, он должен иметь определения всех 5 функций, которые вы объявили чисто виртуальными в BaseClassA. (При отсутствии этих определений BaseClassB также становится абстрактным, и, следовательно, вы не можете создавать объекты из него).

1 голос
/ 22 ноября 2010

BasicClassB происходит только от BaseClassA, который является абстрактным классом , поскольку эти методы:

virtual std::string getDateTime() = 0;
virtual void Write(std::string data, bool addDate) = 0;
virtual bool CheckFile() = 0;
virtual bool OpenFile(std::string path) = 0;
virtual void CloseFile() = 0;

Являются чисто виртуальными .

Сообщение об ошибке довольно ясно: чтобы иметь возможность создать BasicClassB, вы должны предоставить реализацию для вышеупомянутых методов.

Также обратите внимание, что ваше определение Write в BasicClassB:

virtual void Write(std::string data) { };

Отличается от BaseClassA:

virtual void Write(std::string data, bool addDate) = 0;

Таким образом, этот метод все еще необходимо реализовать, чтобы BasicClassB стал инстанцируемым.

0 голосов
/ 22 ноября 2010

Тот факт, что вы добавляете «= 0» к своим функциям, означает, что они являются чисто виртуальными и должны быть реализованы в дочерних классах.Что, очевидно, не то, что вы хотите.Если вы удалите «= 0» из функций, которые имеют реализацию в базовом классе, он должен работать так, как задумано.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...