如果不是为了努力维护与C之间的兼容性,C++远比现在更简单些。
如果C++并不需要支持C原有的struct,那么class的观念可以借由关键词class来支持。但是,从C迁徙到C++,除了效率,另外一个最长被程序员问道的问题就是:什么时候一个人应该再C++程序中以struct取代class?
关键词的困扰
答案之一是:当它让一个人感觉比较好的时候。
这个答案之处一个重要的特性:关键词struct本身并不一定象征其后随之声明的任何东西。我们可以使用struct取代class,但仍然声明public、protected、private等等存取字段与一个完全public的接口,以及virtual functions和单一继承、多重继承、虚拟继承......
如果这个语言只支持一个关键词,可以省掉许多混淆与迷惑。但是,如果C++要支持现存的C程序代码,它就不能不支持struct。引入class这个关键词,使人非常满意,因为这个语言所引入的不只是关键词,还有它支持的封装和继承的哲学。
策略性正确的struct(The Politiccally Conrrect Struct)
C程序员的巧设有时候却成为了C++程序员的陷阱。例如把一个元素的数组放在一个struct的尾端,于是每个struct objects可以拥有可变大小的数组:
struct mumble
{
//stuff
char pc[1];
};
//从档案或标准输入装置中取得一个字符串,
//然后为struct本身和该字符串配置足够的内存
struct mumble* pmum1 = (struct mumble*)
malloc(sizeof(struct mumble) + strlen(string) + 1);
strcpy(&mumble.pc,string);
如果我们改用class来声明,而该class是:
- 指定多个access sections,内含数据;
- 从另一个class派生而来;
- 定义一个或多个virtual functions
那么或许可以顺利转化,但也或许不行。
C++中凡处于同一个access section的数据,必定保证以其声明次序出现在内存布局中,然而被放置在多个access sections中的各笔数据,排列次序就不一定了。下面的声明,前述的C伎俩可以有效运行,或许不能,需视protect data members被放在private data members的前面或后面而定(放在前面才可以):
class stumble
{
public:
//operations
protected:
//protected stuff
private:
char pc[1];
};
同样道理,base classes和derived classes的data member的布局也没有谁先谁后的强制规定,因而也就不能保证前述的C伎俩一定有效。Virtual functions的存在使前述伎俩的有效性成为一个问号。最好的忠告就是:不要那么做!
如果一个程序员迫切需要一个相当复杂的C++ class的某部分数据,使它拥有C声明的那样子,那么那一部分最好抽取出来成为一个独立的struct声明。将C和C++组合在一起的做法就是,从C struct中派生出C++的部分:
struct C_point{...};
class Point: public C_point{...};
于是C和C++的两种用法都可以获得支持:
extern void draw_line(Point,Point);
extern "C" void draw_rect(C_point,C_point);
draw_line(Point(0,0),Point(100,100));
draw_rect(Point(0,0),Point(100,100));
这种习惯用法现在不再被推荐,因为某些编译器(例如Microsoft C++)在支持virtual function的机制中对于class的继承布局做一些改变。推荐使用组合:
struct C_point{...};
class Point
{
public:
operations C_point(){return _c_point;}
// ...
private:
C_point _c_point;
// ...
};
C struct在C++中的一个合理用途,是当你要传递“一个复杂的class object 的全部或者部分”到某个C函数中去时,并保证拥有与C兼容的空间布局。然而这项保证只有在组合(composition)的情况下才存在。如果是“继承”而不是“组合”,编译器会决定是否应该有额外的data members被安插到base struct subobject之中。