我们可以在什么地方创建对象?
-
全局变量区
Person p;
-
栈
void Max() { Person p; }
-
堆
new
和delete
//在堆中创建对象: Person* p = new Person(); //释放对象占用的内存 delete p;
在堆中创建对象: new
delete
在C语言中我们使用malloc
申请堆空间
使用完毕后使用free
释放空间
C++:
class Person { private: int x; int y; public: Person() { printf("Person()执行了! /n"); } Person(int x,int y) { printf("Person(int x,int y)执行了! /n"); this->x=x; this->y=y; } ~Person() { printf("~Person() 执行了! /n"); } }; Person* p =new Person(); Person* p1=new Person(1,2); delete p; delete p1; //反汇编 33: Person* p =new Person(); 00401070 push 8 00401072 call operator new (004013c0) 00401077 add esp,4 0040107A mov dword ptr [ebp-1Ch],eax 0040107D mov dword ptr [ebp-4],0 00401084 cmp dword ptr [ebp-1Ch],0 00401088 je main+57h (00401097) 0040108A mov ecx,dword ptr [ebp-1Ch] 0040108D call @ILT+20(Person
Person) (00401005) 004010D7 mov dword ptr [ebp-3Ch],eax 004010DA jmp main+0A3h (004010e3) 004010DC mov dword ptr [ebp-3Ch],0 004010E3 mov edx,dword ptr [ebp-3Ch] 004010E6 mov dword ptr [ebp-20h],edx 004010E9 mov dword ptr [ebp-4],0FFFFFFFFh 004010F0 mov eax,dword ptr [ebp-20h] 004010F3 mov dword ptr [ebp-14h],eax 35: delete p; 004010F6 mov ecx,dword ptr [ebp-10h] 004010F9 mov dword ptr [ebp-2Ch],ecx 004010FC mov edx,dword ptr [ebp-2Ch] 004010FF mov dword ptr [ebp-28h],edx 00401102 cmp dword ptr [ebp-28h],0 00401106 je main+0D7h (00401117) 00401108 push 1 0040110A mov ecx,dword ptr [ebp-28h] 0040110D call @ILT+15(Person
`scalar deleting destructor') (00401014) 0040113A mov dword ptr [ebp-44h],eax 0040113D jmp main+106h (00401146) 0040113F mov dword ptr [ebp-44h],0
new
关键字到底做了什么?
new
delete
的本质:
-
分析
malloc
函数的执行流程char* p = (char*)malloc(123);
//逐层调用 00401072 call malloc (004013f0) 00401404 call _nh_malloc_dbg (00401470) 00401484 call _heap_alloc_dbg (004014e0) // 00401559 call dword ptr [__pfnAllocHook (00427cb0)] 0040167D call _heap_alloc_base (00404380) 0040443C call dword ptr [__imp__HeapAlloc@12 (0042d190)]
-
分析
new
的执行流程Person p = new Person();
36: Person* p =new Person(); 0040107D push 8 0040107F call operator new (004013d0) //new //逐层调用 004013DA call _nh_malloc (00401450) 00401461 call _nh_malloc_dbg (00401470) 00401484 call _heap_alloc_dbg (004014e0) // 00401559 call dword ptr [__pfnAllocHook (00427cb0)] 0040167D call _heap_alloc_base (00404380) 0040443C call dword ptr [__imp__HeapAlloc@12 (0042d190)] // 00401084 add esp,4 00401087 mov dword ptr [ebp-20h],eax 0040108A mov dword ptr [ebp-4],0 00401091 cmp dword ptr [ebp-20h],0 00401095 je main+64h (004010a4) 00401097 mov ecx,dword ptr [ebp-20h] 0040109A call @ILT+20(Person::Person) (00401019) 0040109F mov dword ptr [ebp-3Ch],eax 004010A2 jmp main+6Bh (004010ab) 004010A4 mov dword ptr [ebp-3Ch],0 004010AB mov eax,dword ptr [ebp-3Ch] 004010AE mov dword ptr [ebp-1Ch],eax 004010B1 mov dword ptr [ebp-4],0FFFFFFFFh 004010B8 mov ecx,dword ptr [ebp-1Ch] 004010BB mov dword ptr [ebp-14h],ecx
总结:
new = malloc + 构造函数
动动手:
分析 delete的执行流程 和free对比
数组堆空间申请 new[]/delete[]
分别用C和C++方式在堆中申请Int数组
int* p = (int*)malloc(sizeof(int)*10); free(p); int* p = new int[10]; delete[]p;
分别用C和C++方式在堆栈中申请Class类型数组
int* p = (Person*)malloc(sizeof(Person)*10); free(p); Person* p = new Person[10]; delete[] p;
delete和delete[]有什么区别?
如果对象数组 只使用delete p
(一个delete)我们发现 只有一个析构函数执行
要把10个对象所占用空间全部释放,并且每个都要执行析构函数的话 必须使用 delete[] p
引用类型
引用类型是C++中的特性
引用类型就是变量的"别名"
-
基本类型
int x= 1; int& p =x; //起个别名叫p p = 2; //p就是x printf("%d /n",x);
-
类
Person p; Person& px =p //起个别名叫px px.x = 10; //px 就是p printf("%d /n",p.x);
-
指针
int****** i = (int******)1; int******& r =i; //起个别名叫r r = (int******)2; //r就是I printf("%d /n",r);
-
数组
int arr[] = {1,2,3}; int (&p)[3] = arr; //起个别名叫 p p[0] =4; //p 就是arr printf("%d /n",arr[0]);
引用类型就是给变量起的别名
引用类型在定义时必须赋初始值
引用类型的本质
int x= 1; int& p =x; //起个别名叫p p = 2; //p就是x printf("%d /n",x); 43: int x= 1; 00401058 mov dword ptr [ebp-4],1 44: int& p =x; //起个别名叫p 0040105F lea eax,[ebp-4] 00401062 mov dword ptr [ebp-8],eax 45: p = 2; //p就是x 00401065 mov ecx,dword ptr [ebp-8] 00401068 mov dword ptr [ecx],2 46: printf("%d /n",x); 0040106E mov edx,dword ptr [ebp-4] 00401071 push edx 00401072 push offset string "%d /n" (0042501c) 00401077 call printf (00403880) 0040107C add esp,8 //测试 吧上述代码中的引用类型改为指针。 int x= 1; int* p =&x; //起个别名叫p *p = 2; //p就是x printf("%d /n",x); //结果发现生成的反汇编结果一模一样 //这里我们暂时得出结论,,引用类型就是指针。或者说在底层实现上 就是指针
引用类型和指针的区别
int x = 1; //必须初始化 int* p = &x; int& ref = x; //运算 p++; ref++; //赋值 p = (int*)1; ref = 100; //反汇编 49: int x = 1; 00401028 mov dword ptr [ebp-4],1 50: //必须初始化 51: int* p = &x; 0040102F lea eax,[ebp-4] 00401032 mov dword ptr [ebp-8],eax 52: int& ref = x; 00401035 lea ecx,[ebp-4] 00401038 mov dword ptr [ebp-0Ch],ecx 53: 54: //运算 55: p++; 0040103B mov edx,dword ptr [ebp-8] 0040103E add edx,4 00401041 mov dword ptr [ebp-8],edx 56: ref++; 00401044 mov eax,dword ptr [ebp-0Ch] 00401047 mov ecx,dword ptr [eax] 00401049 add ecx,1 0040104C mov edx,dword ptr [ebp-0Ch] 0040104F mov dword ptr [edx],ecx 57: 58: //赋值 59: p = (int*)1; 00401051 mov dword ptr [ebp-8],1 60: ref = 100; 00401058 mov eax,dword ptr [ebp-0Ch] 0040105B mov dword ptr [eax],64h class Base { public: int x; int y; Base(int x,int y) { this->x = x; this->y =y; } } Base b(1,2); //必须初始化 Base* p = &b; Base& ref = b; //运算 p++; //ref++; //赋值 p = (Base*)1; //ref = 100;
总结:
- 被引用必须赋初始值,且只能指向一个变量,“从一而终”。
- 对引用复制,是对其指向的变量赋值,而不是修改引用本身的值
- 对引用做运算,就是对其指向的变量做运算,而不是对引用本身做运算。
- 引用类型就是一个“弱化了的指针”。
个人见解,其实我觉得引用类型就像是一个
*p
也就是取值了的指针~~~
引用在函数参数传递中的作用(基本类型)
void Plus(int& i) { i++; return; } int main(int argc,char* argv[]) { int i = 10; Plus(i); printf("%d /n",i); return 0; } 102: i++; 00401038 mov eax,dword ptr [ebp+8] 0040103B mov ecx,dword ptr [eax] 0040103D add ecx,1 00401040 mov edx,dword ptr [ebp+8] 00401043 mov dword ptr [edx],ecx //传入的虽然是指针,但是对参数操作就是对对应参数操作,而不是指针本身的操作 108: int i = 10; 00401078 mov dword ptr [ebp-4],0Ah 109: Plus(i); 0040107F lea eax,[ebp-4] 00401082 push eax 00401083 call @ILT+0(Plus) (00401005) 00401088 add esp,4 110: printf("%d /n",i); 0040108B mov ecx,dword ptr [ebp-4] 0040108E push ecx 0040108F push offset string "%d /n" (0042201c) 00401094 call printf (004010d0) 00401099 add esp,8 //当函数的参数为引用类型的时候 //传入的参数为参数的地址
引用在函数参数传递中的作用(构造类型)
struct Base { int x; int y; Base(int x,int y) { this->x = x; this->y = y; } } void PrintByRef(Base& refb,Base* pb) { //通过指针读取 printf("%d,%d /n",pb->x,pb->y); //通过引用获取 printf("%d %d /n",refb.x,refb.y); //指针可以重新赋值,可以做运算 //引用不可以 //refb = (Base&)1; //refb++; } //反汇编 121: //通过指针读取 122: printf("%d,%d /n",pb->x,pb->y); 00401088 mov eax,dword ptr [ebp+0Ch] 0040108B mov ecx,dword ptr [eax+4] 0040108E push ecx 0040108F mov edx,dword ptr [ebp+0Ch] 00401092 mov eax,dword ptr [edx] 00401094 push eax 00401095 push offset string "%d,%d /n" (00422028) 0040109A call printf (004011b0) 0040109F add esp,0Ch 123: //通过引用获取 124: printf("%d %d /n",refb.x,refb.y); 004010A2 mov ecx,dword ptr [ebp+8] 004010A5 mov edx,dword ptr [ecx+4] 004010A8 push edx 004010A9 mov eax,dword ptr [ebp+8] 004010AC mov ecx,dword ptr [eax] 004010AE push ecx 004010AF push offset string "%d %d /n" (0042201c) 004010B4 call printf (004011b0) 004010B9 add esp,0Ch 125: //指针可以重新赋值,可以做运算 126: //引用不可以 127: //refb = (Base&)1; 128: //refb++; 133: Base b(1,2); 00401108 push 2 0040110A push 1 0040110C lea ecx,[ebp-8] 0040110F call @ILT+5(Base::Base) (0040100a) 134: Base* p =&b; 00401114 lea eax,[ebp-8] 00401117 mov dword ptr [ebp-0Ch],eax 135: Base& ref=b; 0040111A lea ecx,[ebp-8] 0040111D mov dword ptr [ebp-10h],ecx 136: 137: PrintByRef(ref,p); 00401120 mov edx,dword ptr [ebp-0Ch] 00401123 push edx 00401124 mov eax,dword ptr [ebp-10h] 00401127 push eax 00401128 call @ILT+15(PrintByRef) (00401014) 0040112D add esp,8
给狗起个人的名字?
引用是变量的别名,如:
int x = 10; int& r = x; //int类型的别名就应该是 Int& Base b(1,2); Base&r = b; //Base类型的别名 就应该是 Base& Base& r = (Base&)x; //虽然可以变异 但是意义不大
常引用
class Base { public: int x; }; void Print(const Base& ref)//常量参数 { //ref = 100; //不论是不是const不能修改 //ref.x = 200; //不是const能修改指向的内容 是const 不能修改 printf("%d /n",ref.x); } int main(int argc,char* argv[]) { Base b; b.x = 100; Print(b); return 0; }
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论