1、直接使用结构体做为参数
struct sclass { int a; int b; }; int Plus(int a,int b) { return a+b; } int main(int argc, char* argv[]) { sclass s; s.a = 10; s.b = 20; int x = Plus(s.a,s.b); printf("%d ",x); return 0; }
总结:
将结构体直接做为参数传递的时候,传递的是整个结构体.
反汇编:
sub esp,10h
mov eax,esp
mov ecx,dword ptr [ebp-10h]
mov dword ptr [eax],ecx
mov edx,dword ptr [ebp-0Ch]
mov dword ptr [eax+4],edx
mov ecx,dword ptr [ebp-8]
mov dword ptr [eax+8],ecx
mov edx,dword ptr [ebp-4]
mov dword ptr [eax+0Ch],edx
call @ILT+35(Plus) (00401028)
add esp,10h
2、传递结构体指针
struct sclass { int a; int b; int c; int d; }; int Plus(sclass* sc) { return sc->a+sc->b; } int main(int argc, char* argv[]) { sclass s; s.a = 10; s.b = 20; int x = Plus(&s); printf("%d ",x); return 0; }
反汇编:
mov dword ptr [ebp-10h],0Ah
mov dword ptr [ebp-0Ch],14h
lea eax,[ebp-10h]
push eax
call @ILT+40(Plus) (0040102d)
add esp,4
3、函数可以放在结构体里面,也可以放在结构体外面
struct sclass { int a; int b; int c; int d; int Plus(sclass* sc) { return sc->a+sc->b; } };
探测:通过sizeof来探测将函数放在里面与外面结构体的大小有什么变化?
4、函数放在里面如何使用的问题 观察反汇编:
struct sclass { int a; int b; int c; int d; int Plus(sclass* sc) { return sc->a+sc->b; } }; int main(int argc, char* argv[]) { sclass s; s.a = 10; s.b = 20; int x = s.Plus(&s); printf("%d %x ",x,sizeof(s)); return 0; }
注意观察反汇编:
1、参数传递
2、堆栈平衡
有没有发现什么问题?
5、封装、类、成员函数
struct sclass { int a; int b; int c; int d; int Plus() { return a+b; } }; int main(int argc, char* argv[]) { sclass s; s.a = 10; s.b = 20; int x = s.Plus(); printf("%d %x ",x,sizeof(s)); return 0; }
封装:
1、将函数定义到结构体内部,就是封装.
2、编译器会自动传递结构体的指针给函数.
类:
带有函数的结构体,称为类.
成员函数:
结构体里面的函数,称为成员函数.
6、什么是this指针?
struct sclass { int a; int b; int c; int d; int Plus() { return a+b; } }; sclass s; s.Plus();
观察反汇编:
lea ecx,[ebp-10h]
call @ILT+90(Plus) (0040105f)
this指针的特点:
1、你用或者不用,它就在那里
2、参数个数确定的时候,用ecx来传递
3、参数个数不确定的时候,最后一个传递(参见不定长参数)
4、this指针不能做++ -- 等运算,不能重新被赋值.
5、this指针不占用结构体的宽度.
//显示使用this指针 struct sclass { int a; int b; void Init(int a,int b) { this->a = a; this->b = b; } void Print() { printf("%d %d",a,b); } }; sclass s; s.Init(1,2); s.Print();
7、构造函数
struct Person { int age; int level; Person() { printf("Person对象创建了 "); } Person(int age,int level) { this->age = age; this->level = level; } void Print() { printf("%d-%d ",age,level); } };
构造函数的特点:
1、与类同名
2、没有返回值
3、创建对象的时候执行
4、主要用于初始化
5、可以有多个(最好有一个无参的),称为重载 其他函数也可以重载
6、编译器不要求必须提供
8、析构函数
析构函数的特点:
1、只能有一个析构函数,不能重载
2、不能带任何参数
3、不能带返回值
4、主要用于清理工作
5、编译器不要求必须提供
#include "malloc.h" struct Person { int age; int level; char* arr; Person() { printf("无参构造函数执行了..."); } Person(int age,int level) { printf("有参构造函数执行了..."); this->age = age; this->level = level; arr = (char*)malloc(1024); } ~Person() { printf("析构函数执行了..."); free(arr); arr = NULL; } void Print() { printf("%d-%d ",age,level); } };
9、继承
1、观察反汇编 struct Person { int age; int sex; }; struct Teacher { int age; int sex; int level; int classId; }; struct Student { int age; int sex; int code; int score; };
Teacher t; t.age = 1; t.sex = 2; t.level = 3; t.classId = 4; printf("%d ",sizeof(t));
观察反汇编:
mov dword ptr [ebp-10h],1
mov dword ptr [ebp-0Ch],2
mov dword ptr [ebp-8],3
mov dword ptr [ebp-4],4
push 10h
push offset string "%d
" (0042201c)
call printf (004010e0)
add esp,8
Student s; s.age = 1; s.sex = 2; s.code = 3; s.score = 4; printf("%d ",sizeof(s));
mov dword ptr [ebp-10h],1
mov dword ptr [ebp-0Ch],2
mov dword ptr [ebp-8],3
mov dword ptr [ebp-4],4
push 10h
push offset string "%d
" (0042201c)
call printf (004010e0)
add esp,8
2、改变写法: struct Person { int age; int sex; }; struct Teacher:Person { int level; int classId; }; struct Student:Person { int code; int score; };
Teacher t; t.age = 1; t.sex = 2; t.level = 3; t.classId = 4; printf("%d ",sizeof(t));
观察反汇编:
mov dword ptr [ebp-10h],1
mov dword ptr [ebp-0Ch],2
mov dword ptr [ebp-8],3
mov dword ptr [ebp-4],4
push 10h
push offset string "%d
" (0042201c)
call printf (004010e0)
add esp,8
Student s; s.age = 1; s.sex = 2; s.code = 3; s.score = 4; printf("%d ",sizeof(s));
mov dword ptr [ebp-10h],1
mov dword ptr [ebp-0Ch],2
mov dword ptr [ebp-8],3
mov dword ptr [ebp-4],4
push 10h
push offset string "%d
" (0042201c)
call printf (004010e0)
add esp,8
总结:
1、什么是继承?
继承就是数据的复制
2、为什么要用继承?
减少重复代码的编写
3、Person 称为父类或者基类
4、Teacher、Student称为子类或者派生类
5、t和s可以称为对象或者实例.
6、可以用父类指针指向子类的对象.
多层继承: struct X { int a; int b; }; struct Y:X { int c; int d; }; struct Z:Y { int e; int f; }; Z z; z.a = 1; z.b = 2; z.c = 3; z.d = 4; z.e = 5; z.f = 6; printf("%d ",sizeof(z)); struct X { int a; int b; }; struct Y:X { int a; int d; }; struct Z:Y { int e; int f; }; Z z; z.X::a = 1; z.b = 2; z.Y::a = 3; z.d = 4; z.e = 5; z.f = 6; printf("%d ",sizeof(z)); printf("%d ",z.X::a); printf("%d ",z.b); printf("%d ",z.Y::a); printf("%d ",z.d); printf("%d ",z.e); printf("%d ",z.f);
观察反汇编:
mov dword ptr [ebp-18h],1
mov dword ptr [ebp-14h],2
mov dword ptr [ebp-10h],3
mov dword ptr [ebp-0Ch],4
mov dword ptr [ebp-8],5
mov dword ptr [ebp-4],6
push 18h
push offset string "%d
" (0042201c)
call printf (004010e0)
add esp,8
多重继承: struct X { int a; int b; }; struct Y { int c; int d; }; struct Z:X,Y { int e; int f; }; Z z; z.a = 1; z.b = 2; z.c = 3; z.d = 4; z.e = 5; z.f = 6; printf("%d ",sizeof(z)); 3、继承的特性 struct X { int a; int b; }; struct Y { int a; int d; }; struct Z:X,Y { int e; int f; }; Z z; z.X::a = 1; z.b = 2; z.Y::a = 3; z.d = 4; z.e = 5; z.f = 6; printf("%d ",sizeof(z)); printf("%d ",z.X::a); printf("%d ",z.b); printf("%d ",z.Y::a); printf("%d ",z.d); printf("%d ",z.e); printf("%d ",z.f);
观察反汇编:
mov dword ptr [ebp-18h],1
mov dword ptr [ebp-14h],2
mov dword ptr [ebp-10h],3
mov dword ptr [ebp-0Ch],4
mov dword ptr [ebp-8],5
mov dword ptr [ebp-4],6
push 18h
push offset string "%d %x
" (0042201c)
call printf (004010e0)
add esp,8
总结:
一个子类可以有多个父类,即多重继承
观察反汇编:
mov dword ptr [ebp-18h],1
mov dword ptr [ebp-14h],2
mov dword ptr [ebp-10h],3
mov dword ptr [ebp-0Ch],4
mov dword ptr [ebp-8],5
mov dword ptr [ebp-4],6
push 18h
push offset string "%d %x
" (0042201c)
call printf (004010e0)
add esp,8
总结:
1、多重继承增加了程序的复杂度,容易出错
2、微软建议使用单继承,如果需要多重继承可以改为多层继承
10、将定义与实现分离,代码会有更好的可读性
xxx.h 头文件中 struct Test { int x; int y; int z; void Init(int x,int y,int z); void Function1(); void Function2(); void Function3(); }; xxx.cpp void Test::Init(int x,int y,int z) { this->x = x; this->y = y; this->z = z; } void Test::Function1() { printf("Function1:%x ",x); } void Test::Function2() { printf("Function2:%x ",y); } void Test::Function3() { printf("Function3:%x ",z); }
特别说明:
1、xxx.h 只是一个文件,可以是任何的后缀名,如果你愿意,
可以叫xxx.exe
2、#include 的作用只是把里面的内容复制过来 仅此而已.
如:#include "abc.exe"
3、xxx.h 与 xxx.cpp并不要求一定同名
11、public private的使用
public的意思是,这个成员哪里都可以用,不用担心被修改,所以,一旦发布成public的成员,是不能够改名字的.
private的意思是,这个成员只用于内部使用,不要在其他的地方使用.
如下: 探测一:观察private成员是否有空间 struct Test printf("%d ",sizeof(Test)); { private: 探测二:直接使用private成员 int x; public: Test t; int y; t.x = 10; }; t.y = 20;
总结:
1、对外提供的函数或者变量,发布成public的 但不能随意改动.
2、可能会变动的函数或者变量,定义成private的 这样编译器会在使用的时候做检测.
3、只有结构体内部的函数才可以访问private的成员.
4、public/private可以修饰函数也可以修饰变量.
private真的不能访问吗?
struct Test { private: int x; public: int y; void Init(int x,int y) { this->x = x; this->y = y; } }; Test t; t.Init(1,2); int* p = (int*)&t; int n = *p; int m = *(p+1); printf("%d %d ",n,m);
总结:
private修饰的成员与普通的成员没有区别 只是编译器会检测.
private修饰的成员只要自己的其他成员才能访问
1、成员权限的区别: class Base { int x; int y; }; int main(int argc, char* argv[]) { Base base; base.x = 10; base.y = 20; return 0; } class与struct的区别: 编译器默认class中的成员为private 而struct中的成员为public
2、继承中的区别: class Base { public: int x; int y; }; class Sub:Base { public: int a; int b; }; int main(int argc, char* argv[]) { Sub sub; sub.x = 1; //无法访问 sub.y = 2; //无法访问 sub.a = 3; sub.b = 4; return 0; }
class Sub:private Base { public: int a; int b; }; 父类中的程序继承后变成private属性 如果不希望改变成员的属性: class Sub:public Base { public: int a; int b; };
private是否被继承 class Base { public: Base() { x = 11; y = 12; } private: int x; int y; }; class Sub:Base { public: int a; int b; }; int main(int argc, char* argv[]) { Sub sub; sub.a = 1; sub.b = 2; int* p = (int*)⊂ printf("%d ",sizeof(sub)); printf("%d ",*(p+0)); printf("%d ",*(p+1)); printf("%d ",*(p+2)); printf("%d ",*(p+3)); return 0; }
总结:
1、父类中的私有成员是会被继承的
2、只是编译器不允许直接进行访问