陈硕的Blog

对 C++ 历史的个人观点

先把 PPT 放出来,文章以后有空再写吧。

cpp_history

幻灯片2

幻灯片3

幻灯片4

幻灯片5

幻灯片6

幻灯片7

幻灯片8

幻灯片9

幻灯片10

幻灯片11

幻灯片12

幻灯片13

幻灯片14

幻灯片15

幻灯片16

cpp_history17

幻灯片18

幻灯片19

cpp_history20

幻灯片22

幻灯片23

cpp_history24

幻灯片25

cpp_history26

幻灯片27

幻灯片28

幻灯片29

解释:integers 固定长度有什么好处?或者说为什么 <stdint.h>  typedefs 没有解决问题?

1. 格式化输入输出 scanf/printf,int64_t 应该用什么格式?"%d" 还是 "%ld" 还是 "%lld" ? int_fast32_t 呢?

C99 为了解决这个,引入了 inttypes.h 头文件,其中定义了一堆宏,类似 PRId32, PRId64, PRIdFAST32,代码写起来是这样:

int64_t value = getValue();

printf("value = " PRId64 "\n", value);

2. 在 C++ 里,可以用函数重载 (overload) 来解决。但是 typedef 并不真正引入新类型(golang 与此不同),你如何知道 int_fast32_t 与 int64_t 是不是同一类型呢?另外还有 size_t/time_t 呢。比如

void foo(uint64_t x)

{}

void foo(size_t x)

{}

在有的系统(64 位 Linux)下会报编译错,因为 size_t 和 uint64_t 都是 unsigned long 类型,不能重载 foo 两次。怎么办?用宏和条件编译吗?

 

另外的例子是 time_t 和 int64_t:

void bar(time_t y)

{}

void bar(int64_t y)

{}

这段代码有错没错?取决于 time_t 与 int64_t 是不是同样的 typedef,如果整数不定长,除了用丑陋的 #if / #endif 条件编译,有办法解决吗?

幻灯片30

解释1:finally 有什么用?确实可以用栈上对象析构函数里的动作来模拟 finally,这又是一个 idiom,为什么不正大光明地让语言支持这一常用功能呢?

 

解释2:数据成员的默认值有什么用?

如果 class Foo 有一个 enum State state_; 成员,希望初始化为 INVALID_STATE。而 Foo 有 4 个构造函数,那么你得在每个构造函数里写:

Foo::Foo()

: state_(INVALID_STATE)

{}

Foo::Foo(XXXX1)

: state_(INVALID_STATE)

{}

Foo::Foo(XXXX1, YYYY2)

: state_(INVALID_STATE)

{}

Foo::Foo(XXXX1, YYYY2, ZZZZ3)

: state_(INVALID_STATE)

{}

state_ 的初始化要写四处。对于 enum,或许还可以用一个公用的 init() 来初始化。那么对于 class-type 如 string/vector,用 init() 这种办法就不能享受 initialization list 的好处了,因为对象在构造之后再被赋值,重复劳动。

更糟糕的是,万一你将来加了一个 int turnedOn_ 成员,初始值为 -1,你得在 4 个构造函数那里去增加初始化代码,万一漏了一处,等待你的就是 uninitialized value,自求多福吧。

cpp_history31

关于 allocator,它没有带来任何好处,如果内存分配这种事情都需要重新定义,重写数据结构也是理所应当的:

http://blog.csdn.net/Solstice/archive/2009/08/02/4401382.aspx

 

auto_ptr 为什么是坏的,因为太容易用错,且不能放到标准容器里。

Gregory Colvin 最早设计的 auto_ptr 是没有“所有权转移”这个语意的,跟现在的 scoped_ptr 一样。但是标准委员会莫名其妙地加了这个语意,造成了很多陷阱。

scoped_ptr/unique_ptr/shared_ptr 都是更好的替代,语意明确,不容易用错。

 

至于为什么 valarray 是坏的,见《C++ 标准程序库》相关章节,再说,有谁会用 valarray 做科学计算吗?同样坏的还有 vector<bool>。

 

如果 XML/logging 这些基本构件不标准化,很难让几个第三方库协作起来,因为每个库都会自己发明一套互不兼容的 logging 和 XML 处理机制。

