c++对象模型详解之一——《深度探索C++对象模型》

单一继承且无虚函数的对象模型

  1. class A  
  2. {  
  3. public:  
  4.     int foo( )  {    return  val ;    }  
  5.     static int staFun( )  {     return  staVal ;  }  
  6.     static int staVal ;  
  7. private:  
  8.     int val ;  
  9.     char bit1 ;  
  10. } ;  
  11.   
  12. class B : public A  
  13. {  
  14. public:  
  15.     char foo( )  {    return  bit2;    }  
  16. private:  
  17.     char bit2 ;  
  18. };  
class A
{
public:
    int foo( )  {    return  val ;    }
    static int staFun( )  {     return  staVal ;  }
    static int staVal ;
private:
    int val ;
    char bit1 ;
} ;

class B : public A
{
public:
    char foo( )  {    return  bit2;    }
private:
    char bit2 ;
};

 

内存布局:

静态数据成员:

它被编译器提出于class之外,并被视为一个global变量(但只在class生命范围之内可见)

每个静态数据成员只有一个实体,存放在程序的数据段之中,

经由’.’运算符,对一个静态数据成员进行存取操作,只是语法上的一种便宜行事而已。静态数据成员其实并不在class object之中,因此存取它并不需要通过class object。

 

虽然你可以不靠class object 来存取一个静态成员,但其存取函数却得绑定于一个class object之上。(若静态成员的访问控制为protected或private,则必须通过存取函数来访问)

 

【注意:】类的静态数据成员,必须要在全局下进行定义,然后才能使用。

无论它的访问控制是什么,必须在main函数之前,对静态数据成员定义。

例如:int A::staVal = 0 ;

否则,编译器会报错:未定义的引用。

因为编译器把涉及静态数据成员的使用,都转换为直接使用,而静态成员在class中声明,外部无法看见。故需要在外部进行定义,使其后的代码可见。

 

成员函数的处理:

C++的设计准则之一:非静态成员函数至少和一般的外部函数有相同的存储效率。

C++编译器会把成员函数内化为一般的函数:

①改写函数原型,安插一个额外的参数this指针。用以提供一个存取管道,使类对象得以调用该函数。

int A::foo (A* const this)

若该成员函数是const,则变成:

int A::foo (const A* const this)

②对函数体中 类对象的非静态数据成员的存取操作,改为经由this指针来存取。

int A::foo (A* const this)

{            return  this->val ;             }

③将成员函数重新写成一个外部函数,对函数名称进行处理,使它在程序中成为独一无二的。

 

以上的转换操作结束之后,每个调用操作都要转换。

  1. A objA ;  
  2. A * ptr = & objA ;  
  3.   
  4. ptr->foo( ) ;   
  5. objA.foo() ;  
  6. //分别被转换为:  
  7. foo_intA( ptr ) ;  
  8. foo_intA( & objA ) ;   
A objA ;
A * ptr = & objA ;

ptr->foo( ) ; 
objA.foo() ;
//分别被转换为:
foo_intA( ptr ) ;
foo_intA( & objA ) ; 

 

静态成员函数:

静态成员函数的主要特征是它没有this指针。

故其:

①它不能够直接存取其class中的非静态成员

②它不能够直接被声明为const、virtual

③它不需要经由class object才被调用——虽然大部分时候它是这样被调用的。

 

对静态成员函数的调用

  1. objA.staFun( ) ;  
  2. ptr->staFun( ) ;  
  3. //会转换为:  
  4. staFun_staticintvoid( ) ;  
  5. staFun_staticintvoid( ) ;  
objA.staFun( ) ;
ptr->staFun( ) ;
//会转换为:
staFun_staticintvoid( ) ;
staFun_staticintvoid( ) ;

静态成员函数由于缺乏this指针,因此差不多等同于外部函数。只是它作用于类的静态数据成员上。

 

 

单一继承且有虚函数的对象模型

例:

  1. class A  
  2. {  
  3. public:  
  4.     virtual int foo( )  {    return  val ;    }  
  5.     virtual int funA( ) {}  
  6. private:  
  7.     int val ;  
  8.     char bit1 ;  
  9. } ;  
  10.   
  11. class B : public A  
  12. {  
  13. public:  
  14.     virtual int foo( )  {    return  bit2;    }  
  15.     virtual int funB( ) {}  
  16. private:  
  17.     char bit2 ;  
  18. };  
class A
{
public:
    virtual int foo( )  {    return  val ;    }
    virtual int funA( ) {}
private:
    int val ;
    char bit1 ;
} ;

class B : public A
{
public:
    virtual int foo( )  {    return  bit2;    }
    virtual int funB( ) {}
private:
    char bit2 ;
};

内存布局:

一个class只要有一个虚函数,那么每一个class object被安插上一个由编译器内部产生的指针,指向该表格(virtual table)。

virtual table 的第一项是表示class的类型。

因为,基类指针的特殊性,它可以指向基类对象,也可以指向派生类对象。故:ptr->foo( ) ;这种调用,我们需要知道ptr所指对象的真实类型。(就算不知道ptr所指对象的类型,也可以正确调用fun函数,但是由于fun函数有编译器插入的this指针,this指针要与ptr指向的对象地址正确对应,以正确访问对象中的成员变量,但ptr中却没有这样的信息)

virtual table 之后的表格是class中的每个虚函数地址。

 

一个class只会有一个virtual table。派生类的virtual table是在基类的virtual table上增加,修改的。

派生类中的虚函数会改写(overriding)与基类中同名且参数相同的虚函数,把virtual table表中相应的基类虚函数地址改写为相应派生类虚函数的地址。

 

以上工作都是由编译器完成的。执行期要做的就是在特定的virtual table表项中激活相应的虚函数,然后根据virtual table首项的类型信息,正确执行此虚函数。

 

例如:ptr->foo( ) ;

一般而言,我并不知道ptr所指对象的真正类型。然而我知道。经由ptr可以存取到该对象的virtual table。

虽然我不知道哪一个foo( )实体会被调用,但我知道每一个foo( )函数的地址都放在虚表的第二项。

故:根据以上信息,编译器可以将该调用转化为:

( *ptr->vptr[1] )( ptr ) ;

 

 

【注意:】基类指针虽然可以指向派生类,但是它实际上指向的是派生类中的基类部分。(这就不违反指针的特性了,指针类型与其指向范围是一致的)故基类指针不能访问派生类的成员。(但基类指针可以通过访问派生类的虚函数,间接操作派生类成员)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值