首页
社区
课程
招聘
[原创]C++基本语法
发表于: 2021-1-4 22:23 6249

[原创]C++基本语法

2021-1-4 22:23
6249

C++基本语法

一;类和对象

    1.1;如何定义一个类?

    在C++语言当中,我们可以使用关键字“struct”和“class”来定义。

//struct关键字定义一个类
struct Person
{
               //成员变量
               int m_age;
               //成员函数
               void run() {
                               cout << m_age << " --- > run()" << endl;
               }
};
//class关键字定义一个类
class Person
{
               //成员变量
               int m_age;
               //成员函数
               void run() {
                               cout << m_age << " --- > run()" << endl;
               }
};

    1.2;关键字“struct" 和“class"定义类有什么区别?

    struct关键字定义的成员权限默认为public。

//struct关键字定义一个类
struct Person
{
               //成员变量
               int m_age;
               //成员函数
               void run() {
                               cout << m_age << " --- > run()" << endl;
               }
};
//总结:struct定义的类成员可以被对象/指针任意调用。
int main() {
               //实例化一个类
               Person student;  
               //使用指针来指向对象
               Person* q = &student;
               //调用成员变量
               q->m_age = 10;
               //调用成员函数
               q->run();
}

    class关键字定义的成员权限默认为private。需要手动添加为public:才能被任意调用

//struct关键字定义一个类
class Person
{
public:
               //成员变量
               int m_age;
               //成员函数
               void run() {
                               cout << m_age << " --- > run()" << endl;
               }
};
//总结:struct定义的类成员可以被对象/指针任意调用。
int main() {
               //实例化一个类
               Person student;
               //使用指针来指向对象
               Person* q = &student;
               //调用成员变量
               q->m_age = 10;
               //调用成员函数
               q->run();
}

    1.3;如何实例化对象(创建一个对象)?

    我们可以使用类名+变量名来新建一个对象。

int main() {
               //实例化一个类
               Person student;   // Person是类名,student为变量(对象的名称)。
}

    1.4; 使用指针来指向一个对象

//实例化一个类
Person student;   // Person是类名,student为变量(对象的名称)。
//使用指针来指向对象
Person* p = &student;

     1.5;如何调用类当中的成员?

        1.5.1;使用对象来调用

    使用“.”来调用类当中的成员

int main() {
               //实例化一个类
               Person student;   // Person是类名,student为变量(对象的名称)。

               //调用成员变量
               student.m_age = 1;
               //调用成员函数
               student.run();
               getchar();
               return 0;
}

        1.5.2;使用指针来调用对象当中的成员

int main() {
               //实例化一个类
               Person student;   // Person是类名,student为变量(对象的名称)。
               //使用指针来指向对象
               Person* q = &student;
               //调用成员变量
               q->m_age = 10;
               //调用成员函数
               q->run();
}

二;对象的内存

   2.1;对象内存大小

    注释:这里没有讨论类当中出现指针且指向堆空间的情况。如果出现指针指向堆空间的情况,内存大小需要加上指针的大小和申请堆空间的大小。

     对象的大小本质上取决于类当中的成员成员变量的数量与大小。我们用下面代码来说明。(成员函数在代码段中,没有放入栈空间)

#include 
using namespace std;

class Person
{
public:
               int m_age;
               void run() {
                               cout << m_age << " --- > run()" << endl;
               }
};
int main() {
               Person student;
               Person* q = &student;
               q->m_age = 10;
               q->run();
}

    如上代码当中我们发现在"Person"类当中定义了一个int类型(4字节)的成员变量。接下来,我们来反编译查看一下,详细如下面代码:

    14:       Person student;
    15:       Person* q = &student;
00007FF6C845235B  lea         rax,[rbp+4]  //栈空间为对象划分了4字节的空间
00007FF6C845235F  mov         qword ptr [rbp+28h],rax

    现在,我们在“Person”类当中新增一个成员变量int类型。看看接下来栈空间会划分多大的空间。

class Person
{
public:
               int m_age;
               int m_age2;
               void run() {
                               cout << m_age << " --- > run()" << endl;
               }
};

    添加之后,我们发现,栈空间划分了8字节的空间,总结:对象的大小本质上取决于类当中的成员成员变量的数量与大小

    15:       Person student;
    16:       Person* q = &student;
