Andy Niu Help
1.0.0.0
|
变量 | |
理解实例和静态 | |
字段和方法__实例和静态 | |
抑制多态机制 | |
多态行为的条件 | |
详细描述
变量说明
多态行为的条件 |
1、第一是虚方法,第二表面类型和真实类型不一样,也就是使用引用或者指针。 2、考虑下面的代码,Say是虚方法 Dog d; Animal a = d; a.Say(); 3、a.Say(); 不具备多态行为。为什么? Animal a = d; 会发生对象切割,也就是d中的subobject会赋值给a。 但是特别注意,其中的vptr字段不会被复制,也就是vptr字段初始化就确定了,不会被修改。 那么,a的表面类型和真实类型都是Animal,a.Say()调用被编译器转化为(*(a.vptr[1]))(&a); 取出虚方法表的第2个槽位(注:第一个槽位[0]是type_info,表示类型信息RTTI,即运行时类型信息,typeid和dynamic_cast) 解引用,vptr指向Animal的虚方法表,然后传递this指针,作为方法第一个参数。 如果编译器足够聪明,这里可以抑制多态行为,a.Say()转化为a.Animal::Say(); 直接定位到Animal的Say方法。 如果Animal::Say()是inline方法,效率会更高。 4、考虑多态行为,就是OO(Object Oriented面向对象)。不考虑多态,只是ADT,对数据和操作封装,就是OB(Object Based)
字段和方法__实例和静态 |
1、考虑字段: a、从语义来讲,实例字段对每个对象有意义,针对每个对象,大家各不相同,互不影响。 静态字段对所有对象和类本身有意义,针对所有对象和类本身,大家都是一样的。 举例来说,对于Person类,Age是实例字段,MaxAge是静态字段。对于每个Person对象都有一个Age,对于所有的Person对象 和Person类,最大的年龄是一样的(比如150岁)。 b、从内存来讲,实例字段是每个对象有独属于自己的一块内存,互不影响。静态字段是所有对象和类本身共享一块内存。 2、考虑方法: a、从语义来讲,实例方法可以访问自己的字段和静态的字段,静态的字段是所有对象共享,当然可以访问, 但是不能访问其他对象的实例字段。特别注意:这里的不能访问其他对象的实例字段是指不能直接访问, 如果方法的参数列表中包含其他对象,当然可以访问,甚至可以访问同类对象的私有字段,但是不能访问不同类对象的私有字段。 (特别注意,private有两层含义:当前对象可以访问自己的private成员,也可以访问同类型对象的private成员, 必须是同类型,Dog对象可以访问其他Dog对象的private成员,但是不能访问Animal对象的private成员) 静态方法,不依赖具体的对象,即使没有对象,也可以调用静态方法。静态方法只能访问静态字段。 b、从内存来讲,无论实例方法还是静态方法,在内存中只有一个拷贝。 相对于普通方法,实例方法在形参表中在第一个位置增加一个形参,为this指针 相对于普通方法,静态方法可以认为是名称空间内的一个普通方法,类名就是名称空间,静态方法只能访问静态字段, 也就是只能访问同一个名称空间的字段。静态方法可以通过类名访问,也可以通过对象访问,通过对象访问,在内部没有使用this指针。 3、理解了静态方法,就知道为什么静态方法不能访问实例字段。静态方法不能是const,为什么? const方法解决什么问题,对于实例方法,this指针不能修改指向,可以修改this指针的内容,为了限制修改this指针的内容, 使用const方法,也就是让this指针指向const对象。但是静态方法压根就没有this指针,所以const修饰静态方法没有意义。 4、字段和方法编译之后,也就是数据段和代码段,数据段存在多个内存拷贝(有可能使用COW技术,减少内存拷贝的个数), 代码段只有一个内存拷贝,而且是只读的,可以加工同一类型的数据。同步控制加锁,表面上是对代码加锁,实际上是对数据加锁。
- 参见
抑制多态机制 |
1、我们知道,虚方法的调用,编译器会转化为虚方法表中对应的slot,然后调用。在有些情况下,我们不期望这种多态机制。 2、考虑,下面的情况 #include <stdio.h> class Animal { public: virtual void Say(int a); }; #include "Animal.h" void Animal::Say(int a) { printf("Animal::I am Say(int a)"); } #include "Animal.h" class Dog : public Animal { public: virtual void Say(int a); }; #include "Dog.h" void Dog::Say(int a) { Say(a); printf("Dog::I am Say(int a)"); } 测试代码, int main(int argc, char* argv[]) { Animal* pa = new Dog(); pa->Say(1); return 0; } 在子类中调用Say方法,由于多态机制,又调用子类的Say方法,变成死循环,堆栈溢出Stack Overflow 如何解决? 这个时候需要抑制多态机制,如下: void Dog::Say(int a) { Animal::Say(a); printf("Dog::I am Say(int a)"); } 3、考虑下面的情况,增加一个虚方法Play,在Dog的Play方法中,如下: void Dog::Play() { Say(5); printf("Dog::I am Play()"); } 这里的Say会进行多态机制,调用Dog的Say方法。我们知道,这种情况下,其实不需要多态机制,直接调用Dog的Say方法。如下: void Dog::Play() { Dog::Say(5); printf("Dog::I am Play()"); } 相对于上一种做法,这种做法减少了运行期对虚方法的决议过程(也就是转化为对应的slot),编译器确定了方法,效率高一些。
- 参见
理解实例和静态 |
1、成员分为字段和方法 2、考虑字段,实例字段占用对象内存,每个对象有专属于自己的一块内存,存储字段的值。 静态字段不占用对象内存,内存中只有一份,类和所有对象共享。 3、考虑方法,和字段不同,方法是代码段。 无论是实例方法还是静态方法,并不占用对象的内存。在内存中只有一份,而且是只读的。 方法是相对于类的,实例方法关联this指针,静态方法不关联this指针,只能访问静态字段。 4、考虑方法内的static字段,是针对所有对象的,所有对象都会调用这个方法。 这个static字段不光是当前对象进来只执行一次,是所有对象进来只执行一次。
Copyright (c) 2015~2016, Andy Niu @All rights reserved. By Andy Niu Edit.