2013-02-14 1 views
1

Я реализую printing pattern for a class hierarchy, как описано в C++ FAQ. В FAQ, функция печати объявлена ​​в базовом классе, как это:Печать шаблона для иерархии классов в C++ Часто задаваемые вопросы

protected: 
    virtual void printOn(std::ostream& o) const = 0; // pure virtual 
    // -- or -- 
    virtual void printOn(std::ostream& o) const;  // plain virtual 

Я рассматриваю реализации простой виртуальной версии метода printOn но с изменением. Я хотел бы изменить тип возвращаемого void к std::ostream&, как показано здесь:

protected: 
    virtual std::ostream& printOn(std::ostream& o) const; 

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

std::ostream& DerivedClass::printOn(std::ostream& stream) const 
{ 
    return stream 
     << "<DerivedClass>" << '\n' 
     << BaseClass::printOn(stream) 
     << "<member_one>" << member_one_ << "</member_one>" << '\n' 
     << "<member_two>" << member_two_ << "</member_two>" << '\n' 
     << "</DerivedClass>" << std::endl; 
} 

В отличие от этого, вот как DerivedClass::printOn может выглядеть, если виртуальный BaseClass::printOn метод объявлен void, как показано в FAQ:

void DerivedClass::printOn(std::ostream& stream) const 
{ 
    stream << "<DerivedClass>" << '\n'; 
    BaseClass::printOn(stream); 

    stream 
     << "<member_one>" << member_one_ << "</member_one>" << '\n' 
     << "<member_two>" << member_two_ << "</member_two>" << '\n' 
     << "</DerivedClass>" << std::endl; 
} 

 

ВОПРОС: Does кто-нибудь видит какие-либо подводные камни с моей предлагаемой ревизией на возвращаемый тип printOn?

ответ

2

Если BaseClass::printOn(stream) возвращает std::ostream &, то вы не можете писать

stream << ... << BaseClass::printOn(stream) << ...; 

Вы должны были бы написать:

stream << ...; 
BaseClass::printOn(stream) << ...; 

Ясно, что это едва ли не лучше, чем в случае, когда он возвращается void , Вы можете вернуть тип с оператором не оп поток-выход:

struct noop_manipulator { 
    noop_manipulator(std::ostream &) {} 
    friend inline std::ostream &operator<<(std::ostream &os, const noop_manipulator &) { 
    return os; 
    } 
}; 

noop_manipulator DerivedClass::printOn(std::ostream& stream) const 
{ 
    return stream << ...; 
} 

В качестве альтернативы, вы можете просто играть с синтаксисом:

void DerivedClass::printOn(std::ostream& stream) const 
{ 
    return stream << "<DerivedClass>" << '\n', 
     BaseClass::printOn(stream), stream 
     << "<member_one>" << member_one_ << "</member_one>" << '\n' 
     << "<member_two>" << member_two_ << "</member_two>" << '\n' 
     << "</DerivedClass>" << std::endl; 
} 

Недостатком всех этих подходов является то, что они загромождать ваш использование виртуального вызова суперкласса, что достаточно необычно, что его нужно сделать максимально ясным. Описанный шаблон печати сам по себе довольно необычен; если у вас уже нет иерархии виртуального наследования, более обычным для производных классов является их собственный operator<< и вызов базового класса operator<< через static_cast в базовый класс.

+0

@DavidRR синтаксически вы вызываете 'ostream << ostream', который специально не определен, но обычно вызывает оператор' operator void * 'fail-check. Вы должны ожидать увидеть значение указателя в выходном потоке. – ecatmur

+0

Да, после того, как я на самом деле прототипировал то, что я предложил, мне действительно стало ясно, что я в конечном итоге вызываю «ostream << ostream». И я действительно сейчас разрабатываю виртуальную иерархию наследования с нуля, и я чувствовал, что шаблон печати, рекомендованный C++ часто задаваемым вопросом, был подходящим. Я также буду иметь в виду ваш рекомендуемый альтернативный подход для переопределения 'operator <<' в производном классе. – DavidRR