1.3 对象的差异
1.3.1 C++支持三种程序设计范型
1. 过程型(procedural model):C语言作为典型,按照任务流程一一处理;
2. 抽象数据类型模型(ADT/Object-Based):通过一组公共接口操作,但其运算定义并不对外公开,程序员处理的是一个拥有固定而单一类型的实体;
3. 面向对象模型(OO):模型中存在一些彼此相关的类型,通过一个抽象的基类(用以提供公共接口)被封装起来,将继承与多态作为典型来思考可以简化理解。程序员处理的实体是未知类型(虽然有所界定),只有执行到每一个特定执行点时才能够获得其真实类型,在C++中通过引用或指针操作才能完成,通过传值则会引起对象切割问题。
OB与OO的关键差别在于OB不支持类型扩充,但一个OB设计比一个对等的OO设计速度更快,空间更紧凑。速度快是因为所有的函数引发操作都在编译期解析完成,对象建构起来时不需要设置virtual机制;空间紧凑是因为每一个class object不需要负担传统上未了支持virtual机制而需要的额外负担。
ADT与OO的比较示例:
class Lib_Material { …… };
class book : public Lib_Material { …… };
class viedo : public Lib_Material { …… };
1.3.1 C++支持三种程序设计范型
1. 过程型(procedural model):C语言作为典型,按照任务流程一一处理;
2. 抽象数据类型模型(ADT/Object-Based):通过一组公共接口操作,但其运算定义并不对外公开,程序员处理的是一个拥有固定而单一类型的实体;
3. 面向对象模型(OO):模型中存在一些彼此相关的类型,通过一个抽象的基类(用以提供公共接口)被封装起来,将继承与多态作为典型来思考可以简化理解。程序员处理的实体是未知类型(虽然有所界定),只有执行到每一个特定执行点时才能够获得其真实类型,在C++中通过引用或指针操作才能完成,通过传值则会引起对象切割问题。
OB与OO的关键差别在于OB不支持类型扩充,但一个OB设计比一个对等的OO设计速度更快,空间更紧凑。速度快是因为所有的函数引发操作都在编译期解析完成,对象建构起来时不需要设置virtual机制;空间紧凑是因为每一个class object不需要负担传统上未了支持virtual机制而需要的额外负担。
ADT与OO的比较示例:
class Lib_Material { …… };
class book : public Lib_Material { …… };
class viedo : public Lib_Material { …… };
//OO -- 未知类型
//plm与rlm可能指向一个Lib_Material或者其派生类型
Lib_Material *plm = get_material();
Lib_Material &rlm = get_material();
//plm与rlm可能指向一个Lib_Material或者其派生类型
Lib_Material *plm = get_material();
Lib_Material &rlm = get_material();
//ADT – 确切类型
//dlm一定是一个Lib_Material对象
Lib_Material dlm = *plm;
//dlm一定是一个Lib_Material对象
Lib_Material dlm = *plm;
1.3.2 C++中的多态特性
C++中,多态只存在于一个个的public class体系中,对非public的派生行为以及类型为void*的指针可以说是多态,但并没有被语言明白地支持,即需要程序员通过明确的转型操作来管理。多态的主要用途是经由一个共同的接口来影响类型的封装,接口通常被定义在一个抽象的base class中,通过指针或引用在运行期决定其真正类型。
C++支持多态的方式:
1. 经由一组隐含的转化操作,如下例中①;
2. 经由虚函数机制,如下例中②;
3. 经由dynamic_cast和typeid运算符,如下例中③。
class Shape {
public:
virtual void rotate() { cout << “Shape::rotate()” << endl; }
};
class Circle : public Shape {
public:
void rotate() { cout << “Circle::rotate()” << endl; }
};
C++中,多态只存在于一个个的public class体系中,对非public的派生行为以及类型为void*的指针可以说是多态,但并没有被语言明白地支持,即需要程序员通过明确的转型操作来管理。多态的主要用途是经由一个共同的接口来影响类型的封装,接口通常被定义在一个抽象的base class中,通过指针或引用在运行期决定其真正类型。
C++支持多态的方式:
1. 经由一组隐含的转化操作,如下例中①;
2. 经由虚函数机制,如下例中②;
3. 经由dynamic_cast和typeid运算符,如下例中③。
class Shape {
public:
virtual void rotate() { cout << “Shape::rotate()” << endl; }
};
class Circle : public Shape {
public:
void rotate() { cout << “Circle::rotate()” << endl; }
};
Shape *ps = new Circle(); //-----①
ps->rotate(); //-----②
Circle *pc = dynamic_cast<Circle*>(ps); //-----③
Shape s = *ps;
s.rotate(); //非多态,Shape::rotate()
ps->rotate(); //-----②
Circle *pc = dynamic_cast<Circle*>(ps); //-----③
Shape s = *ps;
s.rotate(); //非多态,Shape::rotate()
1.3.3 类对象占用空间的组成部分
1. non-static数据成员的总大小;
2. 根据边界对齐需求增设的空间――存放于成员之间或者集合体边界;
3. 为了支持virtual而由内部产生的任何额外负担,如vptr等。
1.3.4 指针类型
指针本身所需要的内存空间是固定的(32位系统中为4bytes),而不管它指向的是什么数据类型。所谓“指向不同类型之各指针”之间的差异,不在于指针的表示法不同,也不在于其内容不同,而是在其所寻址出来的对象类型不同,因此“指针类型”会教导百衲衣起如何解释某个特定地址中的内存内容及其大小。注意:
1. 类型为void*的指针只能够含有一个地址,而不能通过它操作所指的对象;
2. 转型cast只是一种编译器指令,大部分情况下它并不改变一个指针所含有的真正地址,只影响“被指出的内存大小和其内容”的解释方式。
3. 采用拷贝构造函数或者赋值操作将一个对象内容完整拷贝到另一个对象中去的时候,此处指派生类对象拷贝到基类对象,其基类对象的vptr并不指向派生类的vtbl。解释原因为:编译器必须确保如果某个对象含有一个或一个以上的vptr,那些vptr的内容不会被基类对象初始化或改变,例如:(未理解)
class ZooAnimal {
public:
virtual ~ZooAnimal();
};
class Bear : public ZooAnimal { …… };
Bear b;
ZooAnimal za = b; //注意vptr的指向问题
1. non-static数据成员的总大小;
2. 根据边界对齐需求增设的空间――存放于成员之间或者集合体边界;
3. 为了支持virtual而由内部产生的任何额外负担,如vptr等。
1.3.4 指针类型
指针本身所需要的内存空间是固定的(32位系统中为4bytes),而不管它指向的是什么数据类型。所谓“指向不同类型之各指针”之间的差异,不在于指针的表示法不同,也不在于其内容不同,而是在其所寻址出来的对象类型不同,因此“指针类型”会教导百衲衣起如何解释某个特定地址中的内存内容及其大小。注意:
1. 类型为void*的指针只能够含有一个地址,而不能通过它操作所指的对象;
2. 转型cast只是一种编译器指令,大部分情况下它并不改变一个指针所含有的真正地址,只影响“被指出的内存大小和其内容”的解释方式。
3. 采用拷贝构造函数或者赋值操作将一个对象内容完整拷贝到另一个对象中去的时候,此处指派生类对象拷贝到基类对象,其基类对象的vptr并不指向派生类的vtbl。解释原因为:编译器必须确保如果某个对象含有一个或一个以上的vptr,那些vptr的内容不会被基类对象初始化或改变,例如:(未理解)
class ZooAnimal {
public:
virtual ~ZooAnimal();
};
class Bear : public ZooAnimal { …… };
Bear b;
ZooAnimal za = b; //注意vptr的指向问题