如果程序里要把 library A 生成的 XML 对象传到 library B 里,恐怕只好用字符串来作为中间媒介,这会增加很多无谓序列化/反序列化的开销。

logging 也是如此,如果没有标准化接口,如何让 library A 和 library B 按相同的格式写到同一个日志文件呢?恐怕又得自己写写 adapter 来协调这些第三方库了。

幻灯片32

posted on 2010-04-06 21:34 陈硕 阅读(8392) 评论(25)  编辑 收藏 引用

评论

# re: 对 C++ 历史的个人观点 2010-04-06 22:38 OwnWaterloo

个人觉得很多改进都是不切实际的。

—— Exact-width integer types

没有必要。
当需要确定宽度的整数类型时, 应该使用stdint.h那样的typedef。(据说C++0x也会引入相应的cstdint)
而不是一开始就将数据类型固定死。

而且,即使是C99, Exact-width integer types也是optional的。
也就是说,确实是不能保证在每个平台上都有确定宽度整数。
更有用的应该是Minimum-width integer types
或者Fastest minimum-width integer types

—— finally
引入finally还不如引入lambda和auto。
有了lambda和auto, 配合RAII, finally就不值钱了。
而lambda和auto也是C++0x准备加入的。

—— default value
这能有什么用?
C和C++给程序员最大限度的控制。

struct X x;
X_fill(&x); // 明明知道x马上就会被fill, 初始化也是多余的。

如果确实需要:
struct X x_default_initialized = X();


—— allocator
绝对是需要的。 你用不上不等于别人用不上。

如果去掉allocator, 当那个默认的allocator不满足你的需要时,报销的(不能被复用的)不仅仅是那个默认的allocator, 而是连同整个data structure库都废掉。

而加上allocator并不会引起什么害处。
别说“vector<int, my_allocator> 和vector<int> 不是同一类型”什么的。
当你确实需要这种行为时, 他们本来就不应该是一个类型。

—— auto_ptr和valarray
不知道这两位又怎么了。
是不是boost::scope_ptr比 const std::auto_ptr新潮?
而在C++加入restrict 之前, valarray都是有用的, 只是你可能用不上而已。



—— 其他的库
只有threading是必须加入语言的。
因为语言不提供帮助的话, threadding库是搞不出来的。

而其他的network, xml, log,就越界了。

C++不是那些有大公司撑腰的语言, 可以对标准库肆意的扩充。
  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-06 22:50 Sunshine Alike

mark,期待博主文章也放出来~  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-07 00:26 陈硕

@OwnWaterloo
已在正文中答复。  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-07 01:58 OwnWaterloo

———— integers

>> integers 固定长度有什么好处?或者说为什么 <stdint.h> typedefs 没有解决问题?

scanf/printf这个你已经说了, inttypes.h
而cin/cout更不用说了, 因为有重载。

>> 在 C++ 里,可以用函数重载 (overload) 来解决。但是 typedef 并不真正引入新类型(golang 与此不同),你如何知道 int_fast32_t 与 int64_t 是不是同一类型呢?另外还有 size_t/time_t 呢
你真的需要吗? 真的需要有is_same_type这种东西。

你通常你需要做的就是像cin/cout那样, “对类型本身, 而不是各种typedef”去重载。
比如:
void f(char x); void f(int x); ...
然后无论uint64_t和size_t是什么, 都可以f之。

或者使用enable_if这种机制。

java和C#那套东西, 只是在当前的pc机已经服务器上行得通。
比如它们要规定整数长度, 规定浮点规格, 甚至连char都规定了。
但这是很短视的作法。

这两门语言所说的unicode, 其实只是utf16。
我不知道usc4流行起来的时候, 这两门语言打算怎么办?
又搞出个wchar?
或许那时候它们早就灭亡了。

而对其他各种古怪的平台, 我也不知道java se和.net compact活得怎样。
我只想知道, 在16位的平台上, 32位的int是否总是要由2个机器字拼出来。
甚至, 在8位平台上, 是否需要4个机器字来拼。

而这两门语言又希望做到强类型, short的使用不是那么愉快:
short s = ...;
s = s + 1; // error
s += 1; // ok

C语言肯定是打算尽可能贴近机器的, 而不是贴近程序员的。
所以C标准不会规定确定长度的整数, 即使C99中, 那些typedefs也是optional的。

