内存分配有三种方式
1 静态分配,或者说静态存储区域分配。
就是说内存在程序编译的时候就已经分配好了,这款内存在程序整个运行期间都存在,如全局变量,static变量。
2 自动分配,或者说是在栈上分配。
比如在执行函数时,函数内局部变量的存储单元就是在栈上创建的,函数执行结束时,这些存储单元自动被释放。
3 堆上分配,或者说是动态内存分配。
那些用malloc和new申请来的内存,就是动态的,在堆上分配的。这些堆上分配出来的空间,由申请使用他们的那些人负责释放。
补充:堆开辟内存空间时,是向着内存空间增加的方向的。而栈则是向下的,就是向着内存地址减少的方向。
重载,覆盖,隐藏
覆盖就是派生类函数覆盖基类的函数。满足覆盖的条件:派生类和基类的函数名相同,参数也相同,基类的函数必须有virtual。
隐藏是指派生类的函数屏蔽了与其同名的基类函数。满足隐藏的条件:派生类和基类的函数名相同,参数不同,不管有无virtual,都算隐藏。或者,派生类和基类函数名相同,参数相同,没有virtual,也算隐藏(有了virtual算什么,往上看)
重载就是有几个函数名相同,但是参数不同的函数。重载发生在相同范围,一定是同一个类。
运算符重载 virtual关键字可有可无 operator不影响优先级
不是所有的运算符都能被重载,以下运算符就不行:
:: 作用域解析运算符
?: 条件运算符
. 直接成员运算符
.* 接解除类成员指针引用运算符
sizeof
动态绑定 早期绑定
通过静态解析的基类指针来调用函数,都会调用基类函数。函数通过指针的静态,仅取决于指针的类型,而不取决与对象。
对于一个基类指针或者引用,定义时的类型称为静态类型,实际指向对象的类型称为动态类型。
在任意时候指针pbox都可以指向包含以box为其基类的 派生类的地址,这里的pbox 声明为box* 类型。
多态是通过基类指针、基类引用调用函数实现多态特性。
我们用virtual关键字来避开静态绑定。
动态绑定的条件,被调用函数必须是虚函数,必须通过基指针或者基类引用来调用。如果通过this指针间接调用虚函数也是动态调用。
动态调用必须是针对虚函数的调用,不能是针对一般成员函数的调用,并且就算把成员函数声明为虚函数,还要用基类指针或者基类引用直接调用,或者通过this指针间接调用这两个条件才能实现动态调用。
对于一般成员函数(非虚函数)必定是静态调用。
如果基类中把一个函数定义成虚函数,那么所有派生类中都有相同的返回值类型,函数签名,包括const也被认为是虚函数。反之,如果在派生类中,函数的返回值,签名,只有有一个和基类不同,在调用的时候就不会认为是基类的虚函数,此时执行静态调用。
如果虚函数在基类声明中带有默认参数值,那么在动态调用中,总是使用基类声明中的默认值。
有几个情况是静态调用函数:
设pbox是基类的指针,rbox是基类引用形参,volume()是虚函数。
使用类名和作用域运算符来强调调用指定类的虚函数,如:
pbox->Box::volume()
rbox.Box::volume()
使用对象名和成员运算符来调用虚函数是总是静态调用的
hardcase.volume(); 显式调用运算的hardcase的volume()
另外,用基类指针来调用一般成员函数(非虚函数)总是静态调用
基类指针和派生类指针 之间的 转换
派生类指针可以直接转化为基类指针:
但是倒过来,基类指针不能隐式地转换成派生类指针,如果一定要,那么就是用强制类型转换,并且这种方式不保证一定成功。
关于派生类和基类直接的转换代码
#include<iostream> using namespace std; class box { public: virtual void yoyo() { cout<<"Box yoyo"<<endl; } }; class carton:public box { public: void yoyo() { cout<<"carton yoyo"<<endl; } }; class cerealpack:public carton { public: void yoyo() { cout<<"cerealpack yoyo"<<endl; } }; int main() { carton ca; carton *pca = &ca; cerealpack ce; cerealpack *pce = &ce; box bo; box* pbo = &bo; pce->yoyo(); pce = static_cast<cerealpack*>(pbo); pce->yoyo();
return 0; }