C没有抽象数据类型的能力,C++支持ADT,同时支持OO,而且还是多继承的OO,同时支持接口继承和实现继承。
所以引出一大堆问题和冲突。
其中最主要的是关于初始化的问题。
我们知道,C/C++严格区分初始化和赋值,很多人觉得挺奇怪,初始化不就是初次赋值么?呵呵,似乎就是,又似乎不是。初始化是在定义的时候赋值。
那位说了,这有啥好处?这涉及到一个程序状态正确性的说明性方法的问题。具体的说:就是涉及到断言的问题。
断言一般分为三种,静态断言、动态断言和不变性断言。所谓静态断言就是论断静态情况下成立的那种断言,初始化就是明显的静态行为。动态断言就是论断程序动态行为中符合的断言,典型的例子就是Eiffel的前置条件和后置条件或者叫做前提和结论。不变性断言是更高层次的断言,它论断程序永远遵从的限制,规定出程序的本质特征,对应于Eiffel的不变式。
其实初始化就是一个静态断言,表明该对象的值静态的等于这个静态初始化表达式的值。
C语言虽然不支持ADT,但是它支持Compound Data和数组,所以它的初始化方法也是比较复杂的。有兴趣的读者可以看看我的另一篇关于 = 0的blog里面涉及到C语言的初始化和组合字面值等问题。
C的初始化的基本语法举例如下:
int i = 6;
struct S
{
int a;
short b;
};
struct S o = {.a = 3, .b = 4};
...
另外还有数组,我就不举例了。
C++当然也可以用这些方法(其实现在似乎不能用,不过我相信以后会能用的),但是,C++又提供了一个更一致和抽象的方法——构造函数。
构造函数的语法如下:
class C
{
public:
C(int i, short s)
: _i(i)
, _s(s)
{}
private:
int _i;
short _s;
};
很多人批评他奇怪的语法,问为什么不像不同的方法一样写在{}里面,对于这个,我的想法是:由于这是一个静态断言,而不是一个赋值运算,所以这个特定的语法是合适的。
对于初始化,语法是这样的:
C o(3, 5);
当然了,也有人会这样写:
C o = C::C(3, 5);
这估计是为了照顾情绪低落的C程序员,主要是为了一个习惯,其实两者是一样的。
T o(initializeExpr)这种语法强烈的影响了C++的文化,甚至连原始类型都可以这样初始化了。
比如:
int i(0);
你可以认为int也是一种类型,它也有一个构造函数,甚至都可以有默认值……
这个统一看起来是小事一桩,其实对于template大有用处。而且还有更深层次的语义改变,你能看出来么?
我直说了,不管怎样T o = xxx这种语法都是不可重用的,你如果想规定第二个o也等于xxx你就得另写一遍或者使用赋值运算符,而如果你想给出一个稍有一点差别的但是基本规则相同的值,你用=语法就没有办法了。
而构造函数导致的T o(xxx)这个语法,使得你可以重用构造规定的一系列静态断言和赋值规则(顺便说一句,int i()保证i为0,这就是一种重用),第一种语法没有这个重用性。
C++的麻烦在于它有两种初始化的语法和语义,这会导致语言复杂,编译器复杂,最主要的是导致程序员迷惑,这是生产率的大敌。
不说了,赶快回家吃饭吧。
根据上面说的东西,大家一定会认为我赞成构造函数而反对复合字面常量,其实不是这样的。两者各有用处,相对来说,构造函数这种方式比较重量一些,复合字面常量轻量一些,联系现在流行的敏捷开发,现在流行的脚本式语言,我们可以感觉出来轻量级方法的优势。
所以引出一大堆问题和冲突。
其中最主要的是关于初始化的问题。
我们知道,C/C++严格区分初始化和赋值,很多人觉得挺奇怪,初始化不就是初次赋值么?呵呵,似乎就是,又似乎不是。初始化是在定义的时候赋值。
那位说了,这有啥好处?这涉及到一个程序状态正确性的说明性方法的问题。具体的说:就是涉及到断言的问题。
断言一般分为三种,静态断言、动态断言和不变性断言。所谓静态断言就是论断静态情况下成立的那种断言,初始化就是明显的静态行为。动态断言就是论断程序动态行为中符合的断言,典型的例子就是Eiffel的前置条件和后置条件或者叫做前提和结论。不变性断言是更高层次的断言,它论断程序永远遵从的限制,规定出程序的本质特征,对应于Eiffel的不变式。
其实初始化就是一个静态断言,表明该对象的值静态的等于这个静态初始化表达式的值。
C语言虽然不支持ADT,但是它支持Compound Data和数组,所以它的初始化方法也是比较复杂的。有兴趣的读者可以看看我的另一篇关于 = 0的blog里面涉及到C语言的初始化和组合字面值等问题。
C的初始化的基本语法举例如下:
int i = 6;
struct S
{
int a;
short b;
};
struct S o = {.a = 3, .b = 4};
...
另外还有数组,我就不举例了。
C++当然也可以用这些方法(其实现在似乎不能用,不过我相信以后会能用的),但是,C++又提供了一个更一致和抽象的方法——构造函数。
构造函数的语法如下:
class C
{
public:
C(int i, short s)
: _i(i)
, _s(s)
{}
private:
int _i;
short _s;
};
很多人批评他奇怪的语法,问为什么不像不同的方法一样写在{}里面,对于这个,我的想法是:由于这是一个静态断言,而不是一个赋值运算,所以这个特定的语法是合适的。
对于初始化,语法是这样的:
C o(3, 5);
当然了,也有人会这样写:
C o = C::C(3, 5);
这估计是为了照顾情绪低落的C程序员,主要是为了一个习惯,其实两者是一样的。
T o(initializeExpr)这种语法强烈的影响了C++的文化,甚至连原始类型都可以这样初始化了。
比如:
int i(0);
你可以认为int也是一种类型,它也有一个构造函数,甚至都可以有默认值……
这个统一看起来是小事一桩,其实对于template大有用处。而且还有更深层次的语义改变,你能看出来么?
我直说了,不管怎样T o = xxx这种语法都是不可重用的,你如果想规定第二个o也等于xxx你就得另写一遍或者使用赋值运算符,而如果你想给出一个稍有一点差别的但是基本规则相同的值,你用=语法就没有办法了。
而构造函数导致的T o(xxx)这个语法,使得你可以重用构造规定的一系列静态断言和赋值规则(顺便说一句,int i()保证i为0,这就是一种重用),第一种语法没有这个重用性。
C++的麻烦在于它有两种初始化的语法和语义,这会导致语言复杂,编译器复杂,最主要的是导致程序员迷惑,这是生产率的大敌。
不说了,赶快回家吃饭吧。
根据上面说的东西,大家一定会认为我赞成构造函数而反对复合字面常量,其实不是这样的。两者各有用处,相对来说,构造函数这种方式比较重量一些,复合字面常量轻量一些,联系现在流行的敏捷开发,现在流行的脚本式语言,我们可以感觉出来轻量级方法的优势。