而C++, 这个就有分歧了。
我是希望C++依然能贴近机器。
毕竟, 如果真的需要开发起来很爽, 已经有java或者C#了, 直接用就是了。

如果让C++规定整数长度, 而提供:
typedef xxx nature_width_t;
并大量使用之, 我觉得很难接受。



———— finally

>> finally 有什么用?确实可以用栈上对象析构函数里的动作来模拟 finally,这又是一个 idiom,为什么不正大光明地让语言支持这一常用功能呢?

这一功能常用吗? 在我看来, 它只是没有确定性析构时机的语言中的一种“补救措施”。
不能因为其他语言有这个, 就一定要搬到C++中。

C++不需要这种烂玩意, 请允许我说它是烂玩意。
为什么? 因为finally会造成“执行代码与回滚代码”分离两处 —— 相信你明白我的意思。
所以C#会早早引入using, 而不思进取的java也终于打算在java7加入类似的机制。

所以, 这种东西只会让代码写得更烂, 而不是更好。
这是最主要的原因。


其次, “让资源归类所有”, 这在C++中已经被广泛接受了, 是这样吗?
所以, 在很多时候, C++都可以将action和rollback写在一处。
而实现一个范型的RRID类, 其实Loki已经有ScopeGuard而boost也有scope_exit。
当语言支持auto和lambda之后, 它们都不再需要使用语言的阴暗角落或者晦涩的语法。
可以实现的十分优雅, 用户代码也可以写的十分优雅。

而且auto的实现并不难, 编译器必然是知道某个表达式的类型。
lambda应该也不难, 很多时候那些手写的functor都是有规律的, 很适合让机器自动生成的。
而finally的实现方式, 我就不清楚了。

———— 默认值

>>解释2:数据成员的默认值有什么用?

我依然没看出有什么用。
你举的例子, 其实是C++0x打算支持的“转发构造函数”可以优美解决的。

而且, 据我所知, C++0x也是打算支持这样的特性的, 我忘记叫什么名字了:
class X {
T v = default_value_for_v;
};

注意, 我的观点不是排斥这种特性。
我反对的是将这种行为“作为默认行为”。
我希望默认情况下, 语言不要做多余的事情, 除非我显示要求。

—— allocator
我看了你给的链接, 没发现有具有说服力的例子。

1. 当你不需要定制allocator。

无论那种设计, 都不会对你造成影响。

2. 当你需要定制allocator。

无论那种设计, 你可能都需要将两种容器区别对待。
也就是说, 你这种要求:
>>但这完全没道理,我不过想访问一个vector<string>,根本不关心它的内存是怎么分配的,却被什么鬼东西allocator挡在了门外。

不过想访问一个vector<string>的要求, 本来就是不合理的。
你只考虑到了“使用allocator会有什么坏处”, 却没想过“不使用allocator你同样会遇到这些问题”。
所以, 这并不是allocator的错。

而使用allocator的设计, 你至少不需要去重新实现一套数据结构。

当然, STL对allocator的设计并不是很好。
比如它要求不同的allocator必须被认为是同一个。
所以会导致这样的问题:
>> 那么每种类型的allocator必须是全局唯一的(Singleton)

C++应该取消这个限制, 而不是取消allocator。


在你所说parser的case中, 你的目标是这样对吗:
一个allocator的类型有不同的实例, 这些实例是不在线程之间共享的。

那么, 这些container本身也是不在线程间共享, 依然可以用allocator解决。
只是它会触犯STL对allocator的限制。


—— 各种库, auto_ptr, valarray, xml, log

>>auto_ptr 为什么是坏的,因为太容易用错,且不能放到标准容器里。

C++什么东西是“不容易用错的”?
(而且, 当你允许工程使用boost之后, 你会遇到更多容易用错的东西)


这和finally还不同。
学会finally之后, 写出的代码依然是臭的。
而auto_ptr, 至少在学会之后, 它还是有用的。


valarray的slice我还确实没用过。


对, auto_ptr, valarray, vector<bool> 都是有各种各样的毛病的。
但是, 既然他们已经被加入标准库了, 对付这些毛病的办法就是去“学”。
移除是没法移除的。 早就被deprecated的strstream到现在都可以使用。
vc10, gcc4.4.0), vc10甚至一个警告都不给。

