C语言
在C语言中,“数据”及“处理数据的操作”是分开声明的,这种处理方式被称为procedural:一组“分布在各个以功能为导向的函数中”的算法驱动和处理外部数据。
C struct实例
假设我们当前存在一个struct Point3d:1
2
3
4
5typedef struct point3d{
float x;
float y;
float z;
} Point3d;
如果我们需要打印该Point3d,则必须定义这样一个函数:1
2
3void Point3d_print(const Point3d *pd){
printf("(%f,%f,%f)",pd->x,pd->y,pd->z);
}
又或者为了追求效率,直接使用函数宏:1
同样地,某个点的特定坐标可以直接存取:1
2
3
4
5Point3d pt;
pt.x = 0.0;
//宏操作
SetX(pt,0.0);
C++
ADT
C++中实现Point3d可能会使用独立的“抽象数据类型”(abstract data type,ADT)来实现:1
2
3
4
5
6
7
8class Point3d{
public:
...
private:
...
};
inline
ostream& operator<<(ostream &os,const Point3d &pt){...}
继承体系
又或者以一个两层到三层的继承体系完成:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class Point{
public:
...
protected:
...
};
class Point2d:public Point{
public:
...
protected:
...
};
class Point3d:public Point2d{
public:
...
protected:
...
};
模板化
我们甚至可以更进一步地抽象,将坐标值类型甚至坐标数量参数化:1
2
3
4
5
6
7template <typename type,int dim>
class Point{
public:
...
private:
...//内含数组或容器
}
对比
仅从Point来看,C与C++处理问题的方式截然不同,这并非是语言间的区别,而是过程式与面向对象的差别。
从软件工程的角度而言,封装性比全局数据要好,但也付出了编写和使用上的代价。
封装的成本
从Point的角度而言,封装并未带来任何成本:
- data
所有的数据均存储于Object内,就如同struct中的情况一样。 - member function
成员函数虽然包含于class的声明之内,但却不会出现在object中。每一个non-inline成员函数都只会生成一个函数实体。
C++在内存布局以及存储时间上的额外负担主要由virtual引起:
- virtual function
实现函数执行期绑定。 - virtual base class
实现“多次出现在继承体系中的base class在派生类中只存在一个可共享的实体”。
一般而言,我们没有理由认为C++就一定会比C庞大且迟缓。