Вопрос о перегрузке операторов абстрактного класса в C ++ и реализации интерфейса - PullRequest
23 голосов
/ 13 января 2010

(отредактировано из исходного сообщения, чтобы изменить "BaseMessage" на "const BaseMessage &")

Привет всем, Я очень плохо знаком с C ++, поэтому я надеюсь, что вы, ребята, сможете помочь мне «увидеть ошибки моих путей».

У меня есть иерархия сообщений, и я пытаюсь использовать абстрактный базовый класс для обеспечения соблюдения интерфейс. В частности, я хочу заставить каждое производное сообщение предоставлять перегруженный << оператор. </p>

Когда я пытаюсь сделать это с чем-то вроде этого:

class BaseMessage
{
public:

// some non-pure virtual function declarations
// some pure virtual function declarations

virtual ostream& operator<<(ostream& stream, const BaseMessage& objectArg) = 0;

}

компилятор жалуется, что

"ошибка: невозможно объявить параметр« objectArg »абстрактного типа« BaseMessage »

Я полагаю, что здесь также есть проблемы с "друзьями", но когда я попытался объявить это как:

virtual friend ostream& operator<<(ostream& stream, const BaseMessage objectArg) = 0;

Компилятор добавил ошибку добавления

«ошибка: виртуальные функции не могут быть друзьями»

Есть ли способ гарантировать, что все мои производные (message) классы предоставляют оператор "<<" ostream? </p>

Большое спасибо,

Steve

Ответы [ 5 ]

41 голосов
/ 13 января 2010

Общепринятым условием для этого является использование friend оператора вывода на базовом уровне и вызов его закрытой виртуальной функции:

class Base
{
public:

    /// don't forget this
    virtual ~Base();

    /// std stream interface
    friend std::ostream& operator<<( std::ostream& out, const Base& b )
    {
        b.Print( out );
        return out;
    }

private:

    /// derivation interface
    virtual void Print( std::ostream& ) const =0;
};
3 голосов
/ 13 января 2010

Операторы потока, такие как:

virtual ostream& operator<<(ostream& stream, const BaseMessage objectArg) = 0;

просто не может быть функциями-членами, и поэтому не может быть виртуальными функциями. Причина этого в том, что когда вы говорите что-то вроде:

a << b;

Вы действительно говорите

a.operator<<( b );

В этом случае a является потоком, а не экземпляром вашего класса, поэтому оператор не может быть членом вашего класса. Обычно вы должны сделать ее бесплатной (не-членской) функцией, которая обращается к вашему экземпляру класса через подходящие функции-члены.

3 голосов
/ 13 января 2010

Абстрактный класс не может быть создан, поэтому сделайте это:

virtual ostream& operator<<(ostream& stream, const Base &objectArg) = 0; 

Виртуальная функция должна быть функцией-членом экземпляра, тогда как функция Friend не является функцией-членом, поэтому она не может быть объявлена ​​как виртуальная.

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

Мое предложение:

class Base {
 public:
 virtual ostream&  print (ostream& stream) const = 0; 
};


class Derived :public Base {
 public:
 virtual ostream&  print (ostream& stream) const { //do something } 
};

ostream& operator <<(ostream& stream, const BaseMessage &objectArg) 
{
  return objectArg.print(stream); 
}
1 голос
/ 13 января 2010

Объявление для оператора << () неверно.Для двоичной версии op << вам не нужно объявлять второй параметр - предполагается, что он равен <code>this, если op << является функцией-членом класса: </p>

virtual ostream& operator<<(ostream& stream) = 0;

Более того, как уже упоминалось, операторы вставки потока должны быть глобальными функциями.Создание функций-членов просто не сработает.

Также не то, что не связано с вашим вопросом, но проблема, тем не менее.В исходной реализации вы передали абстрактный объект базового класса по значению, а не по ссылке или по указателю.Когда вы делаете это, вы «режете объект».Я уверен, что вы намеревались передать функции указатель базового класса на полиморфный тип, а затем полиморфно вызывать методы вызова функции.Например, вы пытались сделать что-то похожее на это:

#include <cstdio>
#include <string>
#include <iostream>
using namespace std;

class Base 
{
public:
    virtual void dump() 
    {
        cout << "Base";
    };
};

class Der : public Base
{
public:
    void dump()
    {
        cout << "Der";
    };
};

void DumpIt(Base b)
{
    b.dump();
}


int main() 
{
    Der obj;
    DumpIt(obj);
    return 0;

}

... и ожидали, что на выходе будет «Der», но на самом деле на выходе получается «Base» из-за Object Slicing.Поскольку функция DumpIt () принимает базовый объект по значению, новый временный базовый объект создается на основе оригинала.Чтобы получить ожидаемую функциональность, вам нужно передать по ссылке или по указателю:

void DumpIt(Base & b)
{
    b.dump();
}

Выход этой функции - "Der".

0 голосов
/ 13 января 2010

Проблема здесь в том, что «BaseMessage objectArg» говорит, что объект objectArg должен быть передан по значению.

Это невозможно, поскольку вы сделали класс абстрактным с помощью своего чисто виртуального вызова. Передача по ссылке «BaseMessage & objectArg» или передача по указателю «BaseMessage * objectArg» могут устранить эту ошибку.

Передача по значению означает, что он создает новый экземпляр переменной, используя конструктор копирования. Поскольку вы убедились, что невозможно создать экземпляр BaseMessage, это невозможно сделать.

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