而且, 它们(包括strstream)对会使用的人来说并不是一无是处。
就我来说, 我并不需要scoped_ptr, 或者说不会因为这样一个小东西去引入boost,或者将编译器限制到支持C++0x的那些上。

vector<bool> 和valarray都是试验品。
如果你需要一个可增长的位图, 你还是得去实现vector<bool>那样的东西。
使用上还是需要注意将他们和STL其他容器有所区分。
那么, 移除它能获得什么好处?

valarray就让它呆那里就可以了, 会给你带来负担吗?


—— xml和log

你说的这个问题, 是不能靠语言提供标准库来做的。
很简单, Qt用std::string了吗? ACE用了吗? MFC用了吗?


你可以说std::string设计得差, 设计得太学术。
你也可以说上面那些库不屑于使用模板。

但是, std::xml, std::log难道不会重蹈覆辙?
上面讨论的那几个家伙(auto_ptr, valarray, vector<bool>)已经犯错了。

同时, 正因为我们看到这些不够完美的东西, 对待标准库时我们更需要“谨慎”。
我觉得标准委员会做得很好, 甚至将concepts砍了都做得很好。
就是两个字“谨慎”。
没有它们, 依然还在开发; 但如果因为冲动加入它们, 可能就是以后无法移除的负担。
就像上面那几个。

目前这种模式: 将boost作为试验田, 等有充分使用基础之后再纳入tr1, 我也觉得很好。


C++需要的是稳定, 只把必须加入语言的东西加入,(移除就更没必要了) 其他的慢慢观望。
我宁愿使用一个从vc8-vc10, 从gcc3-gcc4都可以使用的C++。
而不是告诉用户, 你必须使用什么编译器的什么版本。

C++不像C#那样, 可以霸气的说“我就是不向后兼容,你拿我怎么招?” “你不还是得用我?”
  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-07 08:40 ccsdu2009

搞不明白的是很多国人做ppt总喜欢搞英文的 纳闷  回复  更多评论   

# re: 对 C++ 历史的个人观点[未登录] 2010-04-07 09:38 hh

@ccsdu2009
眼界低不怪你, 等你明白了就好了.  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-07 10:35 溪流

搬凳子学习  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-07 10:55 溪流

C++ 1x 确定了?  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-07 12:30 wuqq

@OwnWaterloo
关于vector<T> 与vector<T, Allocator>,我是这么看的:首先明确的一个概念是接口还是实现,任何暴露其内部实现的接口都不是好的设计。那么Allocator是实现还是接口呢,我倾向于把它看成是实现,vector就是一段连续的内存,至于怎么分配内存,那是你内部实现的事。你可以看到在c++0x的tr1库中,function, shared_ptr都是可以内存定制的,但在类型声明中都没有allocator模板参数,这就是设计思想的改变。  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-07 12:54 陈梓瀚(vczh)

@wuqq
有些时候你并不能拿C++的接口跟其他语言的interface等同。举个例子,如果你的vector没有allocator这个模板参数,而是在构造函数里面需要一个IAllocator<T>*,你认为使用的时候方便吗?

在给C++写类的时候,你做的不是在完成一个功能,而是在扩充语言让C++变得更强大。  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-07 13:48 wuqq

@陈梓瀚(vczh)
1)难道你忘记了可以取默认值。比如构造函数:
vector(allocator_ptr a = default_allocator());
或者干脆使用重载写成两个函数。
2)老大,我只是写一个类而已,我就是要完成一个功能,我干嘛要扩充语言。
3) 我不知道C++的接口特殊在什么地方,事实上,我觉得大部分C++程序员的接口都设计得很滥,“C++倾向于过分复杂的设计”——《UNIX编程艺术》的作者N年前就说出了这样的话。  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-07 16:37 OwnWaterloo

@wuqq
C++不玩那套, 别拿其他语言的什么接口/实现那套玩意来“套”C++的设计。
allocator是一种规范。
整个STL都是在这种duck typing的基础上构建起来的, 而不是java或者C#那种接口式的设计。


