- C语言:数据与函数分开
声明一个struct Point3d
typedef struct point3d
{
float x;
float y;
float z;
}Point3d;
打印一个Point3d,定义函数或者宏
void Point3d_print(const Point3d *pd)
{
printf("(%g, %g, %g)", pd->x, pd->y, pd->z);
}
#define Point3d_print( pd ) \
printf("(%g, %g, %g)", pd->x, pd->y, pd->z);
或者直接在程序中完成操作
void my_foo()
{
Point3d *pd = get_a_point();
printf("(%g, %g, %g)", pd->x, pd->y, pd->z);
}
某个点的特定坐标值可以直接存取
Point3d pt;
pt.x = 1.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) {}
float x() const { return _x; }
float y() const { return _y; }
float z() const { return _z; }
void x(float xval) { _x = xval; }
void y(float yval) { _y = yval; }
void z(float zval) { _z = zval; }
private:
float _x;
float _y;
float _z;
};
//输入输出运算符必须是非成员函数
//const对象只能调用const成员函数
inline ostream& operator<<(ostream &os, const Point3d &pt)
{
os << "(" << pt.x() << ", " << pt.y() << ", " << pt.z() << ")";
return os;
}
也可以以一个双层或三层的class结构完成
class Point
{
public:
Point(float x = 0.0) : _x(x) {}
float x() const { return _x; }
void x(float xval) { _x = xval; }
protected:
float _x;
};
class Point2d : public Point
{
public:
Point2d(float x = 0.0, float y = 0.0)
: Point(x), _y(y) {}
float y() const { return _y; }
void y(float yval) { _y = yval; }
protected:
float _y;
};
class Point3d : public Point2d
{
public:
Point3d(float x = 0.0, float y = 0.0, float z = 0.0)
: Point2d(x, y), _z(z) {}
float z() const { return _z; }
void z(float zval) { _z = zval; }
protected:
float _z;
};
坐标类型和坐标维度都可以参数化
template<typename T>
class Point3d
{
Point3d(T x = 0.0, T y = 0.0, T z = 0.0)
: _x(x), _y(y), _z(z) {}
T x() const { return _x; }
T y() const { return _y; }
T z() const { return _z; }
void x(T xval) { _x = xval; }
void y(T yval) { _y = yval; }
void z(T zval) { _z = zval; }
private:
T _x;
T _y;
T _z;
};
template<typename T, int dim>
class Point
{
public:
Point();
Point(T coords[dim])
{
for (int index = 0; index < dim; index++)
{
_coords[index] = coords[index];
}
}
T& operator[](int index)
{
assert(index < dim && index >= 0);
return _coords[index];
}
T operator[](int index) const
{
assert(index < dim && index >= 0);
return _coords[index];
}
private:
T _coords[dim];
};
template<typename T, int dim>
ostream& operator<<(ostream &os, const Point<T, dim> &pt)
{
os << "(";
for (int ix = 0; ix < dim - 1; ix++)
{
os << pt[ix] << ", ";
}
os << pt[dim - 1];
os << ")";
return os;
}
int main()
{
float arr[5] = { 1,2,3,4,5.1 };
Point<float, 5> pt(arr);
cout << pt << endl;
return 0;
}
- 加上封装后的布局成本
1.data member没有增加;
2.non-inline member functions不在object中,只会诞生一个函数实例;
3.inline function 在其每一个使用者身上产生一个函数实例。
4.额外负担主要由virtual引起
1.1 C++对象模型
- class data members: static, nonstatic
- calss member functions: static, nonstatic, virtual
已知下面的class Point声明
class Point
{
public:
Point(float xval);
virtual ~Point();
float x() const;
static int PointCount();
protected:
virtual ostream& print(ostream &os) const;
float _x;
static int _point_count;
};
C++对象模型
- nonstatic data members 每一个class object内;
- static data members 存放在class object之外;
- static、nonstatic function members 存放在class object之外;
- 每个class对应一个virtual table (vtbl);
- 每一个class object安插一个指针(vptr),指向vtbl。vptr的设定和重置由构造函数、析构函数和拷贝赋值运算符完成。每一个class所关联的type_info object(用以支持RTTI)也由virtual table被指出来,通常放在表格的第一个slot。
-
缺点:nonstatic data members 有所修改,应用程序代码需要重新编译
加上继承
C++支持单一继承和多重继承
class istream : virtual public ios {};
class ostream : virtual public ios {};
class iostream : public istream, public ostream {};
虚拟继承的情况下,base class不管在继承串链中被派生多少次,永远只会存在一个实例
- base class的data members直接放置于derived class obejct中;
- virtual base class:导入virtual base class table或扩充原来的virtual table。
1.2 关键词所带来的差异
- struct
- class
1.3 对象的差异
- 程序模型
char boy[] = "Danny";
char *p_son;
p_son = new char[strlen(boy) + 1];
strcpy(p_son, boy);
if (!strcmp(p_son, boy))
{
printf("[%s] equals [%s]\n", p_son, boy);
}
delete[] p_son;
- 抽象数据类型模型
string girl = "Anna";
string daughter;
daughter = girl;
if (girl == daughter)
{
cout << "[" + girl + "]" << " equals " << "[" + daughter + "]" << endl;
}
- 面向对象模型:base class用以提供共同的接口,需要通过base class的pointer或reference来完成多态
void check_in(Library_materials *pmat)
{
if(pmat->late())
{
pmat->fine();
}
pmat->check_in();
if(Lender *plend = pmat->reserved())
{
pmat->notify(plend);
}
}
Library_materials thing1;
// class Book : public Library_materials {};
Book book;
thing1 = book;
// 调用Library_materials::check_in()
thing1.check_in();
Library_materials &thing2 = book;
// Book::check_in()
thing2.check_in()
C++以下列方法支持多态
- base class指针指向derived class对象
- 虚函数机制
- dynamic_cast和typeid运算符
shape *ps = new Circle();
ps->rotate();
if( circle *pc = dynamic_cast<Circle *>(ps))
...
表现一个class object需要多少内存
- nonstatic data members的总和大小
- 由于alignment而padding上去的空间
- 为了支持virtual而由内部产生的额外负担
指针的类型
指针本身所需的内存大小是固定的
class ZooAnimal
{
public:
ZooAnimal();
virtual ~ZooAnimal();
virtual void rotate();
protected:
int loc;
string name;
};
ZooAnimal za("Zoey");
ZooAnimal *pza = &za;
- 指向地址1000的整形指针,在32位机器上,将涵盖地址空间1000~1003
- 如果string是传统的8-bytes,那么一个ZooAnimal指针将横跨地址空间1000~1015
加上多态之后
class Bear : public ZooAnimal
{
public:
Bear();
~Bear();
void rotate();
virtual void dance();
protected:
enum Dances {...};
Dances dances_known;
int cell_block;
};
Bear b("Yogi");
Bear *pb = &b;
Bear &rb = *pb;
Bear b;
ZooAnimal *pz = &b;
Bear *pb = &b;
// 不合法
pz->cell_block;
// ok
(static_cast<Bear *>(pz))->cell_block;
// run-time operation
if(Bear *pb2 = dynamic_cast<Bear *>(pz))
pb2->cell_block;
// ok
pb->cell_block;
除了ZooAnimal中出现的members,不能够使用pz来直接处理Bear的任何members。唯一例外是使用virtual机制。