zoukankan      html  css  js  c++  java
  • 关于对象

    image

    这是一个C语言结构,需要打印该信息时需要另外定义接口:Point3d_print(const point3d *pd);

    但在C++中,可能采用了封装形式:

    image

    像C++那样给数据进行class的封装之后,布局的成本到底增加了多少呢?答案是没有增加成本。

    3个数据成员直接内涵在一个class对象里,就像C的struct一样,而成员函数虽然含在class声明里,却不在对象之中。

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

    1. Virtual function:用以支持一个有效率的“执行期绑定”;

    2. Virtual base class:用以实现“多次出现在继承体系中的base class,有一个单一而被共享的实体”;

    在C++中,有2种类成员,静态的和非静态的;以及3种类成员函数,静态的、非静态的、虚拟的。

     1 class Point
     2 {
     3 public:
     4     Point(float xval);
     5     virtual ~Point();
     6 
     7     float x() const;
     8     static int PointCount();
     9 protected:
    10     virtual ostream& print(ostream &os) const;
    11 private:
    12     float _x;
    13     static int _pointCount;
    14 };

    -----------------------------------简单对象模型(没有被应用和实现)-----------------------------------

    这个类的对象模型是一系列的slots,每一个slots指向一个members。members按照其声明次序,依次被指定一个slots。

    每一个data member或function member都有自己的一个slot。

    这是简单的对象模式,其中member都不放在对象中,只有指向member的指针才放在对象内。这么做避免了members的不同类型而导致的不同存储空间需求。

    对象中的成员都是以slot索引值来寻址,其中_x成员的索引是6,_point_count的成员索引是7,所以一个对象的大小就很容易的被计算出来:

    对象大小 = 指针大小 * 所声明的成员数量;

    -----------------------------------表格驱动对象模型(没有被应用和实现)-----------------------------------

    还是因为避免members的类型不同而导致的空间需求,另外种模型是将所有与members有关的信息抽象出来。

    做成2个表:“数据成员表” 和 “函数成员表”,对象本身则内涵指向这2个表的指针:
    1. 数据成员表保存数据,占用数据类型的相应内存空间;

    2. 函数成员表保存slots,指向每一个函数地址;

    虽然这个模型未能被采用,但virtual function却采用了这个模型。

    -----------------------------------C++对象模型(被采用了)-----------------------------------

    什么是C++模型呢?

    原作者将简单对象模型进行了改动,并对内存空间和存取时间都做了优化。此模型中:

    1. non-static data member被配置在每一个对象中;

    2. static data member则被放置在对象之外;

    3. non-static 和 static 的function members都被放在每个对象之外;

    4. virtual functions则采用了表格驱动的模型:每一个类生成了一堆指向virtual function的指针,这些指针放在virtual functions table中,而对象则内部保存了一个指针,指向该table

    优点在于:存取时间和空间的效率;

    缺点在于:非静态数据成员有修改,则也需要重新编译;

    C++支持单一继承:

      class Library_materials {...};

      class Book : public Liarary_materials {...};

      class Rental_book : public Book {...};

    C++支持多重继承:

      class istream : public istream, public ostream {...};

    虚拟继承:

      class istream : virtual public ios {...};

      class ostream : virtual public ios {...};

    虚拟继承的情况下,基类无论被继承多少次(n个派生类),永远只会存在一个实体。

    那么,对象模型对编程来说有什么影响?

    对象模型的不同,导致:

    “现有的程序代码必须修改” 或 “必须加入新的代码”;

     1 X foobar()
     2 {
     3     X xx;
     4     X *px = new X;
     5 
     6     // foo()是一个虚函数 
     7     xx.foo();
     8     px->foo();
     9 
    10     delete px;
    11     return xx;
    12 }

    这个函数可能在内部被转化为:

     1 void foobar(X &_result)
     2 {
     3     //构造_result
     4     _result.X::X();
     5 
     6     //扩展X *px = new X
     7     px = _new(sizeof(X));
     8     if(px != 0)
     9         px->X::X();
    10     
    11     //扩展xx.foo()但不使用virtual机制
    12     foo(&_result);
    13     
    14     //使用virtual机制扩展px->foo()
    15     (*px->vtbl[2])(px)
    16 
    17     //扩展delete px
    18     if(px != 0) {
    19         (*px->vtbl[1])(px);
    20         _delete(px);
    21     }
    22 
    23     //不需使用named return statement
    24     //不需要摧毁local object xx
    25     return;
    26 }

    C++支持的设计模型:

    1. 程序模型:和C一样的开发。就像字符串处理中,使用字符数组(char str[] = “abcdefg”)和字符指针(char *str)。

    2. 数据抽象模型(ADT)。

    3. 面向对象模型:数据和方法通过一个抽象的class封装起来。

    1 //描述object:不确定类型
    2 Librar_materials *px = retrieve_some_meterial();
    3 Librar_materials &rx = *px;
    4 
    5 //描述已知类型
    6 Librar_materials dx = *px;

    指针px指向了一个未知类型的对象地址;

    引用rx被赋予一个未知类型的对象地址;

    这个对象可能是Librar_materials类型对象,也有可能是它的一个子类型;

    但dx一定是Librar_materials类型的对象;

    所以,对于对象的多态操作,要求对象必须通过一个"指针"或"引用"来存取(动态完成)

    C++中,多态只存在于一个public class体系中,也就是说,px只能指向自我类型的对象,或者就是派生类对象。

    C++支持多态的形式:

    1. 隐含的转化操作:

      shape *ps = new circle();

    2. 通过virtual function机制:

      ps->rotate;

    3. 通过dynamic_cast和typeid运算符:

      if(circle *pc = dynamic_cast<circle *> (ps))...

    C++指针的类型

    对象指针?数据类型指针?模板数组指针?

    指针都需要足够的内存来放置一个机器地址,而指向各不同类型的指针,在于其所对应的类型:

    所以,一个指针可以指向一个地址(比如1000),单该地址的跨度空间(1000~1xxx?)是根据指针类型决定的。由此可见,一个void*类型的指针,确实能够指向一个地址,单无论如何都无法确认空间跨度,所以也无法用该指针来操作某个object。

    所谓转型,并非改变一个指针所指向的地址,而只影响“该指针所指出的内存大小和内容”的解释方式。

     加上多态之后.....:

     1 class zooAnimal
     2 {
     3 public:
     4     zooAnimal();
     5     virtual ~zooAnimal();
     6     virtual void rotate();
     7 protected:
     8     int loc;
     9     String name;
    10 };
     1 class bear : public zooAnimal
     2 {
     3 public:
     4     bear();
     5     ~bear();
     6     void rotate();
     7     virtual void dance();
     8 protected:
     9     enum Dances{...};
    10     Dances dances_known;
    11     int cell_block;
    12 }
    13 bear b("youyou");
    14 bear *pb = &b;
    15 bear &rb = *pb;

    首先,对象b所占用的空间为 sizeof(zooAnimal) + sizeof(enum)(4字节) + sizeof(int)(4字节);

    而指针pb自身只占用4个字节的地址空间,其指向zooAnimal类型地址的首地址;而引用rb被赋予了pb的地址,所以rb自身的地址和pb的地址是同一个地址;

    1 bear b;
    2 zooAnimal *pz = &b;
    3 bear *pb = &b;
    4 //那么指针pz和pb都是被指向了对象b的首地址
    5 //他们区别是什么?

    是的,区别就是涵盖的地址跨度不同,pb的跨度是整个bear对象,而pz则是bear对象的zooAnimal空间部分。当然,我们也不能通过pz指针来操作属于bear的任何成员。

    除非我们通过virtual机制.....

     1 //不合法:cell_block并不属于zooAnimal的一个member
     2 //即使pz确实指向了bear对象的首地址
     3 pz->cell_block;
     4 
     5 //经过一个强制类型转换,可以操作!
     6 //强制调整操作的内存空间跨度
     7 ((bear *)pz)->cell_block;
     8 
     9 //动态类型转换:dynamic_cast<>
    10 //和上面的强制转换区别在于运行时期的转换,而非静态转换
    11 if(bear *pb2 = dynamic_cast<bear *>(pz))
    12     pb2->cell_block;
    13 
    14 //可以!cell_block就是bear类型的一个成员
    15 pb->cell_block;

    当我们写上:

    pz->rotate();时,pz的类型将在编译时期决定。

    在每一个执行点,编译器都会进行一个决定:pz所指定的对象类型决定了rotate()所调用的实体。类型信息的封装并不在pz中,而是在link中(在对象的vptr和vptbl中)。

    之所以指针和引用能够支持多态,就是因为它们并不会引发内存中任何“与类型有关的内存委托操作”,会受到改变的只有它们所指向的内存“大小和内容解释方式”而已。

    “与类型有关的内存委托操作”:当一个基类对象被直接初始化为一个基类对象时(其实就是基类指针被指向子类对象),子类对象会自动被执行切割,塞入较小的内存空间中。

    意思就是说明该指针被初始化时就决定了对象空间大小和内容,这时候如果调用该指针进行操作,则只能操作基类的操作。(静态)

    但如果声明了virtual,那么就只有当该指针被动态操作时(调用时),才会决定指针所指向的对象空间大小(就是该指针所指向的对象空间所记录的空间大小信息)——子类对象。

    1 zooAnimal za;
    2 zooAnimal *pza;
    3 
    4 bear b;
    5 panda *pp = new panda;
    6 
    7 pza = &b;

  • 相关阅读:
    spring加载bean实例化顺序
    Java生成CSV文件实例详解
    JSch
    socket(一)
    Core Data
    运行时c函数
    ReactiveCocoa(RAC)
    先来个xmpp学习连接
    FMDB
    NSKeyedArchive(存储自定义对象)
  • 原文地址:https://www.cnblogs.com/davidsguo008/p/3645110.html
Copyright © 2011-2022 走看看