在C语言中,数据和函数(操作数据的方法)是没有关联机制的,它们被分开声明和使用,这样的语言叫做“程序性语言”,由一组分布在各个以功能为导向的算法所驱动,各个函数处理共同的外部数据。
例如,定义一个结构体
typedef struct Point {
int x;
int y;
}Point2d;
当需要对这个结构体对象进行处理时,就需要定义一个函数:
void print_point(const Point2d &pt) {
printf("(%d, %d)", pt.x, pt.y);
}
或者更高效地,定义一个宏:
#define PRINT_POINT(pt) printf("(%d, %d)", pt.x, pt.y);
当需要对其进行修改,就需要这样做:
Point2d pt;
pt.x = 1;
pt.y = 2;
到了C++这里,class机制可以允许你用独立的“抽象数据类型”来实现一个Point2d:
class Point2d{
public:
Point2d(int x, int y) : x_(x), y_(y) { }
int x() const {return x_; }
int y() const {return y_; }
void setX(int x) {x_ = x; }
void setY(int y) {y_ = y; }
private:
int x_;
int y_;
};
inline ostream & operator << (ostream &os, const Point2d &pt) {
os << "(" << pt.x() << ", " << pt.y() << ")";
}
更具有通用性的,定义一组继承关系的类,可以抽象出一维、二维和三维点:
class Point {
public:
Point(int x) : x_(x) { }
int x() const {return x_; }
void setX(int x) {x_ = x; }
protected:
int x_;
}
class Point2d : public Point{
public:
Point2d(int x, int y) : Point(x), y_(y) { }
int y() const {return y_; }
void setY(int y) {y_ = y; }
protected:
int y_;
};
class Point3d : public Point2d {
public:
Point3d(int x, int y, int z) : Point2d(x, y), z_(z) { }
int z() const {return z_; }
void setZ(int z) {z_ = z; }
protected:
int z_;
};
更通用的,使用模板类实现参数抽象化:
template <class T>
class Point3d : public Point2d {
public:
Point3d(T x, T y, T z) : x_(x), y_(y), z_(z) { }
T x() const {return x_; }
T y() const {return y_; }
T z() const {return z_; }
void setX(T x) {x_ = x; }
void setY(T y) {y_ = y; }
void setZ(T z) {z_ = z; }
protected:
T x_;
T y_;
T z_;
};
可见,C++和C,不仅仅在程序的风格上截然不同,在变成的思想上也是有明显的差异:C++旨在实现事务“抽象”,更通用性能更庞大,C语言的优势在于精简(相对C++而言)。
很难说,要定义一个点,上述的使用C语言和C++来实现哪一种更可取,或者哪一种更好、更强!
加上封装以后的空间成本
上述例子中,C++对Point2d的操作方式(在类中定义了一些方法),相对于C的结构体机制来说,增加了多少空间成本呢?答案是:没有增加任何的空间成本!数据成员(data member)直接内含在每一个类对象中,这和C结构体是一样的。至于成员函数,虽然被声明在类的定义中,但是却没有出现在任何一个类对象中,每一个非内联的成员函数(注意必须是非内联)只会产生一个函数实体。
造成空间成本的情况
C++在这两种情况下才会造成额外的空间负担:
- 虚函数:执行期绑定的高效机制,实现C++的多态性
- 虚基类:多次出现在继承体系中,但是只保留一份共享实体的机制。
除此以外,在没有充分的理由来说C++比C更庞大或臃肿。