2010-01-13 2 views
23

(отредактированный исходного сообщение, чтобы изменить «BaseMessage» на «Const BaseMessage &»)C++ абстрактного оператора класса перегрузка и обеспечение интерфейса вопрос

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

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

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

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;

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

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

Есть ли способ, чтобы убедиться, что все мои производные (сообщение) классов обеспечивают «< <» оператор ostream ?

Спасибо много,

Стив

+0

Ответ Нилолай - лучшее решение для того, что вы пытаетесь выполнить. Однако конкретная ошибка, которую вы получали, заключается в том, что вы пытаетесь передать объект BaseMessage по значению (второй аргумент вашему виртуальному оператору <<). Это не может работать, потому что BaseMessage включает в себя чистую виртуальную функцию (тот же виртуальный оператор <<), поэтому невозможно создать экземпляр BaseMessage для передачи по значению. Обратите внимание, что версия оператора Nilolai << принимает второй аргумент по ссылке (это будет некоторый класс, полученный из Base). –

ответ

40

Общей конвенции для этого является наличие оператора в 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; 
}; 
+0

Это единственное работающее решение, которое я видел на странице здесь: все остальные говорят outa их задницу :) (попробуйте скомпилировать своих людей кода!) +1 – jkp

+0

Это отлично поработало, спасибо, Николай, я вижу много мест, чтобы использовать это шаблон. - Steve –

+0

Я не знал, что вы можете определить функцию друга непосредственно внутри объявления класса. Хорошо знать!В учебниках всегда указывалось бы объявление друга внутри и определение снаружи. Может ли определение непосредственно внутри класса работать с функцией 'swap' friend? –

3

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

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

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

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

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

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); 
} 
0

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

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

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

3

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

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

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

a << b; 

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

a.operator<<(b); 

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

1

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

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() принимает объект Base по значению, создается новый временный базовый объект на основе оригинала. Для того, чтобы получить функциональность вы ожидаете, что вам нужно пройти по ссылке или указателю:

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

Выход из этой функции «Der».

Смежные вопросы