2013-11-19 3 views
5

У меня есть три класса: ConsoleInputStream, ConsoleOutputStream, ConsoleErrorStream. Все они получены от Stream.C++ extern: указатель против ссылки

Каждый поток имеет виртуальные функции read и write; как вы предполагаете, когда пользователь пытается использовать функцию-член ConsoleInputStreamwrite, он выдает сообщение об ошибке. То же самое происходит, когда пользователь пытается использовать функцию ConsoleOutputStreamwrite.

Теперь пришло время показать код.

// STREAM.HPP 
namespace streamlib { 
extern ConsoleInputStream stdin_default; 
extern ConsoleOutputStream stdout_default; 
extern ConsoleErrorStream stderr_default; 
extern Stream& stdin; 
extern Stream& stdout; 
extern Stream& stderr; 
} // namespace streamlib 


// STREAM.CPP 
namespace streamlib { 
ConsoleInputStream stdin_default; 
ConsoleOutputStream stdout_default; 
ConsoleErrorStream stderr_default; 
Stream& stdin = stdin_default; 
Stream& stdout = stdout_default; 
Stream& stderr = stderr_default; 
} // namespace streamlib 


// MAIN.CPP 
int main() 
{ 
    streamlib::stdout = streamlib::stdin; 
    streamlib::stdout.write(); // Still working, but should have thrown error! 
} 

Однако, все работает отлично, когда я определяю stdin, stdout и stderr как указатели вместо ссылки, то есть ошибка возникает, как ожидалось. Но я не хочу выделять/освобождать память и использовать оператор ->, где я мог бы (мог бы?) Использовать оператор простой точки.

Реальная ситуация, конечно, еще более intricated: У меня также есть некоторые другие виды, полученные из Stream и просто хочу, чтобы иметь возможность быстро перегрузить stdin, stdout, stderr потоков с различными видами потоков. Можно ли это сделать со ссылками?

Заранее благодарен!

+3

(Я игнорирую очевидные опечатки в стороне заголовка/.cpp, где вы явно смешали stdout/stdout_default) Не уверен, что именно вы ищете. Я думаю, проблема в том, что вы пытаетесь присвоить один поток другому, и это само по себе, вероятно, плохое (в более крупной схеме вы теперь «просочились» ссылку на основной дескриптор файла или что-то в этом роде). Если это проблема, то отказ от всех присвоений одного потока другому, вероятно, имеет смысл. Вы можете использовать 'private: operator = (Stream &)', чтобы запретить его. –

+0

@MatsPetersson: Я исправил опечатки. Могли бы вы посоветовать лучший способ перенаправить такие потоки? Я думал об использовании функций-членов 'int fd() const' и' void fd (int) 'для изменения дескриптора файла внутри экземпляра класса, но это явно не охватывает некоторые ситуации, скажем, если я попытаюсь перенаправить на виртуальный поток (что-то вроде std :: ostringstream), который не имеет файлового дескриптора. – ghostmansd

+0

Я не уверен, что понимаю, о чем вы спрашиваете, какова была цель перенаправления выходного файла в качестве входного файла? –

ответ

3

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

streamlib::stdout = streamlib::stdin 

это соответствует заданию разыменовывается указателей, а не простые указателей. Другими словами, если stdout и stdin были указатели, эквивалентный код был бы

(*streamlib::stdout) = (*streamlib::stdin) 

Это вызывает оператор присваивания Stream, передавая stdin быть назначен stdout.

Один из способов - определить назначаемый «поток указателей», который инкапсулирует указатель на «реальный» поток и переопределяет его при назначении указателем на любой поток, назначенный ему. Это позволит вам сохранить синтаксис объекта, позволяя вам использовать оператор dot . вместо оператора разыменования указателя ->.

+0

Вы хотите представить простую оболочку, такую ​​как предоставление 'ConsoleInputStreamWrapper' (возможно, мне нужно найти другое имя, а?), У которого есть' Stream * stream' член? – ghostmansd

+0

@ghostmansd Да, вы бы предоставили оболочку с указателем внутри. Он может наследовать от 'Stream' или иметь оператор неявного преобразования, определенный для преобразования этого объекта-оболочки в объект' Stream'. – dasblinkenlight

2

Как вы определили назначение в своих потоках? С эталонной декларацией:

streamlib::stdout = streamlib::stdin; 

присваивает то, что называется на второй ссылку на то, что называют первым.

Если унаследовано вовлечение, обычно это плохая идея: назначение поддержки; в этом случае, например, нет способа , что ваш ConsoleOutputStream может стать a ConsoleInputStream, что означает, что нормальное сообщение условия задания, вероятно, не могут быть выполнены. Обычное решение предназначено для базового класса для определения частного назначения оператора, а не для его реализации. Вы также можете получить от boost::noncopyable (но это также подавит копию —, которую вы можете или не захотите). И если вы используете C++ 11, лучшим решением будет отметить назначение оператора как удаленное.

Наконец, что касается указателей: вам не нужно динамическое распределение для использования указателей; вы можете инициализировать указатель с адресом адреса другого объекта. И причина, по которой это работает с указателями, заключается в том, что назначение указателей приводит к переустановке указателя . Если a и b являются ссылками, то эквивалент a = b с использованием указателей будет *a = *b.

+0

Спасибо за большой объем информации, которую я получил сегодня. Мне жаль, что я не смог бы принять два ответа вместе! Я принял ответ @dasblinkenlight, поскольку он обеспечивает действительно простое исправление этой проблемы (или лучше для моего непонимания понятий языка). Однако, спасибо! – ghostmansd

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