美文网首页
06-封装、内存布局、堆、构造和析构

06-封装、内存布局、堆、构造和析构

作者: 一亩三分甜 | 来源:发表于2021-07-20 00:34 被阅读0次

    《C++文章汇总》
    上一篇介绍了引用和汇编《05-汇编补充&面向对象》,本文介绍封装、内存布局和堆空间。

    1.封装

    #include <iostream>
    using namespace std;
    
    struct Person {
    private:
        int m_age;
    public:
        void setAge(int age){
            if (age < 0) {
                m_age = 1;
            }else{
                m_age = age;
            }
        }
        int getAge(){
            return m_age;
        }
    };
    int main(){
        Person person;
        person.setAge(10);
        cout << "age:" << person.getAge() << endl;
        return 0;
    }
    //输出
    age:10
    

    2.内存空间布局

    ◼ 每个应用都有自己独立的内存空间,其内存空间一般都有以下几大区域
    代码段(代码区)
    ✓ 用于存放代码
    数据段(全局区)
    ✓ 用于存放全局变量等
    栈空间
    ✓ 每调用一个函数就会给它分配一段连续的栈空间,等函数调用完毕后会自动回收这段栈空间 ✓ 自动分配和回收
    堆空间
    ✓ 需要主动去申请和释放

    3.堆空间

    ◼ 在程序运行过程,为了能够自由控制内存的生命周期、大小,会经常使用堆空间的内存
    ◼ 堆空间的申请\释放
    一一对应关系
    malloc \ free
    new \ delete
    new [] \ delete []
    x86 32bit环境,指针变量占用4个字节


    image

    ◼注意
    申请堆空间成功后,会返回那一段内存空间的地址
    申请和释放必须是1对1的关系,不然可能会存在内存泄露
    ◼ 现在的很多高级编程语言不需要开发人员去管理内存(比如Java),屏蔽了很多内存细节,利弊同时存在
    利:提高开发效率,避免内存使用不当或泄露
    弊:不利于开发人员了解本质,永远停留在API调用和表层语法糖,对性能优化无从下手

    堆空间初始化

    void test3(){
        int *p = (int *)malloc(4);
        *p = 0;
        //将4个字节中的每一个字节都设置为1,并不是将4个字节设置为1
        int size = sizeof(int) * 10;
        int *q = (int *)malloc(size);
        //momery set
        memset(q, 0, size);
        //从p地址开始的连续4个字节中的每一个字节都设置为1
        //memset(q,1,4);
        //将4个字节设置为1
        //00000000 00000000 00000000 00000001
        //将4个字节中的每一个字节都设置为1
        //00000001 00000001 00000001 00000001
    }
    

    堆空间new的时候初始化

    void test4(){
        int *p0 = new int;//Mac平台会初始化为0,Windows平台未初始化
        int *p1 = new int();
        int *p2 = new int(5);
        cout << *p0 << endl;
        cout << *p1 << endl;
        cout << *p2 << endl;
    }
    //输出
    0
    0
    5
    
    image

    memset函数是将较大的数据结构(比如对象、数组等)内存清零的比较快的方法


    图片.png

    4.对象的内存

    ◼ 对象的内存可以存在于3种地方
    全局区(数据段):全局变量
    栈空间:函数里面的局部变量
    堆空间:动态申请内存(malloc、new等)

    struct Person {
        int m_age;
    };
    //全局区
    Person g_person;
    int main(){
        //栈空间
        Person person;
        //堆空间
        Person *per = new Person;
    }
    

    5.构造函数Constructor

    ◼ 构造函数(也叫构造器),在对象创建的时候自动调用,一般用于完成对象的初始化工作

    struct Person {
        int m_age;
        
        Person(){
            m_age = 0;
            cout << "Person()" << endl;
        }
        
        Person(int age){
            m_age = age;
            cout << "Person(int age)" << endl;
        }
        void display(){
            cout << m_age << endl;
        }
    };
    
    int main(){
        Person person1;
        person1.display();
        Person person2(20);
        person2.display();
        Person person3(30);
        person3.display();
        getchar();
        return 0;
    }
    //输出
    Person()
    0
    Person(int age)
    20
    Person(int age)
    30
    

    ◼特点
    函数名与类同名,无返回值(void都不能写),可以有参数,可以重载,可以有多个构造函数
    一旦自定义了构造函数,必须用其中一个自定义的构造函数来初始化对象,若不调用会报错

    struct Person {
        int m_age;
    //    Person(){
    //        m_age = 0;
    //        cout << "Person()" << endl;
    //    } 
        Person(int age){
            m_age = age;
            cout << "Person(int age)" << endl;
        }
        void display(){
            cout << m_age << endl;
        }
    };
    
    int main(){
        Person person1;//会报错,Person()构造函数被注释了
        person1.display();
        Person person2(20);
        person2.display();
        Person person3(30);
        person3.display();
        getchar();
        return 0;
    }
    

    ◼注意
    通过malloc分配的对象不会调用构造函数

    struct Car {
        int m_price;
        Car(){
            cout << "Car::car()" << endl;
        }
        void run(){
            cout << "Car::run " << m_price << endl;
        }
    };
    int main(){
        Car car;//调用构造函数
        Car *c = new Car;//调用构造函数
        delete c;
        Car * cc = (Car *)malloc(sizeof(Car));//没有调用构造函数
        cc->m_price = 10;
        cc->run();
    }
    //输出
    Car::car()
    Car::car()
    Car::run 10 
    

    ◼ 一个广为流传的、很多教程\书籍都推崇的错误结论:
    默认情况下,编译器会为每一个类生成空的无参的构造函数
    正确理解:在某些特定的情况下,编译器才会为类生成空的无参的构造函数
    ✓ (哪些特定的情况?以后再提)

    struct Teacher {
        int m_age;
    //    Teacher(){
    //        cout << "Teacher()" << endl;
    //    }
    };
    int main(){
        Teacher teacher;
        teacher.m_age = 10;//不会默认生成构造函数
    }
    

    函数声明和构造函数易混淆:4个无参,3个有参,一共创建了7个Person对象,有两个是函数声明,并没有创建Person对象


    image
    struct Person {
        int m_age;
        
        Person(){
            m_age = 0;
            cout << "Person()" << endl;
        }
        
        Person(int age){
            m_age = age;
            cout << "Person(int age)" << endl;
        }
        void display(){
            cout << m_age << endl;
        }
    };
    Person g_person0;//Person()
    Person g_person1();//函数声明
    Person g_person2(20);//Person(int age)
    int main(){
        Person person0;//Person()
        Person person1();//函数声明
        Person person2(20);//Person(int age)
        
        Person *p0 = new Person;//Person()
        Person *p1 = new Person();//Person()
        Person *p2 = new Person(20);//Person(int age)
        //4个无参,3个有参,一共创建了7个Person对象,有两个是函数声明,并没有创建Person对象
        getchar();
        return 0;
    }
    //输出
    Person()
    Person(int age)
    Person()
    Person(int age)
    Person()
    Person()
    Person(int age)
    

    6.成员变量初始化

    I.没有自定义构造函数的情况下:new Person()堆空间成员变量初始化为0,栈空间没有初始化成员变量,里面是中断代码cccccccc

    struct Person {
        int m_age;
    };
    //全局区:成员变量初始化为0
    Person g_person;
    int main(){
        //栈空间:没有初始化成员变量,里面是cccccccc
        Person person;
        //堆空间:Windows没有初始化成员变量,Mac初始化了成员变量
        Person *p0 = new Person;
        //堆空间:成员变量初始化为0
        Person *p1 = new Person();
        
        cout << g_person.m_age << endl;
        cout << person.m_age << endl;//Windows报错
        cout << p0->m_age << endl;
        cout << p1->m_age << endl;
        
        getchar();
        return 0;
    }
    //输出
    0
    86053
    0
    0
    

    II.自定义了构造函数,但没有对成员变量赋值初始化,Windows和Mac平台下,全局区依然初始化成员变量为0
    Windows平台下Person *p0 = new Person堆空间没有初始化成员变量,
    Windows平台下Person *p0 = new Person()堆空间没有初始化成员变量,编译器认为自定义了构造函数,会自己初始化成员变量,编译器不再帮忙初始化成员变量
    Mac平台下堆空间初始化成员变量为0


    图片.png

    ◼ 如果自定义了构造函数,除了全局区,其他内存空间的成员变量默认都不会被初始化,需要开发人员手动初始化

    struct Person {
        int m_age;
        Person(){
            
        };
    };
    //全局区:成员变量初始化为0
    Person g_person;
    int main(){
        //栈空间:没有初始化成员变量,里面是cccccccc
        Person person;
        //堆空间:Windows没有初始化成员变量,Mac初始化了成员变量
        Person *p0 = new Person;
        //堆空间:Windows没有初始化成员变量,Mac初始化了成员变量
        Person *p1 = new Person();
        
        cout << g_person.m_age << endl;
        cout << person.m_age << endl;//Windows报错
        cout << p0->m_age << endl;
        cout << p1->m_age << endl;
        
        getchar();
        return 0;
    }
    //输出
    0
    86053
    0
    0
    

    Windows下Person对象数组,没有自定义构造函数,成员变量都为0,自定义了构造函数,成员变量未初始化
    Mac下均初始化为0

    struct Person {
        int m_age;
        //Person(){
        //};
    };
    int main(){
        Person *p = new Person[3]();
        cout << p[0].m_age << endl;
    }
    windows下为0
    
    struct Person {
        int m_age;
        Person(){
        };
    };
    int main(){
        Person *p = new Person[3]();
        cout << p[0].m_age << endl;
    }
    windows下为-842150451,显然没有初始化
    
    struct Person {
        int m_age;
        //Person(){
        //};
    };
    int main(){
        Person *p = new Person[3]{};
        cout << p[0].m_age << endl;
    }
    windows下为0
    
    struct Person {
        int m_age;
        Person(){
        };
    };
    int main(){
        Person *p = new Person[3]{};
        cout << p[0].m_age << endl;
    }
    windows下为-842150451,显然没有初始化
    

    7.对象初始化,对象所有成员变量清零

       Person(){
            memset(this, 0, sizeof(Person));
        };
    

    8.析构函数Destructor

    ◼析构函数(也叫析构器),在对象销毁的时候自动调用,一般用于完成对象的清理工作
    ◼特点
    函数名以~开头,与类同名,无返回值(void都不能写),无参,不可以重载,有且只有一个析构函数
    ◼注意
    通过malloc分配的对象free的时候不会调用析构函数
    ◼构造函数、析构函数要声明为public,才能被外界正常使用

    struct Person {
        int m_age;
        Person(){
            memset(this, 0, sizeof(Person));
            cout << "Person Person()" << endl;
        };
        ~Person(){
            cout << "Person ~Person()" << endl;
        }
    };
    int main(){
        cout << 1 << endl;
        {
            Person person;
        }
        cout << 2 << endl;
        getchar();
        return 0;
    }
    //输出
    1
    Person Person()
    Person ~Person()
    2
    

    malloc函数创建的对象不会调用析构函数

    struct Person {
        int m_age;
        Person(){
            memset(this, 0, sizeof(Person));
            cout << "Person Person()" << endl;
        };
        ~Person(){
            cout << "Person ~Person()" << endl;
        }
    };
    int main(){
        Person * p = (Person *)malloc(sizeof(Person));
        free(p);
    }
    //没有打印输出
    

    new Person会调用析构函数

    struct Person {
        int m_age;
        Person(){
            memset(this, 0, sizeof(Person));
            cout << "Person Person()" << endl;
        };
        ~Person(){
            cout << "Person ~Person()" << endl;
        }
    };
    int main(){
        Person *p = new Person;
        delete p;
    }
    //输出
    Person Person()
    Person ~Person()
    

    查看当前平台栈空间堆空间的地址大小

    int main(){
        Person *p = new Person;
        //堆空间的地址值
        cout << p << endl;
        //栈空间的地址值
        cout << &p << endl;
        delete p;
    }
    //输出:栈空间的地址值0x7ffeefbff480大于堆空间地址值0x1004b6370
    Person Person()
    0x1004b6370
    0x7ffeefbff480
    Person ~Person()
    

    相关文章

      网友评论

          本文标题:06-封装、内存布局、堆、构造和析构

          本文链接:https://www.haomeiwen.com/subject/nobkmltx.html