在Coders at Work (p355) 一书中,Guy Steele 谈到了 C++:
我认为与 C 向后兼容的决定是一个致命的缺陷。这只是一系列无法克服的困难。C 基本上有一个损坏的类型系统。它足以帮助您避免一些困难,但它不是密封的,您不能指望它
他将类型系统描述为“腐败”是什么意思?
你能用 C 语言的一个简单例子来演示吗?
编辑:
这句话听起来很有争议,但我不想成为。我只是想明白他的意思。
请用C而不是C++给出例子。我也对“基本”部分感兴趣:)
在Coders at Work (p355) 一书中,Guy Steele 谈到了 C++:
我认为与 C 向后兼容的决定是一个致命的缺陷。这只是一系列无法克服的困难。C 基本上有一个损坏的类型系统。它足以帮助您避免一些困难,但它不是密封的,您不能指望它
他将类型系统描述为“腐败”是什么意思?
你能用 C 语言的一个简单例子来演示吗?
编辑:
这句话听起来很有争议,但我不想成为。我只是想明白他的意思。
请用C而不是C++给出例子。我也对“基本”部分感兴趣:)
C 中非类型安全的明显示例仅来自于您可以从 void * 转换为任何类型而无需显式转换的事实。
struct X
{
int x;
};
struct Y
{
double y;
};
struct X xx;
xx.x = 1;
void * vv = &xx;
struct Y * yy = vv; /* no need to cast explicitly */
printf( "%f", yy->y );
当然 printf 本身并不是完全类型安全的。
C++ 不是完全类型安全的。
struct Base
{
int b;
};
struct Derived : Base
{
int d;
Derived()
{
b = 1;
d = 3;
}
};
Derived derivs[50];
Base * bb = &derivs[0];
std::cout << bb[3].b << std::endl;
将 Derived* 转换为 Base* 没有问题,但是当您尝试将 Base* 用作数组时会遇到问题,因为它会使指针算法全部错误,而所有 b 值都是 1,您很可能会得到 3 (因为整数将变为 1-3-1-3 等)
char buffer[42];
FunctionThatDestroysTheStack(buffer); // By writing 43 chars or more
基本上你可以将任何数据类型转换为任何数据类型
struct SomeStruct {
void* data;
};
struct SomeStruct object;
*( (int*) &object ) = 10;
没有人抓住你。
C 类型系统确实存在一些问题。诸如隐式函数声明和隐式转换之类的东西void*
可以静默地破坏类型安全。
C++ 修复了几乎所有这些漏洞。C++ 类型系统不向后兼容 C,它只兼容编写良好的类型安全 C 代码。
此外,反对 C++ 的人通常会将您指向 Java 或 C# 作为“解决方案”。然而,Java 和 C# 在它们的类型系统中确实存在漏洞(数组协方差)。C++ 没有这个问题。
编辑:示例,在 C++ 中,尝试使用 Java 和 C# 类型系统(不正确地)允许的数组协方差。
#include <stdlib.h>
struct Base {};
struct Derived : Base {};
template<size_t N>
void func1( Base (&array)[N] );
void func2( Base** pArray );
void func3( Base*& refArray );
void test1( void )
{
Base b[40];
Derived d[40];
func1(b); // ok
func1(d); // error caught by C++ type system
}
void test2( void )
{
Base* b[40] = {};
Derived* d[40] = {};
func2(b); // ok
func2(d); // error caught by C++ type system
func3(b[0]); // ok
func3(d[0]); // error caught by C++ type system
}
结果:
Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C++ C++0x_extensions
"ComeauTest.c", line 19: error: no instance of function template "func1" matches
the argument list
The argument types that you used are: (Derived [40])
func1(d); // error caught by C++ type system
^
"ComeauTest.c", line 28: error: argument of type "Derived **" is incompatible with
parameter of type "Base **"
func2(d); // error caught by C++ type system
^
"ComeauTest.c", line 31: error: a reference of type "Base *&" (not const-qualified)
cannot be initialized with a value of type "Derived *"
func3(d[0]); // error caught by C++ type system
^
3 errors detected in the compilation of "ComeauTest.c".
这并不意味着 C++ 类型系统中根本没有漏洞,但它确实表明您不能像 Java 和 C# 允许的那样用指向 Base 的指针静默覆盖指向 Derived 的指针。
你必须问他什么意思才能得到一个明确的答案,或者为这句话提供更多的背景。
然而,很明显,如果这是 C++ 的一个致命缺陷,那么这种疾病是慢性的,而不是急性的——C++ 正在蓬勃发展,并且不断发展,正如持续的 Boost 和 C++0x 努力所证明的那样。
我什至不再认为 C 和 C++ 是耦合的——在各自的论坛上呆了几周,很快就解决了他们是两种不同的语言这一事实的困惑之一,每种语言都有自己的优势和弱点。
恕我直言,C 类型系统中“最破碎”的部分是
都映射到单一语言概念“指针”。这意味着,如果你得到一个类型的函数参数X*
,它可能是一个可选参数,它可能会改变函数指向的值,可能是在指向的X*
那个之后有多个实例X
(它是开放的多少 - 数字可以作为单独的参数传递,或者某种特殊的“终止符”值可能会标记数组的结尾,如以 nul 结尾的字符串)。或者,参数可能只是一个单一的结构,你不希望改变它,但通过引用传递它更便宜。
如果你得到 type 的东西X**
,它可能是一个可选值数组,或者它可能是一个简单值数组,你应该改变它。或者它可能是一个二维锯齿状数组。或通过引用传递的可选值。
相比之下,以 ML 语言家族(F#、OCaML、SML)为例。在这里,这些概念映射到不同的语言结构:
X option
X ref
X array
您当然可以将它们组合起来,int optional ref
即可变值,可以设置为空值或某个整数值。int ref optional
另一方面是可选的可变值;它可以什么都没有(没有人可以改变它)或者它可以是一些可变的 int (你可以将它改变为任何其他可变的它,但不能改变它)。
这些区别非常微妙,但是无论您是否使用 ML 编程,您都必须做出这些区别。在 C 语言中,您必须做出相同的区分,但它们并没有在类型系统中明确说明。您必须非常仔细地阅读文档,否则如果您误解了何时使用哪种指针,您可能会引入 sublte(阅读:难以找到)错误。
在这里,“损坏”意味着它不是“严格”的,导致 C++ 中永无止境的乐趣(由于许多自定义类型(对象)和重载运算符,强制转换成为 C++ 中的一大麻烦)。
对 C 的攻击涉及其错位使用作为严格的 OOP 基础。
C 从未被设计为限制编码人员,因此可能是学术界的挫败感(以及 BS 给予世界的 ++ 的华丽辉煌)。
“我发明了面向对象这个术语,我可以告诉你我并没有想到 C++”
(艾伦·凯)