关于对象——前言

C语言

 
在C语言中,“数据”及“处理数据的操作”是分开声明的,这种处理方式被称为procedural:一组“分布在各个以功能为导向的函数中”的算法驱动和处理外部数据。

C struct实例

假设我们当前存在一个struct Point3d:

1
2
3
4
5
typedef struct point3d{
float x;
float y;
float z;
} Point3d;

如果我们需要打印该Point3d,则必须定义这样一个函数:
1
2
3
void Point3d_print(const Point3d *pd){
printf("(%f,%f,%f)",pd->x,pd->y,pd->z);
}

又或者为了追求效率,直接使用函数宏:
1
#define Point3d_print(pd)   printf("(%f,%f,%f)",pd->x,pd->y,pd->z);

同样地,某个点的特定坐标可以直接存取:
1
2
3
4
5
Point3d pt;
pt.x = 0.0;
//宏操作
# define SetX(p,xval) (p.x)=(xval);
SetX(pt,0.0);


C++

ADT

C++中实现Point3d可能会使用独立的“抽象数据类型”(abstract data type,ADT)来实现:

1
2
3
4
5
6
7
8
class Point3d{
public:
...
private:
...
};
inline
ostream& operator<<(ostream &os,const Point3d &pt){...}

继承体系

又或者以一个两层到三层的继承体系完成:image_1ccad3v788kbnc78s3bg4bfb9.png-9.4kB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Point{
public:
...
protected:
...
};
class Point2d:public Point{
public:
...
protected:
...
};
class Point3d:public Point2d{
public:
...
protected:
...
};

模板化

我们甚至可以更进一步地抽象,将坐标值类型甚至坐标数量参数化:

1
2
3
4
5
6
7
template <typename type,int dim>
class Point{
public:
...
private:
...//内含数组或容器
}


对比

 
仅从Point来看,C与C++处理问题的方式截然不同,这并非是语言间的区别,而是过程式与面向对象的差别。
从软件工程的角度而言,封装性比全局数据要好,但也付出了编写和使用上的代价。


封装的成本

 
从Point的角度而言,封装并未带来任何成本:

  1. data
    所有的数据均存储于Object内,就如同struct中的情况一样。
  2. member function
    成员函数虽然包含于class的声明之内,但却不会出现在object中。每一个non-inline成员函数都只会生成一个函数实体。

C++在内存布局以及存储时间上的额外负担主要由virtual引起:

  • virtual function
    实现函数执行期绑定。
  • virtual base class
    实现“多次出现在继承体系中的base class在派生类中只存在一个可共享的实体”。

一般而言,我们没有理由认为C++就一定会比C庞大且迟缓。