《深度探索C++对象模型》——C++与C程序风格对比

在C语言中,“数据”和“处理数据的操作(函数)”是分开来声明的。由一组“分布在各个以功能为导向的函数中”的算法所驱动,处理共同的外部数据。

先看一个例子:

如果我们声明一个struct Point3d,

typedef struct point3d
{
	float x;
	float y;
	float z;
}Point3d;

欲打印一个Point3d,可能就得定义一个像这样的函数:

void
Point3d_print( const Point3d *pd)
{
	printf("(%g, %g, %g)\n", pd->x, pd->y, pd->z);
}

或者,定义这样的宏:

#define Point3d_print( pd ) 	printf("(%g, %g, %g)\n", pd.x, pd.y, pd.z);

或者,直接在程序调用处完成其操作:

void
my_foo()
{
	Point3d *pd = get_a_point();
	...
	/*直接打印出  point ...*/
	printf("(%g, %g, %g)\n", pd.x, pd.y, pd.z);
}

同样的道理,某个点的特定坐标值可以直接存取:

Poind3d  pt;
pt.x = 0.0;

或者,

#define X( p, xval ) (p.x) = (xval);
…
X( pt, 0.0);

在C++中,Point3d有可能用独立的“抽象数据类型(ADT)”来实现:

class Point3d
{
public:
	Point3d( float x = 0.0, float y = 0.0, float z = 0.0 ):
	  _x(x), _y(y), _z(z){}
	~Point3d(){}
	float x(){ return _x;}
	float y(){ return _y;}
	float z(){ return _z;}
	//...etc..
private:
	float _x;
	float _y;
	float _z;
};

inline ostream&
	operator<<(ostream &os, const Point3d &pt)
{
	os << "(" << pt.x << "," << pt.y << "," << pt.z << ")";
};

或者,

class Point {
public:
	Point( float x = 0.0 ):_x(x){}
	float x(){return _x;}
	void x( float xval ){ _x = xval;}
	//...
protected:
	float _x;
};

class Point2d : Point {
public:
	Point2d( float x = 0.0, float y = 0.0 ):Point(x), _y(y){}
	float y(){ return _y; }
	void y( float yval ){ _y = yval; }
	//...
protected:
	float _y;
};

class Point3d : Point2d {
public:
	Point3d( float x = 0.0, float y = 0.0, float z = 0.0 ):Point2d(x, y), _z(z){}
	float z(){return _z;}
	void z( float zval ){ _z = zval;}
	//...
protected:
	float _z;
};

或者,坐标类型参数化:

template < class type >
class Point3d
{
public:
	Point3d( type x = 0.0, type y = 0.0, type z = 0.0 ):
	  _x(x), _y(y), _z(z){}
	~Point3d(){}
	type x(){ return _x;}
	type y(){ return _y;}
	type z(){ return _z;}
	void x( type xval ){ _x = xval;}
	//...etc..
private:
	type _x;
	type _y;
	type _z;
};

或者,坐标类型和坐标数目均参数化:

template< class type, int dim>
class Point
{
	~Point(){};
	Point( type coords[dim] ){
		for( int index = 0; index < dim; index++ )
			__coords[index] = coords[index];
	}
	type& operator[]( int index ){
		assert(index < dim && index >= 0);
		return __coords[index];
	}
	//...etc...
private:
	type __coords[dim];
};

inline
	template< class type, int dim>
ostream&
	operator<<( ostream &os, const Point< type, dim> &pt )
{
	os << "(";
	for ( int ix = 0; ix < dim - 1; ix++)
	{
		os << pt[ix] << ", ";
	}
	os << pt[dim - 1];
	os << ")";
};

根据上面的例子,很明显的看出,C 程序和 C++ 程序风格上有截然不同,在程序的思考上也有明显的差异。大家看到 Point3d 转到 C++ 之后,是不是反而更加复杂?布局成本是不是增加更多了?答案是 class Point3d 并没有增加成本。三个 data member 直接内含在每一个 class object 之中,就像 C struct  的情况一样。而 member function 虽然含在 class 的声明之内,却不出现在 object 之中。每一个 non-line member function 只会诞生一个函数实体,而不是 inline function 在每一个模块使用者身上产生一个函数实体。事实上,C++ 在布局以及存取时间上主要的额外负担是由 virtual 引起的,包括:

  • virtual function 机制  用以支持一个有效率的“执行期绑定”(running binding)
  • virtual base class     用以实现“多次出现在继承体系中的base class ,有一个单一而被共享的实体”。

此外,还有一些多重继承下的额外负担,发生在“一个derived class 和其二或者后继之 base class的转换”之间。

至此,你也许会问,你咋知道 C++ 的布局成本没有增加成本?C++ 布局又是咋样的?这里面说的 virtual又是怎么给 C++ 在布局以及存取时间上引起额外的负担?敬请关注下一篇博客《C++对象模型》。