00007FF64D70235B  lea         rax,[rbp+8]  
00007FF64D70235F  mov         qword ptr [rbp+28h],rax

    2.2;对象内存的位置

    对象内存存放在数据段(全局区)

#include 
using namespace std;

class Person
{
public:
	int age;
};
Person student; //在函数外创建对象,对象内存在数据段

    对象内存存放在栈空间

#include 
using namespace std;

class Person
{
public:
	int age;
};
int main() {
	Person student; //在函数内创建对象,对象内存在栈空间
}

    对象内存存放在堆空间

#include 
using namespace std;

class Person
{
public:
	int age;
};
int main() {
	Person *p = new Person; //申请一个堆空间,Person类类型对象,对象内存放在堆空间
}

三;THIS

    3.1;this的作用   

    this是什么?在类当中我们经常会看到这么一个关键字。下面我们通过一串代码来理解一下。我们来看一下这个代码它有一个类“Person”。里面有一个方法run()。方法当中是一个输出。其中包含了this这个关键字。在main函数当中,我们实例化了两个对象student,student2。在两个对象当中都对m_age进行了赋值,分别是2,3。

#include 
using namespace std;

class Person
{
public:
               int m_age = 1;
               void run() {
                               cout << "m_age = " << this->m_age << endl;
               }
};
int main() {
               Person student;
               Person student2;
               student.m_age = 2;
               student2.m_age = 3;
               student.run();
               student2.run();
}

    运行代码结果如下图,我们从结果上来看,对象student输出的值和student2输出的值分别为2和3这正是刚刚student.m_age=2,student2.m_age=3的结果。我们现在在来看看类当中的“this->m_age”有什么想法呢?是不是在对象student当中“this->m_age”就等于“student.m_age”,对象student2当中“this->m_age”就等于"student2.m_age"。

    结论:this表示的是当前调用者对象的指针

    3.2;this的原理

    现在我们已经知道了this是什么。那么接下来,我们用如下代码来看一下this的原理。它为什么能做到这些。

#include 
using namespace std;

class Person
{
public:
               int m_age = 1;
               void run() {
                               this->m_age = 5;
               }
};
int main() {
               Person student;
               student.run();
}

    我们在“student2.run()”位置设置断点,进行反汇编来查看一下。首先,我们看到如下指令。这是C++语言“student2.run()”对应的汇编代码。我们发现,首先它将student2对象的地址放入了rcx当中。接下来call指令执行地址00007FF677B0147E的函数。

    16:  student.run();
00007FF677B01B64  lea         rcx,[rbp+4]  //rbp+4即student对象的地址
00007FF677B01B68  call        00007FF677B0147E

     下面,我们执行f11,进入00007FF677B0147E地址的函数看看发生了什么事情。进入之后,我们发现,00007FF677B0147E地址保存的是一个jmp指令,且该指令跳转的目的地址是Person::run。即类当中的run()方法。

00007FF677B0147E  jmp         Person::run (07FF677B01F50h)

    我们继续单步执行,追踪一下代码,这个时候,我们发现跳转到了类当中的run()函数位置了。如下代码,我们发现首先执行mov 指令将rbp+e0h的位置的值放入rax。然后将数值5放入[rax]当中。由c++代码“this->m_age=5”我们很容易猜测rbp+e0h 就是this的指针。那么,我们来看看rbp+e0h位置存放的内容和对象student2有什么关系呢?

    11:                  this->m_age = 5;