如果你确实需要按接口的方式来使用, 自己实现一个满足allocator规范的接口, 假设叫IA。
然后使用vector<T,IA> 即可。
无论你使用什么IA的实现, 你都可以使用vector<T,IA>类型。
甚至可以使用0x中的新的特性:
template<typename T>
using your_vector = std::vector<T,your_allocator<T>>;
your_allocator是一个模板, 转发到IA上。

然后就一直使用your_vector就行了。


模板和接口就是在效率与代码体积上的权衡。
但是, 用模板实现的代码, 如果在乎代码体积, 可以轻易转换为接口去使用。
并且获得接口的所有好处, 例如不暴露实现。
反之, 如果一开始就用接口,当需要模板的行为时, 就没得搞, 永远没办法, 除了重写。


vector太麻烦, 换个例子。
如果将排序算法使用模板实现:
template<class RanIt, class Cmp>
void sort(RanIt first, RanIt last, Cmp c);

那么, 这个模板可以轻易转换为C语言中使用void*和int (*cmp)( ... )的qsort, 也可以轻易转换为使用接口作为Cmp和RanIt的sort。
同时, 不暴露这个模板的任何实现。
你可以将这个模板隐藏到实现文件中。

反之, 如果sort一开始就是qsort或者使用接口:
当你确实需要效率,确实针对不同类型生成另一套代码的时候, 你必须重写。

这就是模板的强大之处, 它的使用是很灵活的, 看你的需求而定。
你不仅仅可以“直接使用”, 你还可以将它作为“代码生成器”, 生成满足你需要的东西。
  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-07 17:15 陈梓瀚(vczh)

@OwnWaterloo
当然这是其中一种观点,不过从语言设计的角度出发,我认为C++没有interface的根本原因是不需要,因为它有多重继承。而且因为模板的类型推导非常强大(优化也是其中一个方面),因此interface的概念也就被弱化了。

不过有些时候使用接口的话,可以避免很多因为想写出优美的句子的同时造成模板膨胀过于厉害。第一个例子自然是boost::spirit了,已经严重到了让编译器浪费人类时间的地步了,这个网上有的是抱怨,我也就不多说了。第二个例子就是linq了。当然我并不说这跟stl比起来有什么优势,假设实现linq就是一个前提,因为linq的特点就是algorithm无敌多,并且algorithm返回自己的iterator,而且鼓励N个algorithm嵌套使用,跟不同的iterator结合起来代码膨胀速度会变得非常快(无论是list&gt;&gt;Where(a)&gt;&gt;Select(b)&gt;&gt;Aggregate(i,C)也好,还是Aggregate(i,Select(Where(list, a), b))也好)。做的时候会发现Where和Select返回的iterator基本上都必须是一个全新类型,结果就是假如你不使用纯虚类当接口,那么每一行都会构造一个全新类型,当你的程序充满linq的时候就不得了了(想象一下spirit)。【当然值不值得实现linq那不讨论】

第二种情况就是,有些人可能会觉得编译器尽早告诉你错误比运行时的那点开销更重要,那么他会用纯虚类当接口去模仿concept。

第三种情况就是,有些时候你为了写成dll,不得不不使用模板……而且很有讽刺意味的是,大部分人写dll完全是因为有人(不一定是写dll的人自己)觉得他帅。  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-07 17:33 空明流转

@陈梓瀚(vczh)
你别老拿Spirit说事儿啊。我等着你的东西呢。。。。  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-07 17:58 陈梓瀚(vczh)

@空明流转
TM我前天labtop出问题了啊……  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-07 19:17 volnet

标记一下,郁闷啊,昨天在你CSDN看的,一点注释都没有,这里原来版本有改进……还有很多回复,晚上回去洗把脸再看  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-07 20:50 唐风

@OwnWaterloo
很精彩,呵呵。
关于allocator这段,我看得很仔细,因为,在TopLanguage里,有一个主题是《{技术}{C++} 突然有一种想法:将文本文件读入vector可以跨过push_­back()吗》
http://groups.google.com/group/pongba/browse_thread/thread/ebb85b7e927c1af9/aebc1741ed60b2f5?hl=zh-CN&lnk=gst&q=%E7%AA%81%E7%84%B6%E6%9C%89%E4%B8%80%E7%A7%8D%E6%83%B3%E6%B3%95%EF%BC%9A%E5%B0%86%E6%96%87%E6%9C%AC%E6%96%87%E4%BB%B6%E8%AF%BB%E5%85%A5vector%E5%8F%AF%E4%BB%A5%E8%B7%A8%E8%BF%87push_back()%E5%90%97#
我在里面表达了与OwnWaterloo同样的观点,但我的认证和说明肤浅多了。我刚刚还特别翻了下,发现当时的“辩方对友”居然正是楼主,世界太小了(楼主也是牛人,哪都见得到滴说)。