时间: 2024-08-26 08:16:54

《深度探索C++对象模型》——C++与C程序风格对比的相关文章

深度探索C++对象模型的读书心得

参考:深度探索C++对象模型 (stanley B.Lippman著 侯捷翻译) 1. Page9 : C++对象模型,说明每一个类仅有一个虚函数表Vtbl,而类的每一个对象都有指向其表的指针. 2. Page30:引用也需要与一个指针(大小为4BYTE)相同的空间. 3. Page28: 指针类型会教导编译器如何解释某个特定地址中的内存内容及大小. 4. Page39: explicit关键字能够制止"单一参数的constructor被当做一个Conversion运算符" 5. Pa

【深度探索C++对象模型】第二章 构造函数语意学(上)

第二章 构造函数语意学(The Semantics of Constructors) -- 本书作者:Stanley B.Lippman 一.前言 首先让我们来梳理一个概念: 默认构造函数(Default Constructor) : 是在没有显示提供初始化式时调用的构造函数.它由不带任何参数的构造函数,或是为所有形参提供默认实参的构造函数定义.如果定义的某个类的成员变量没有提供显示的初始化式时,就会调用默认构造函数(Default Contructor). 如果用户的类里面,没有显示的定义任何

《深度探索c++对象模型》chapter1关于对象对象模型

在c++中,有2种class data member:static和nostatic,以及3钟class member function:static,nostatic和virtual.已知下面这个class Point声明: class Point { public: Point(float xval); virtual ~Point(); float x() const; static int PointCount(); protected: virtual ostream& print(o

【深度探索C++对象模型】第一章 关于对象

第一章 关于对象(Object Lessons) -- 本书作者:Stanley B.Lippman 一.前言 什么是 C++ 对象模型:简单的说,就是 C++ 中面向对象的底层实现机制. 本书组织: 第 1 章,关于对象(Object Lessons),介绍 C++ 对象的基础概念,给读者一个粗略的了解. 第 2 章,构造函数语意学(The Semantics of Constructors),构造函数什么时候会被编译器合成?它给我们的程序效率带来了怎样的影响? 第 3 章,Data语意学(T

深度探索C++对象模型第6章 执行期语意学

(一)对象的构造和析构(Object Construction and Destruction) 一般而言我们会把object尽可能放置在使用它的那个程序区段附近,这么做可以节省非必要的对象产生操作和摧毁操作. 全局对象 如果我们有以下程序片段: Matrix identity main() { //identity 必须在此处被初始化 Matrix m1=identity; ... return 0; } C++保证,一定会在main()函数中第一次用到identity之前,把identity

【深度探索c++对象模型】Function语义学之虚函数

虚函数的一般实现模型:每一个class有一个virtual table,内含该class之中有作用的virtual function的地址,然后每个object有一个vptr,指向virtual table. 识别class是否支持多态,唯一恰当的方法是看它是否有virtual function,只要class拥有virtual function,它就需要额外的执行期信息. 考虑ptr->z(),ptr是基类指针,z是虚函数,为了找到并调用z()的适当实体,我们需要两项信息: 1.ptr所指对象

《深度探索c++对象模型》chapter3 Data语意学

一个空的class:如 class X{} ; sizeof(X)==1; sizeof为什么为1,他有一个隐晦的1 byte,那是被编译器安插进去的一个char,这使得class2的两个objects得以在内存中配置独一无二的地址: X a,b; if(&a==&b) cerr<<"yipes!"<<endl; class X{}; class Y:public virtual X{}; class Z:public virtual X{};

深度探索C++对象模型 第三章 Data 语意学

一个有趣的问题:下列 类 sizeof大小 class X{}    //1 class Y:public virtual X{} //4 or 8 class Z:public virtual X{} // 4 or 8 class A:public Y,public Z{} // 8 or 12 主要原因:为了保持每一个类生成对象在内存中的唯一性,编译器必须要给空类生成一个char来维持object的唯一性: 而virtual继承中,仅保持了base class的指针,有些编译器会继承bas

【深度探索C++对象模型】data语义学

class X{}; class Y :public virtual X{}; class Z :public virtual X{}; class A :public Y, public Z{}; void main() { cout << sizeof(X) << " " << sizeof(Y) << " " << sizeof(Z) << " " << s

深度探索C++对象模型 第二章构造函数语意学

在使用C++时,常常会好奇或者抱怨,编译器为我们做了什么事呢? 为什么构造函数没有为我初始化呢?为什么我还要写默认构造函数呢? 2.1 Default Constructor 的构造操作 如果没有声明默认构造函数,编译器会在需要的时候帮我们产生出来. 为了避免在多个地方被需要导致重复,则编译器将产生的构造函数声明为inline方式. class Foo {public:Foo(), Foo(int) }; class Bar {public: Foo foo;char *str;} Bar ba