关于对象

1. c++ 在布局以及存取时间上主要的额外负担是有virtual引起,包括:

virtual function机制:用以支持一个有效率的“执行期绑定” (runtime binding);

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

 

2. c++中,有两种 class data members: static和nonstatic,以及三种class member functions: static, nonstatic 和virtual。

 

3. c++对象模型:

此模型中,nonstatic data members被配置在每一个class object之内,static data members则被存放在所有的class object之外。 static和弄static function members也被放在所有的

class object之外。virtual functions 则以两个步骤支持之:

(1)每一个class产生出一堆指向virtual functions的指针,放在表格之中,这个表格被称为virtual table(vtbl);

(2)每一个class object被添加了一个指针,指向相关的virtual table。通常这个指针被称为vptr。vptr的设定和重置都由每一个class的constructor, destructor和copy assignment运算符自动完成。

每一个class所关联的type_info object(用于支持runtime type identification)也经由virtual table被指出来,通常放在表格的第一个slot处。

 

4. 在虚拟继承的情况下,base class不管在继承串中被派生多少次,永远只会存在一个实体。

virtual base class的原始模型是在class object中为每一个有关联的virtual base class加上一个指针。

 

5. C++程序设计模型直接支持三种程序设计典范

(1)程序模型,如字符串处理

char boy[] = "Danny";
char* p_son;

p_son = new char[strlen(boy) + 1];
strcpy(p_son, boy);

if (!strcmp(p_son, boy))
    take_to_sidneyland(boy);

(2)抽象数据类型模型

String girl = "Anna";
String daughter;

// String::operator=();
daughter = girl;

// String::operator==();
if (girl == daughter)
    take_to_disneyland(girl);

(3)面向对象模型

 

6. C++以下列方法支持多态:

(1)经由一组隐含的转化操作。例如把一个derived clas指针转化为一个指向其public base type的指针:

shape* ps = new Circle();

(2)经由virtual function机制:

ps->rotate();

(3)经由dynamic_cast和typeid运算符:

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

 

7. 一个class object需要多少内存:

(1)其nonstatic data members的总和大小;

(2)加上任何由于alignment的需求而填补上去的空间;

(3)加上为了支持virtual而由内部产生的任何额外负担。

 

8. 指针的类型

指向不同类型之各指针间的差异,既不在其指针表示法不同,也不在其内容不同,而是在其所寻址出来的object类型不同。也就是说,“指针类型”会教导编译器如何解释某个特定地址中的内存内容及其大小。

一个指向地址1000的整数指针,在32位机器上,将涵盖地址空间1000~1003;那么一个指向地址1000而类型为void*的指针,将涵盖怎样的地址空间呢?这并不能知道。这就是一个类型为void*的指针只能够含有一个地址,而不能够通过它操作所指之object的缘故。

所以,转型(cast)其实是一种编译器指令。大部分情况下它并不能改变一个指针所含的真正地址,它只影响“被指出之内存的大小和其内容”的解释方式。

 

9. 加上多态

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;

b, pb, rb有怎样的内存布局?不管是pointer或是reference都只需要一个word的空间。Bear object需要24bytes,也就是ZooAnimal的16bytes加上Bear所带来的8bytes.

 

假设Bear object放在地址1000处,一个Bear指针和一个ZooAnimal指针有什么不同?

Bear b;
ZooAnimal* pz = &b;
Bear* pb = &b;

它们每个都指向Bear object的第一个byte,其间的差别是,pb所涵盖的地址包含整个Bear object,而pz所涵盖的地址只包含Bear object中的ZooAnimal subobject。

除了ZooAnimal subobject中出现的members,你不能够偶使用pz来直接处理Bear任何members。唯一例外是通过virtual机制。

pz->cell_block;   // 不合法:cell_block不是ZooAnimal的一个成员

((Bear*)pz)->cell_block;  // ok: 经过一个downcast操作就没有问题

pb->cell_block;  // ok: cell_block是Bear的一个成员

当我们写 pz->rotate();时,pz的类型将在编译时期决定一下两点:

(1)固定的可用接口,pz只能够调用ZooAnimal的public接口;

(2)该接口的access level。

在每一个执行点,pz所指的object类型可以决定rotate()所调用的实体,类型信息的封装并不是维护于pz之中,而是维护于link之中,此link存在于object的vptr和vptr所指之virtual table之间。

 

Bear b;
ZooAnimal za = b; // 这会引起切割(sliced)

za.rotate();  // 调用ZooAnimal::rotate()

为什么rotate()所调用的是ZooAnimal实体而不是Bear实体?此外,如果初始化函数将一个object内容完整拷贝到另一个object中去,为什么za的vptr不指向Bear的virtual table?

编译器在初始化和指定(assignment)操作之间做出了仲裁。编译器必须确保如果某个object含有一个或一个以上的vptrs,那些vptrs的内容不会被base class object初始化或改变。

za并不是一个Bear,它是一个ZooAnimal。多态所造成的一个以上的类型的潜在力量,并不能实际发挥在直接存取objects这件事情上。

 

一个pointer或reference之所以支持多态,是因为它们并不引发内存中任何”与类型有关的内存委托操作(type-dependent commitment)“;会受到改变的只是它们所指向的内存的”大小和内容解释方式“而已。

 

posted @ 2020-09-21 23:18  c++11  阅读(154)  评论(0编辑  收藏  举报