C++
this指针
汇编中会将对象的基地址通过ecx传递,然后通过ecx+几来计算
练习
习题1
代码原型,分别分析结构体中的Add跟普通的Add 以及结构体中的Sub跟普通的Sub的区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
#include "stdafx.h" #include <iostream>
using namespace std;
struct Calc { int x; int y; int Add() { return this->x + this->y; } int Sub(int num1) { return this->x + this->y -num1; } }; int Add() { return 1+2; } int Sub(int num1) { return 1+2-num1; }
int main(int argc, char* argv[]) { Calc mycalc = Calc(); mycalc.Add(); mycalc.Sub(1); Add(); Sub(1); return 0; }
|
结构体中的Add函数传参
1 2
| 004012F4 8D 4D F8 lea ecx,[ebp-8] 004012F7 E8 0E FD FF FF call @ILT+5(Calc::Add) (0040100a)
|
结构体中的Sub传参
1 2 3
| 004012FC 6A 01 push 1 004012FE 8D 4D F8 lea ecx,[ebp-8] 00401301 E8 13 FD FF FF call @ILT+20(Calc::Sub) (00401019)
|
普通的Add函数
1
| 00401306 E8 FA FC FF FF call @ILT+0(Add) (00401005)
|
普通的Sub函数
1 2 3
| 0040130B 6A 01 push 1 0040130D E8 11 FD FF FF call @ILT+30(Sub) (00401023) 00401312 83 C4 04 add esp,4
|
结构体中的初始化以及结束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| # 初始化 00401340 55 push ebp 00401341 8B EC mov ebp,esp 00401343 83 EC 44 sub esp,44h 00401346 53 push ebx 00401347 56 push esi 00401348 57 push edi 00401349 51 push ecx 0040134A 8D 7D BC lea edi,[ebp-44h] 0040134D B9 11 00 00 00 mov ecx,11h 00401352 B8 CC CC CC CC mov eax,0CCCCCCCCh 00401357 F3 AB rep stos dword ptr [edi] 00401359 59 pop ecx
#结束
004013AB 5F pop edi 004013AC 5E pop esi 004013AD 5B pop ebx 004013AE 8B E5 mov esp,ebp 004013B0 5D pop ebp 004013B1 C2 04 00 ret 4
|
普通函数的初始化及结束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #初始化 004012A0 55 push ebp 004012A1 8B EC mov ebp,esp 004012A3 83 EC 40 sub esp,40h 004012A6 53 push ebx 004012A7 56 push esi 004012A8 57 push edi 004012A9 8D 7D C0 lea edi,[ebp-40h] 004012AC B9 10 00 00 00 mov ecx,10h 004012B1 B8 CC CC CC CC mov eax,0CCCCCCCCh 004012B6 F3 AB rep stos dword ptr [edi]
#结束 004012C0 5F pop edi 004012C1 5E pop esi 004012C2 5B pop ebx 004012C3 8B E5 mov esp,ebp 004012C5 5D pop ebp 004012C6 C3 ret
|
从这里可以看出不同点
- 结构体中函数传参会同时用寄存器以及栈,ecx存储的是基地址
- 普通函数栈平衡是外平栈,也就是_cdel 而结构体中的函数是内平栈,传进去一个参数,他就ret 4
- 普通函数初始化他不会保存ecx,而结构体中的函数他会保存ecx
- 普通函数无参数就是无参数,而结构体中的函数无参数却还有一个通过ecx传递的参数
习题2
观察空结构体跟非空结构体的大小
空结构体: 1
Calc结构体: 8
这里有点奇怪,空结构体居然有一个字节,而我定义的非空结构体却刚好是8个字节,两个int
习题3
以下函数能否执行,原因?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| struct Person { void Fn_1() { printf("Person:Fn_1()\n"); } void Fn_2() { printf("Person:Fn_2()%x\n"); } }; int main(int argc, char* argv[]) { Person* p = NULL; p->Fn_1(); p->Fn_2(); return 0; }
|
可以,因为Fn_1根本不在结构体里面,所以肯定是可以执行的,调试后发觉,他跳到指定函数处执行,简单来说就是数据通过ecx寻找,而代码通过call直接调用
下面这个呢
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| struct Person { int x ; void Fn_1() { printf("Person:Fn_1()\n"); } void Fn_2() { x = 10; printf("Person:Fn_2()%x\n"); } }; int main(int argc, char* argv[]) { Person* p = NULL; p->Fn_1(); p->Fn_2(); return 0; }
|
不可以,会出错,因为初始化了数据,他会从0处取数据,明显会报错
继承
- 继承
- 多层继承
- 多重继承
- 父类与子类指针
多重继承与多层继承区别
多层:
多重:
从图里很好理解,就是起始地址是不一样的,多层跟多重
练习
习题1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
|
#include "stdafx.h" #include <iostream>
using namespace std;
struct DateInfo { int year; int month; int day; DateInfo(int year, int month, int day) { this->year = year; this->month = month; this->day = day; } DateInfo() { this->year = 2015; this->month = 4; this->day = 2; } void SetDay(int day) { this->day = day; } void SetMonth(int month) { this->month = month; } void SetYear(int year) { this->year = year; } int GetDay() { return this->day; } int GetMonth() { return this->month; } int GetYear() { return this->year; }
}; int main(int argc, char* argv[]) { DateInfo myDate = DateInfo(2015,5,4); printf("%d", myDate.GetDay()); return 0; }
|
习题2
对比上一题跟这一题,我发觉一个问题,就是构造函数的使用,上一题的用法会导致多次初始化,第一次用构造函数初始化,然后在将其复制给main函数栈里的myDate,这样是重复劳动,不建议,建议直接使用第二种初始化方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
#include "stdafx.h" #include <iostream>
using namespace std;
struct TimeInfo { int hour; int minute; int second; void setTime(int hour, int minute, int second) { this->hour = hour; this->minute = minute; this->second = second; } TimeInfo(int hour,int minute, int second) { this->hour = hour; this->minute = minute; this->second = second; } TimeInfo() { this->hour = 0; this->minute = 0; this->second = 0; } int GetHour() { return this->hour; } int GetMinute() { return this->minute; } int GetSecond() { return this->second; }
}; int main(int argc, char* argv[]) { TimeInfo myTime(12,12,12); myTime.GetHour(); return 0; }
|
习题3
题目强人所难,所以我也强指针所难了,利用前面的知识很容易做到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
#include "stdafx.h" #include <iostream>
using namespace std;
struct DateInfo { int year; int month; int day; DateInfo(int year, int month, int day) { this->year = year; this->month = month; this->day = day; } DateInfo() { this->year = 2015; this->month = 4; this->day = 2; } void SetDay(int day) { this->day = day; } void SetMonth(int month) { this->month = month; } void SetYear(int year) { this->year = year; } int GetDay() { return this->day; } int GetMonth() { return this->month; } int GetYear() { return this->year; }
};
struct TimeInfo:DateInfo { int hour; int minute; int second; void setTime(int hour, int minute, int second) { this->hour = hour; this->minute = minute; this->second = second; } TimeInfo(int hour,int minute, int second) { this->hour = hour; this->minute = minute; this->second = second; } TimeInfo() { this->hour = 0; this->minute = 0; this->second = 0; } int GetHour() { return this->hour; } int GetMinute() { return this->minute; } int GetSecond() { return this->second; }
}; int main(int argc, char* argv[]) { TimeInfo myTime(12,13,14); myTime.day = 10; myTime.year = 2018; myTime.month = 11; TimeInfo* pTimeInfo = &myTime; DateInfo* pDateInfo = &myTime; int temp = pTimeInfo->hour; temp = pTimeInfo->minute; temp = *((int* )((int)pDateInfo + 12)); printf("%d\n", temp); myTime.GetHour(); return 0; }
|
习题4
作为c++基础还是有点的,很快搞定这些作业了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
|
#include "stdafx.h" #include <iostream>
using namespace std;
struct MyString { void *buf; int size; MyString() { this->size = 1024; buf = calloc(1024, 1); } MyString(int size) { this->size = size; buf = calloc(size, 1); } ~MyString() { free(buf); } void setString(char *str) { int length = strlen(str); if(length > this->size) { printf("Too long\n"); return; } memcpy(buf, str, strlen(str)); } void printString() { printf("%s\n", buf); } void appendString(char *str) { int length = strlen((char *)buf); int newLength = strlen(str); if(length + newLength > this->size) { printf("Too long\n"); return; } memcpy((void *)((int)buf+length), str, newLength); }
int Size() { return strlen((char *)buf); } }; int main(int argc, char* argv[]) { MyString Str(1024); Str.setString("123"); Str.printString(); printf("Size: %d\n", Str.Size()); Str.appendString("456"); Str.printString(); printf("Size: %d\n", Str.Size()); return 0; }
|
权限控制
C++是为了更安全的
继承的话,默认以私有继承
变量定义也是默认私有
class 权限最小化原则
1 2 3 4 5 6 7 8 9
| class A:class B { }
class A:public class B { }
|
不练习了,这个太简单了,就改class就行
虚函数表
直接用对象调用虚函数跟普通函数一样
如果用指针的话,虚函数变成间接调用E8->FF
有虚函数的,this指针指向的空间第一个为虚函数表
几个直接父类带虚函数就有几个虚表
虚函数图:
练习
习题1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
|
#include "stdafx.h" #include <iostream>
using namespace std;
struct Base
{ public: virtual void Function_1() { printf("Base:Function_1...\n"); } virtual void Function_2() { printf("Base:Function_2...\n"); } virtual void Function_3() { printf("Base:Function_3...\n"); } };
struct Sub:Base { public: virtual void Function_4() { printf("Sub:Function_4...\n"); } virtual void Function_5() { printf("Sub:Function_5...\n"); } virtual void Function_6() { printf("Sub:Function_6...\n"); } };
int main(int argc, char* argv[]) { Sub test; Sub* pSub = &test; typedef void(*pFunction)(void); pFunction pVir = NULL; int i = 0; for(; i<6; i++) { pVir = (pFunction)(((int*)(*(int*)&test))+i); printf("%x : ", pVir); pVir = (pFunction)(*(int *)pVir); printf("%x\n", pVir); pVir(); } return 0; }
|
打印结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 431028 : 401032 Base:Function_1... 43102c : 40100f Base:Function_2... 431030 : 401019 Base:Function_3... 431034 : 401023 Sub:Function_4... 431038 : 401005 Sub:Function_5... 43103c : 401014 Sub:Function_6... Press any key to continue
|
从这里发现的是Sub的虚函数表里同时存了base的虚函数表,也就是说他们是同一张表
习题2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
|
#include "stdafx.h" #include <iostream>
using namespace std;
struct Base { public: virtual void Function_1() { printf("Base:Function_1...\n"); } virtual void Function_2() { printf("Base:Function_2...\n"); } virtual void Function_3() { printf("Base:Function_3...\n"); } };
struct Sub:Base { public: virtual void Function_1() { printf("Sub:Function_1...\n"); } virtual void Function_2() { printf("Sub:Function_2...\n"); } virtual void Function_6() { printf("Sub:Function_6...\n"); } };
int main(int argc, char* argv[]) { Sub test; Sub* pSub = &test; typedef void(*pFunction)(void); pFunction pVir = NULL; int i = 0; for(; i<6; i++) { pVir = (pFunction)(((int*)(*(int*)&test))+i); printf("%x : ", pVir); pVir = (pFunction)(*(int *)pVir); printf("%x\n", pVir); if(pVir == 0) break; pVir(); } return 0; }
|
打印结果
1 2 3 4 5 6 7 8 9 10
| 431028 : 401023 Sub:Function_1... 43102c : 401014 Sub:Function_2... 431030 : 401019 Base:Function_3... 431034 : 40100f Sub:Function_6... 431038 : 0 Press any key to continue
|
发觉只有4个函数,1,2,3,6
重名的直接被覆盖掉了,虚表里,默认用的是Sub的,Sub对象默认用的Sub还是Sub指针默认的是Sub呢,测试下
经过测试Base指针也是默认的Sub,那Base对象呢,Base只有Base的函数,用Sub指针还是Base的调用,一样的
多态
动态绑定运行起来才绑定的,需要实例化,间接call
前期绑定,编译完就有准确的地址的,直接call
virtual后的多态
练习
习题1
思考题:为什么析构函数建议写成virtul的?
写成virtual可以释放子类空间
习题2
写一个例子程序,能体现多态的作用.
狗和猫叫法不一样,这里运用了个抽象类,因为Animal是抽象出来的,他们有共同的方法,叫
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
#include "stdafx.h"
class Animal { public: virtual void cry() = 0; };
class Dog:public Animal { public: virtual void cry() { printf("dog is wang\n"); } };
class Cat:public Animal { public: virtual void cry() { printf("Cat is Miao\n"); } };
int main(int argc, char* argv[]) { Dog dog; Cat cat; Animal* arr[] = {&dog,&cat}; for(int i=0; i<2; i++) { arr[i]->cry(); } return 0; }
|
模板
练习
习题1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
#include "stdafx.h"
template<class T> void Swap(T* num1, T* num2) { T temp = *num1; *num1 = *num2; *num2 = temp; }
int main(int argc, char* argv[]) { int int1=1,int2=2; short short1=1,short2=2; char char1=1,char2=2; Swap(&int1,&int2); Swap(&short1,&short2); Swap(&char1,&char2); return 0; }
|
发觉编译了3份不同的代码,一份用32位寄存器,一份用16位寄存器,一份用8位寄存器
习题2
实际考的是运算符重载,懒得在搞那个冒泡了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
|
#include "stdafx.h"
struct DateInfo { int year; int month; int day; bool operator <(DateInfo &x)const { return this->year < x.year; } DateInfo() { this->year = 2018; this->month = 10; this->day = 1; } ~DateInfo() { } };
template<class T> class Cmp { private: T num1; T num2; public: Cmp(T num1, T num2) { this->num1 = num1; this->num2 = num2; } bool getResult() { return this->num1 < this->num2; } };
int main(int argc, char* argv[]) {
DateInfo a,b; b.year = 2019; Cmp<DateInfo> test(a,b); test.getResult(); return 0; }
|
习题3
后面的
不学了,全都学过,都忘得差不多了
本文作者:NoOne
本文地址: https://noonegroup.xyz/posts/97ad516e/
版权声明:转载请注明出处!