第三章 类、class、struct、对象、this

  1. C++中可以使用struct、class来定义一个类
  2. struct和class的区别
    1. struct的默认成员权限是public
    2. class的默认成员权限是private
     //用Struct 定义一个类
     struct Person
     {
     //public: //默认就是public
     	//成员变量、成员方法、函数
     	int age;
     	// 成员方法
     	void run() {
     		cout << "run(),ageis:" << age << endl;
     	}
     };
        
     //class定义一个类
     class Student
     {
     //private ://默认就是private,一旦私有只能在内部访问
     //指定为public
     public:
     	int no;
     	void study() {
     		cout << "study(): no is " << no << endl;
     	}
     };
        
        
     int main() {
     	//创建一个对象,这里不需要new
     	//这个对象分配的内存在栈空间(确切来说就是在main函数的栈空间),不需要手动管理
     	Person person;
     	person.age = 20;
     	person.run();
        	
     	Person *rperson = &person;
     	rperson->age = 30;
     	rperson->run();
    	   
     	//在栈中分配一个对象
     	Student student;
     	student.no = 300;
     	student.study();
        
     	getchar();
     	return 0;
     }
    
    1. 上面代码中person对象、rperson指针、student的内存都是在函数的栈空间,自动分配和回收的
    2. 可以尝试反汇编struct和class,看看是否有其他区别
      1. 没有其他区别,仅仅是一个public跟private的区别。
    3. 实际开发中,用class表示类比较多

C++编程规范

  1. 每个人都可以有自己的编程规范,没有统一的标准,没有标准答案,没有最好的编程规范
  2. 变量名规范参考
    1. 全局变量:g_
    2. 成员变量:m_
    3. 静态变量:s_
    4. 常量:c_
    5. 使用驼峰标识

对象的内存布局

  1. 对象占用的内存大小
    1. 函数是不占用对象的内存的,函数就是代码,函数是每个Person对象公用的,而且就一份
    2. 因此函数是放在对象外部的
    3. 即对象的内存只包含他的成员变量
    4. 函数的调用反汇编就是:call 函数的地址
      1. 仅仅就是跳转到函数的地址
       struct Person
       {
       	int m_age;
       	void run() {
       		cout << "run(),m_age is:" << m_age << endl;
       	}
       };
          
       Person person;
       person.m_age = 20;
       person.run();
       //打印person对象占用4个字节
       //函数是不占用对象的内存的,函数就是代码,函数是每个Person对象公用的,而且就一份
       //因此函数是放在对象外部的
       //即对象的内存只包含他的成员变量
       cout << sizeof(person) << endl;
      
  2. 思考:如果类中有多个成员变量,对象的内存又是如何布局的?
    1. 代码举例:

       struct Person
       {
       	int m_id;
       	int m_age;
       	int m_height;
       	void run() {
       		cout << "run(),m_age is:" << m_age << endl;
       	}
       };
              
       //调用
       //这一句仅仅是分配12个内存
       Person person;
       //将123分配给前4个内存,下面依次
       person.m_id = 123;
       person.m_age = 20;
       person.m_height = 170;
       person.run();
              
       cout << "&person==" << &person << endl;
       cout << "&person.m_id==" << &person.m_id << endl;
       cout << "&person.m_age==" << &person.m_age << endl;
       cout << "&person.m_height==" << &person.m_height << endl;
       cout << sizeof(person) << endl;
              
       //打印结果
       & person == 0021FD58
       &person.m_id == 0021FD58
       &person.m_age == 0021FD5C
       &person.m_height == 0021FD60
      
    2. 对象的内存分配图如下:

      图1

      1. 对象的首地址= 对象第一个成员变量的首地址
      2. 创建对象的本质就是分配内存
      3. 对象成员变量赋值就是拿到相应的内存赋值
  3. 思考题(仔细看
    1. 最后打印出来的每个成员变量值是多少?

       struct Person
       {
       	int m_id;
       	int m_age;
       	int m_height;
              	
       	void display() {
       		cout << "display(),m_id is:" << m_id << endl;
       		cout << "display(),m_age is:" << m_age << endl;
       		cout << "display(),m_height is:" << m_height << endl;
       	}
       };
              
       Person person;
       person.m_id = 10;
       person.m_age = 20;
       person.m_height = 40;
              
       //对象的本质就是成员变量的一块内存
       Person *rperson = (Person*)&person.m_age;
       rperson->m_id = 50;
       rperson->m_age = 30;
              
       //打印:10,50,30
       person.display();
      

this

  1. this是指向当前对象的指针
  2. 对象在调用成员函数的时候,会自动传入当前对象的内存地址
  3. 可以利用this.m_age来访问成员变量么?
    1. 不可以,因为this是指针,必须用this->m_age
  4. 为什么在调用成员函数时传入当前对象的指针,即this呢?
    1. 由上面我们知道,对象的内存分配只有成员变量且在栈中;
    2. 而类的成员函数是统一编译放在代码段的
    3. 表面上看起来类的成员函数跟对象有什么关联,其实没有任何关联,对象调用成员函数本质就是:call 函数在代码段编译的地址
    4. 但是既然成员函数跟对象没有关联,成员函数内部如何访问对象的成员变量呢? 成员函数在代码区,成员变量在栈区
    5. 只要将对象的栈的首地址在调用成员函数时传入给成员函数,成员函数内部就能拿到相应的成员变量的地址,完成访问了,这就是传入默认this的作用
  5. 因此,this是依赖于类而存在的!!!(比如:成员函数、构造函数)

     struct Person
     {
     	int m_id;
     	int m_age;
     	int m_height;
        	
     	void display() {
     		cout << "display(),m_age is:" << m_age << endl;
     		//上面访问成员变量的本质如下:默认会给该成员函数传入一个this
     		//cout << "display(),m_age is:" << this->m_age << endl;
     	}
     };
        
     Person person;
     person.m_id = 123;
     person.m_age = 20;
     person.m_height = 170;
     //本质:去代码区找到这个函数的地址值,调用这个函数,同时将当前对象的地址值this传给这个函数
     person.display();
    
  6. 汇编证实:对象调用成员函数时,会将当前对象的指针传给成员函数
    1. 源码:

       Person person;
       person.m_id = 123;
       person.m_age = 20;
       person.m_height = 170;
       person.display();
      
    2. 汇编

       //[ebp-14h]是第一个成员变量的地址,也是函数首地址
       mov         dword ptr [ebp-14h],7Bh  
       mov         dword ptr [ebp-10h],14h  
       mov         dword ptr [ebp-0Ch],0AAh  
       //将函数首地址保存到ecx寄存器中
       lea         ecx,[ebp-14h]  
       //调用成员函数,成员函数内部肯定会在ecx寄存器中取值访问
       call        002F1456  
      
Table of Contents