课堂大纲
1.对象模型(Object Model):关于vptr和vtbl
2.对象模型(Object Model):关于this
3.对象模型(Object Model):关于Dynamic Binding
4.谈谈const
5.关于new,delete
6.重载 ::operator new,::operator delete
::operator new[],::operator delete[]
7.示例
8.重载new().delete()
9.basic_string使用new(extra)扩充申请量
1、关于vptr和vtbl
在C++中,每个对象实例有不同的vtbl(虚函数表)用图的方式表达如下图;
我对其理解是(最好配合在课件上的图),当类中有虚函数的时候,编译器会为类插入一个我们看不见的数据并建立一个表。这个表就是虚函数表(vtbl),那个我们看不见的数据就是指向虚函数表的指针——虚表指针(vptr)。虚函数表就是为了保存类中的虚函数的地址。我们可以把虚函数表理解成一个数组,数组中的每个元素存放的就是类中虚函数的地址。当调用虚函数的时候,程序不是像普通函数那样直接跳到函数的代码处,而是先取出vptr即得到虚函数表的地址,根据这个来到虚函数表里,从这个表里取出该函数的地址,最后调用该函数。所以只要不同类的vptr不同,他对应的vtbl就不同,不同的vtbl装着对应类的虚函数地址,这样虚函数就可以完成它的任务了。
2.对象模型(Object Model):关于this
关于this指针,侯老师化的那张图给我们展示了this指针的作用,想起以前在知乎上看过的一个例子形容指针,class类好比一间屋子,类成员比作屋里的东西,而this指针就好比开房子的钥匙,通过他就可以进到屋子,可以拿屋内的各种东西了。
用专业术语来说,this是指向实例化对象本身时候的一个指针,里面存储的是对象本身的地址,通过该地址可以访问内部的成员函数和成员变量。
demo如下:
class Point
{
int x, y;
public:
Point(int a, int b) { x=a; y=b;}
Void MovePoint( int a, int b){ x+=a; y+=b;}
Void print(){ cout<<"x="< };
void main( )
{
Point point1( 10,10);
point1.MovePoint(2,2);
point1.print( );
}
a.对象point1调用MovePoint(2,2)的时候,即将point1对象的地址传递给了this指针
b.编译器编译后的原型应该是void MovePoint(Point *this,int a, int b)
c.在函数体中可以写成{this->x += a; this->y+= b;}
d.也等价为point1.x += a;point1.y += b。(指针变量通过->访问成员变量(函数),对象通过.)
3、关于Dynamic Binding
关于C++对象模型中的动态绑定,在看了一些相关资料可认为编译器用静态分析的方法加上虚拟函数的设计实现在程序运行时动态智能执行正确虚拟函数的技术。
在很多资料上都是通过下列demo来理解的
#include <iostream>
using namespace std;
class A
{
public:
void fA() { cout << "A::fA()" << endl; }
virtual void vfA() { cout << "A::vfA()" << endl; }
void emptyB() { cout << "A::emptyB()" << endl; }
void vfAonly() { cout << "A::vfAonly()" << endl; }
};
class B : public A
{
public:
void fB() { cout << "B::fB()" << endl; }
virtual void vfA() { cout << "B::vfA()" << endl; }
virtual void vfB() { cout << "B::vfB()" << endl; }
void emptyA() { cout << "B::emptyA()" << endl; }
virtual void vfAonly() { cout << "B::vfAonly()" << endl; }
};
int main()
{
A* p = new B;
B& r = *(B*)p;
p->fA(); // 1
//p->fB(); // 2
p->vfA(); // 3
//p->vfB(); // 4
//p->emptyA(); // 5
p->emptyB(); // 6
p->vfAonly(); // 7
cout << endl;
r.fA(); // 8
r.fB(); // 9
r.vfA(); // 10
r.vfB(); // 11
r.emptyA(); // 12
r.emptyB(); // 13
r.vfAonly(); // 14
delete p;
return 0;
}
输出结果为
A::fA()
B::vfA()
A::emptyB()
A::vfAonly()
A::fA()
B::fB()
B::vfA()
B::vfB()
B::emptyA()
A::emptyB()
B::vfAonly()
4、const
这个在C语言中就是常用的,例如申明常量成员变量函数,想重点总结了一下const在函数中的使用,这部分我以前经常出错。
1)const修饰函数参数
a.传递过来的参数在函数内不可以改变(无意义,因为Var本身就是形参)
void function(const int Var);
b.参数指针所指内容为常量不可变
void function(const char* Var);
c.参数指针本身为常量不可变(也无意义,因为char* Var也是形参)
void function(char* const Var);
d.参数为引用,为了增加效率同时防止修改。修饰引用参数时:
void function(const Class& Var); //引用参数在函数内不可以改变
void function(const TYPE& Var); //引用参数在函数内为常量不可变
这样的一个const引用传递和最普通的函数按值传递的效果是一模一样的,他禁止对引用的对象的一切修改,唯一不同的是按值传递会先建立一个类对象的副本,然后传递过去,而它直接传递地址,所以这种传递比按值传递更有效.另外只有引用的const传递可以传递一个临时对象,因为临时对象都是const属性, 且是不可见的,他短时间存在一个局部域中,所以不能使用指针,只有引用的const传递能够捕捉到这个家伙.
(2)const 修饰函数返回值
const修饰函数返回值其实用的并不是很多,它的含义和const修饰普通变量以及指针的含义基本相同。
a.const int fun1() //这个其实无意义,因为参数返回本身就是赋值。
b. const int * fun2() //调用时 constint *pValue = fun2();
//我们可以把fun2()看作成一个变量,即指针内容不可变。
c.int* const fun3() //调用时 int *const pValue = fun2();
//我们可以把fun2()看作成一个变量,即指针本身不可变。
一般情况下,函数的返回值为某个对象时,如果将其声明为const时,多用于操作符的重载。通常,不建议用const修饰函数的返回值类型为某个对象或对某个对象引用的情况。
5.关于new,delete
这个也是常用的,没啥多说的,新申明的数组需要使用内存时,需要new一段size大小的空间,当使用结束时delete掉,即释放内存,他俩是配合着使用的,不能忘写其中一个。
6.重载 ::operator new,::operator delete
::operator new[],::operator delete[]
关于重载,是C++的重点知识,也是本周课程的难点,
对于operatornew我的理解为只分配所要求的空间,不调用相关对象的构造函数。
而对于operatordelete可以释放任意大小的内存。用其的在大用处就是效率。对于这个知识点,我还需要在学习下,有一个博客是专门解释这个知识点的,分享一下http://blog.csdn.net/zhangxiao93/article/details/50768025
7.示例
侯老师的示例都是比较经典的,需要多次温习。
8.重载new().delete()
new和delete是运算符,重载new和delete是可能的。这样做的原因是,有时希望使用某种特殊的动态内存分配方法。例如,可能有些分配子程序,他们的堆已耗尽,自动开始把一个磁盘文件当虚存储使用,或者用户希望控制某一片存储空间的分配等。
重载new和delete的格式如下:
void *operator new (size_t size)
{
.......//完成分配工作
return pointer_to_memory;
}
void operator delete(void *p)
{
......//释放由p指向的存储空间
}