首先,似乎我在征求主观意见,但这并不是我所追求的。我很想听听有关该主题的一些有充分根据的论点。
为了对如何设计现代流/序列化框架有所了解,我最近得到了Angelika Langer和Klaus Kreft撰写的《Standard C ++ IOStreams and Locales》一书的副本。我发现,如果IOStreams的设计不当,那么它就不会首先进入C ++标准库。
在阅读了本书的各个部分之后,我开始怀疑IOStreams是否可以从整体架构的角度与STL进行比较。阅读例如对Alexander Stepanov(STL的“发明人”)的采访,以了解有关STL的一些设计决策。
特别令我惊讶的是:
谁来负责IOStreams的总体设计似乎是个未知数(我很想阅读有关此的一些背景信息-有人知道好的资源吗?);
一旦你钻研输入输出流,例如眼前表面之下,如果你想输入输出流用自己的类扩展,你会得到一个接口具有相当神秘和扑朔迷离的成员函数的名称,例如getloc
/ imbue
,uflow
/ underflow
,snextc
/ sbumpc
/ sgetc
/ sgetn
,pbase
/ pptr
/ epptr
(和有可能甚至更糟的例子)。这使得了解整体设计以及单个零件如何协作变得更加困难。即使我上面提到的那本书没有帮助那多(恕我直言)
因此,我的问题是:
如果你要判断今天的软件工程标准(如果确实是对这些任何普遍同意),将C ++的输入输出流仍然被认为是经过精心设计?(我不想通过通常认为过时的方法来提高软件设计技能。)
小智 40
关于设计它们的人,原始的库(毫不奇怪)是由Bjarne Stroustrup创建的,然后由Dave Presotto重新实现的。然后,由Jerry Schwarz针对Cfront 2.0重新设计并重新实现,使用了来自Andrew Koenig的操纵器思想。库的标准版本基于此实现。
来源“ C ++的设计和演变”,第8.3.1节。
Mar*_*tos 29
一些居心不良的想法找到自己的方式进入标准:auto_ptr
,vector<bool>
,valarray
和export
,仅举几例。因此,我不会将IOStreams的存在必然地视为质量设计的标志。
IOStreams具有历史记录。它们实际上是对早期流库的重做,但是它们是在不存在当今许多C ++惯用语的时候编写的,因此设计人员没有后见之明。随着时间的推移,一个显而易见的问题是,由于大量使用了虚函数并以最精细的粒度转发到内部缓冲区对象,几乎不可能像C的stdio一样高效地实现IOStreams。以定义和实现语言环境的方式。我承认,我对此的记忆非常模糊。我记得几年前它在comp.lang.c ++。moderated上引起了激烈的争论。
dan*_*n04 28
如果您必须根据当今的软件工程标准(如果实际上在这些标准上有任何一般性协议)来判断,那么C ++的IOStreams是否仍会被认为设计合理?(我不想通过通常认为过时的方法来提高软件设计技能。)
我会说“ 不”,原因如下:
错误处理差
错误情况应报告为例外情况,而不是operator void*
。
“僵尸对象”反模式是导致此类错误的原因。
格式化和I / O之间的间隔差
这使得流对象变得不必要复杂,因为无论是否需要,流对象都必须包含额外的状态信息以进行格式化。
它还增加了编写如下错误的几率:
using namespace std; // I'm lazy.
cout << hex << setw(8) << setfill('0') << x << endl;
// Oops! Forgot to set the stream back to decimal mode.
Run Code Online (Sandbox Code Playgroud)
如果相反,您写了类似以下内容:
cout << pad(to_hex(x), 8, '0') << endl;
Run Code Online (Sandbox Code Playgroud)
没有格式相关的状态位,也没有问题。
请注意,像Java,C#和Python的“现代”的语言,所有的对象有一个toString
/ ToString
/ __str__
函数是由I / O函数调用。AFAIK,只有C ++通过将其stringstream
用作转换为字符串的标准方式来实现。
对i18n的支持不佳
基于Iostream的输出将字符串文字拆分为多个部分。
cout << "My name is " << name << " and I am " << occupation << " from " << hometown << endl;
Run Code Online (Sandbox Code Playgroud)
格式化字符串会将整个句子放入字符串文字中。
printf("My name is %s and I am %s from %s.\n", name, occupation, hometown);
Run Code Online (Sandbox Code Playgroud)
后一种方法更易于适应GNU gettext等国际化库,因为整个句子的使用为翻译人员提供了更多上下文。如果您的字符串格式化例程支持重新排序(例如POSIX $
printf参数),那么它也可以更好地处理语言之间的单词顺序差异。
小智 16
我将其作为单独的答案发布,因为这是纯粹的意见。
执行输入和输出(尤其是输入)是一个非常非常棘手的问题,因此毫不奇怪,iostreams库中充满了虚假的东西,事后看来,可以做得更好。但是在我看来,所有的I / O库,无论用哪种语言,都是这样的。我从来没有使用过一种编程语言,在这种语言中,I / O系统真是太漂亮了,这让我对它的设计师感到敬畏。iostreams库确实具有优势,特别是优于CI / O库(可扩展性,类型安全性等),但是我认为没有人将它作为出色的OO或通用设计的示例。
Cha*_*via 14
随着时间的推移,我对C ++ iostream的看法有了很大的改善,尤其是在我开始通过实现自己的流类来实际扩展它们之后。尽管成员函数的名称之类的荒谬可笑,我还是开始欣赏它的可扩展性和总体设计xsputn
。无论如何,我认为I / O流是对C stdio.h的巨大改进,C stdio.h没有类型安全性,并且充斥着主要的安全缺陷。
我认为IO流的主要问题是它们将两个相关但有些正交的概念融合在一起:文本格式和序列化。一方面,IO流被设计为生成对象的人类可读的格式化文本表示形式,另一方面,将对象序列化为可移植格式。有时,这两个目标是相同的,但是有时,这会导致一些令人烦恼的不一致。例如:
std::stringstream ss;
std::string output_string = "Hello world";
ss << output_string;
...
std::string input_string;
ss >> input_string;
std::cout << input_string;
Run Code Online (Sandbox Code Playgroud)
在这里,我们作为输入获得的不是我们最初输出到流中的。这是因为<<
运算符将输出整个字符串,而>>
运算符将仅从流中读取直到遇到空格字符为止,因为流中没有存储长度信息。因此,即使我们输出包含“ hello world”的字符串对象,我们也仅将输入包含“ hello”的字符串对象。因此,尽管流已将其用作格式化工具,但未能正确序列化该对象,然后反序列化该对象。
您可能会说IO流并非设计为序列化工具,但如果是这种情况,输入流的真正用途是什么?此外,实际上,由于没有其他标准的序列化工具,I / O流通常用于序列化对象。考虑到boost::date_time
或boost::numeric::ublas::matrix
,如果您使用<<
运算符输出矩阵对象,则在使用运算符输入矩阵对象时会得到相同的精确矩阵>>
。但是为了实现此目的,Boost设计人员必须将列数和行数信息作为文本数据存储在输出中,这损害了实际的人类可读显示。同样,文本格式设置工具和序列化的笨拙组合。
注意大多数其他语言如何将这两种功能分开。例如,在Java中,格式化是通过toString()
方法完成的,而序列化是通过Serializable
接口完成的。
我认为,最好的解决方案是引入基于字节的流以及基于标准字符的流。这些流将对二进制数据进行操作,而无需考虑人类可读的格式/显示。它们可以仅用作序列化/反序列化工具,以将C ++对象转换为可移植字节序列。
Adr*_*son 10
我总是发现C ++ IOStreams设计不当:它们的实现使正确定义流的新类型非常困难。它们还混合了io功能和格式化功能(想想操纵器)。
就个人而言,我发现的最佳流设计和实现在于Ada编程语言。它是去耦的模型,是创建新型流的一种乐趣,并且无论使用哪种流,输出功能始终有效。这要感谢一个最小公分母:您将字节输出到流,仅此而已。流函数负责将字节放入流中,而不是将整数格式化为十六进制(当然,定义了一组用于处理格式化的类型属性,与类成员等效)不是他们的工作。
我希望C ++对于流来说是如此简单...
Art*_*yom 10
我认为IOStreams设计在可扩展性和实用性方面非常出色。
本地化集成和格式集成。看看可以做什么:
std::cout << as::spellout << 100 << std::endl;
Run Code Online (Sandbox Code Playgroud)
可以打印:“一百”甚至:
std::cout << translate("Good morning") << std::endl;
Run Code Online (Sandbox Code Playgroud)
可以打印“ Bonjour”或“ ??????????” 根据注入的语言环境std::cout
!
仅仅因为iostreams非常灵活,就可以完成这些事情。
可以做得更好吗?
当然可以!实际上,有许多事情可以改进...
今天,正确地从中派生是很痛苦的,stream_buffer
添加额外的格式信息以流式传输是很重要的,但是可能的。
但是,回顾多年前,我仍然很满意图书馆的设计,即将带来许多好处。
因为您可能无法始终看到全局,但是如果您为扩展留点,即使您没有想到的点也可以为您提供更好的能力。