00007FF677B01F86  mov         rax,qword ptr [rbp+00000000000000E0h]  
00007FF677B01F8D  mov         dword ptr [rax],5

    我们来回顾一下,之前在"student.run()"代码中,将student对象的地址放入rcx寄存器当中的,那么我们查看如下代码,我们发现rcx寄存器当中的student对象的地址放入了[rsp+8]的位置。我们通过软件查看发现[rsp+8]的值等于[rbp+e0]。到了这里我们知道了this的指针当中保存着是对象student的地址。

    10:  void run() {
00007FF677B01F50  mov         qword ptr [rsp+8],rcx

    总结:在执行student.run()代码是,将student对象的值保存到寄存器,然后由寄存器压入栈当中,在调用this的时候,会将保存如栈的student对象的地址放入this指针当中。即完成了this表示当前调用者的需求

四;指针访问的本质

    指针通俗的来说是指一段保存着地址的内容空间。那么我们用它来访问数据的本质是什么呢?我们使用下面代码来理解一下。整个代码很简单,类“Person”当中定义了三个成员变量,在main函数当中实例化一个对象且使用指针来访问成员变量并赋值。

class Person
{
public:
               int m_age1;
               int m_age2;
               int m_age3;
};
int main() {
               Person student;
               Person* p = &student;
               p->m_age1 = 1;
               p->m_age2 = 2;
               p->m_age3 = 3;
}

    我们反汇编来查看一下。

    12:  Person student;
    13:  Person* p = &student;
00007FF7EBCC1F8B  lea         rax,[rbp+8]                  //student对象地址放入rax寄存器当中
00007FF7EBCC1F8F  mov         qword ptr [rbp+38h],rax      //student对象地址放入[rbp+38h]地址当中。rbp+38为指针P 
    14:  p->m_age1 = 1;
00007FF7EBCC1F93  mov         rax,qword ptr [rbp+38h]      //将student对象地址放入rax寄存器当中,此时[rax]=student地址
00007FF7EBCC1F97  mov         dword ptr [rax],1            //将1赋值给m_age1
    15:  p->m_age2 = 2;
00007FF7EBCC1F9D  mov         rax,qword ptr [rbp+38h]  
00007FF7EBCC1FA1  mov         dword ptr [rax+4],2          //将2赋值给m_age2
    16:  p->m_age3 = 3;
00007FF7EBCC1FA8  mov         rax,qword ptr [rbp+38h]  
00007FF7EBCC1FAC  mov         dword ptr [rax+8],3          //将3赋值给m_age3

    总结:首先,我们必须知道第一个成员变量m_age1的地址等同于student对象的地址(即偏移量为0),那么m_age2的地址=m_age1+4(因为m_age1成员变量类型为int,即占用4字节空间,所以m_age2的偏移量等于4)。指针就是通过student对象的地址为初始地址,以成员变量的数据类型为偏移量来搜索所有的成员变量并且赋值的。


[招生]科锐逆向工程师培训(2025年3月11日实地,远程教学同时开班, 第52期)!

最后于 2021-5-14 14:44 被天象独行编辑 ,原因: 新增命名空间内容
收藏
免费 5
支持
分享
最新回复 (7)
雪    币: 64
活跃值: (58)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
当你用new创造了一个对象,C++也通过new得到一个新的对象,没有买卖就没有杀害,请停止你们new对象和女朋友的操作,为单身狗创造一片净土。
2021-1-5 11:02
0
雪    币: 1657
活跃值: (6838)
能力值: ( LV12,RANK:215 )
在线值:
发帖
回帖
粉丝
3
xuddk 当你用new创造了一个对象,C++也通过new得到一个新的对象,没有买卖就没有杀害,请停止你们new对象和女朋友的操作,为单身狗创造一片净土。
老哥境界高。所见所闻均站在众生的立场,大爱无疆。敢问阁下可是那悲天悯人的圣人???
2021-1-5 21:39
0
雪    币: 1859
活跃值: (2245)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
4
写的很好,但是感觉后面的一部分写的有些仓促。另外this部分的call指令,00007FF677B01B68  call        00007FF677B0147E,这里属于函数的慢连接,00007FF677B0147E属于一个小的占位程序,为了程序的一个统一性。兄弟,可以了解一下,模块内函数的调用。兄弟很厉害,整体上比我理解的透彻。
2021-3-9 21:06
0
雪    币: 1657
活跃值: (6838)
能力值: ( LV12,RANK:215 )
在线值:
发帖
回帖
粉丝
5
奋进的小杨 写的很好,但是感觉后面的一部分写的有些仓促。另外this部分的call指令,00007FF677B01B68 call 00007FF677B0147E,这里属于函数的慢连接,0000 ...
谢谢指点,这块我学习一下。谢谢。
2021-3-9 22:30
0
雪    币: 203
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
讲解的很详细到位,很多知识点总结的很容易理解
2021-3-12 09:53
0
雪    币: 1657
活跃值: (6838)
能力值: ( LV12,RANK:215 )
在线值:
发帖
回帖
粉丝
7
叮个大包 讲解的很详细到位,很多知识点总结的很容易理解[em_22]
能帮到你就好。
2021-3-15 15:12
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
谢谢大佬,学到了很多。
2021-3-20 13:20
0
游客
登录 | 注册 方可回帖
返回