在这个问题上,我与OwnWaterloo相同,并从他的回答中又学到不少东西。我喜欢这样的“技术论战”,因为它总是能让人进步,呵呵。向楼主,和OwnWaterloo学习。

  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-07 21:25 OwnWaterloo

@唐风
TL上跑题太严重, 看吧, 没几楼就扯到profiling, 扯到“大部分应用都合适了”。
如果万事都扯这么多无关的东西, 就没什么好讨论了。

allocator的问题不在于是否应该有allocator参数,是否应该将allocator作为模板参数。
上面已经说了:

1. 如果一个container没有allocator参数
那它内部的分配方式必然不能被定制, 它绑定了一个“策略”。
当这种策略无法满足用户要求时, 整个container的实现连同这个策略一起被抛弃。

2. 模板vs非模板参数
上面也说了, 模板是非模板的一个范化形式。
模板可以得到非模板的东西, 只要实例化一次即可。
反之不行。
当template alias加入语言后, 使用模板的设计会更优雅。


allocator真正的问题是标准对allocator的状态的描述很模糊。

如果同一allocator type的所有instance都可以被认为是相同的 —— 也就是说,A和B是同一个个类型的2个instance, 从A分配的内存可以由B释放 —— 那没问题, 可以安全的和STL配合使用。

但通常, allocator都需要per instance的状态才能发挥真正作用。
而这种带有per instance状态的allocator和STL交互, 标准说得很模糊。

如果标准规定STL的实现必须注意这个问题, 那allocator就非常好用。
绝对不存在lz所说的“基本用不上”。
即使就是现在的情况, 也可以用一些方式绕过去, 只是很不美观。

综上, allocator的设计应该被改进(取消这个限制), 而且依然作为一个模板参数。


btw:如果C++0x真引入了template alias,boost的智能指针就是一坨垃圾, 无论它设计多少个, 在Loki面前都是垃圾。
  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-09 01:04 欲三更

—— xml和log

你说的这个问题, 是不能靠语言提供标准库来做的。
很简单, Qt用std::string了吗? ACE用了吗? MFC用了吗?

=============

这一句说的太好了,C++很大一部分的混乱的起因不是“没有标准实现”——当然很多时候确实没有标准实现,而是大家都要去实现。而且,那boost来说,boost曾经拒掉一个log方面的库,但是就算是boost里面有这个库,有多少人会去用?很多时候不是主观上不喜欢用,而是说boost,包括像stl这种东西,它们的编程哲学太强势,并且经常迥异于我们惯常的代码。  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-09 11:03 陈硕

@欲三更
这正好证明了我的观点,如果标准库里没有,每个第三方 library 都会自己造一套。
std::string 加入标准大约是在 1994 年,然后又被 STL 拖延,到 1998 年标准才发布。
而 QT, ACE, MFC 的开发均早于 std::string。
QT 是 1991 年开始开发,MFC 最早在 1992 年发布,1993 年 ACE 的版本号已经升到了 2.12。

假如 C++ 在 1985 年提供 string,还会造成这样天下大乱的局面吗?  回复  更多评论   

# re: 对 C++ 历史的个人观点 2010-04-09 16:58 OwnWaterloo

@陈硕
MFC、QT、ACE太老是吧? 来个新点的?
http://www.libnui.net/

去看看它是什么时候开始开发的, 又重复发明了多少轮子吧。
  回复  更多评论   

# re: 对 C++ 历史的个人观点[未登录] 2010-04-11 10:57 chentan

http://www.libnui.net/

这个东东不稳定啊,他那个生成器,我随便拖两下鼠标就崩溃了  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

导航

统计

常用链接

随笔分类

随笔档案

相册

搜索

最新评论

阅读排行榜

评论排行榜