2020, Aug.4 类的构造函数与析构函数

类的构造函数与析构函数

  1. 类的构造函数
  • 引例

    / *
      * 定义一个类型:Cirlce,圆
      * 成员:圆心坐标(x, y),半径radius
      * /
    class Cirlce {
    public:
        int x, y;
        int radius;
    };
    
    int main()
    {
        // 定义一个对象
        Circle a;
        // 问题:此时这个对象a算是一个有效的对象么?
        // 看看它的x,y和radius,是乱的
        
        // 那么,再给它赋值不就好了么?
        a.x = 0;
        a.y = 0;
        a. radius = 1;
        
        // 但是从“对象”的观点来看,对象被创建之时,就应该是有效的,不应该存在“半成品”对象。
        // 比如说,一辆车Car在造出来的时候,必须带着轮子。如果没有轮子,此时他还不能称为Car。
        
        return 0;
    }
    
  • 存在问题:定义了一个对象,新建的的对象在内存中的值是无效的(杂乱的负值)。如何让一个对象在被创建的时候,就赋予初始值。

  • 解决方法:构造函数

  • 构造函数是类的一种特殊的成员函数,构造函数不是普通的函数

    1. 函数名与类名必须相同
    2. 没有返回值
    class Circle {
    public:
        Circle()
        {
            x = y = 0;
            radius = 1;
        }
    };
    
  • 构造函数可以带参数,也可以重载

    class Cirlce {
    ...
    public:
        Cirlce ()
        {
            printf("111\n");
            x = y =0;
            radius = 1;
        }
        Circle(int x, int y, int r)
        {
            printf("222\n");
            this->x = x;
            this->y = y;
            this->radius = r;
        }
    publicint x, y;
        int radius;
    };
    
  • 构造函数如何调用?

    1. 构造函数和普通成员函数不一样,一般不显示调用。

    2. 在创建一个对象时,构造函数被自动调用(由编译器完成)

      Circle a;
      Circle b(1,1,4)l;
      
    3. 它们在内部实质上是分别调用了不同的构造函数,但是表面上没有这个函数调用过程。

    int main()
    {
        Circle a;
        Circle b(1, 1, 4);
    	
        return 0;
    }
    
  • 构造函数的作用:对象一“出生”就是有效的。不存在“半成品”对象。

    它可以理解为“初始化”动作。

    基本类型的初始化;

    int a(10);  			 // 将a初始化为10, 也可以写成int a = 10;
    Student s = {1, "name"}; // struct的初始化
    

    现在,类class的初始化使用构造函数的方式。

  • 小结:

    • 介绍构造函数的语法:名字与类名相同,没有返回值
    • 构造函数的作用:用于初始化对象
    • 构造函数的调用:创建对象的同时,被编译器自动调用。
    • 构造函数也可以重载。
  1. 析构函数
  • 析构和构造是一对相反的过程。
  • 构造函数:对象被创建时被调用
  • 析构函数:对象被销毁时被调用

析:分崩离析

英文:构造函数 constructor

​ 析构函数 destructor

  • 析构函数也不是普通的函数

    1. 名称固定:类名前加上波浪线~
    2. 没有返回值
    3. 不能带参数
    class Object {
    public:
        ~Object()
        {
            
        }
    };
    

    注意:析构函数只能有一个,不能重载

  • 析构函数如何调用?

    析构函数从不显示地调用,而是被编译器自动地调用。

    什么时候被调用?对象被销毁之时

    对于局部变量(对象),在超出变量作用域后,该对象失效

  • 析构函数的作用?

    析构函数:对象在销毁之前,做一个清理和善后的工作。

    比如,申请来的内存要释放,打开的文件FIle*要关闭。

    class StringBuffer {
    public:
        StringBuffer()
        {
            m_buffer = malloc(1024*512); // 申请内存
        }
        ~StringBuffer()
        {
            free(m_buffer); 			// 释放内存
        }
    };
    
    class DataStore {
    public:
        DataStore()
        {
            m_head.next = NULL;
        }
        ~DataStore()
        {
            // 删除链表中所有节点,把所有,malloc的内存都free掉
        }
    private:
        Student m_head;
    };
    
  • 分开写在.h和.cpp里时,规则和普通函数一样

    class DataStore {
    public:
        // 函数声明
        DataStore();
        ~DataStore();
    };
    // 函数定义,要加类名前缀
    DataStore::DataStore() {}
    DataStore::~DataStore() {}
    
  • 完整DataStore实例(Add和Find函数)

    ///DataStore.h
    struct Student {
        int id;
        char name[16];
        Student* next;
    }
    
    class DataStore {
    public:
        DataStore();
        ~DataStore();
    publicvoid Add(const Student* data);
        Student* Find(int id);
        void Print();
    private:
        Student m_head;
    };
    
    
    ///DataStore.cpp///
    #include <stdio.h>
    #include "DataStore.h"
    
    DataStore::DataStore() // 创建资源
    {
        m_head.next = NULL;
    }
    DataStore::~DataStore() // 释放资源
    {
        Student* p = m_head.next;
        while(p)
        {
            Student* next = p->next;
            free(p);
            p = next;
        }
    }
    
    void DataStore::Add(const Student* data)
    {
        // 创建对象、复制数据
        Student* copy = (Student*)malloc(sizeof(Student));
        *copy = *data;
        
        // 插入一个对象到链表中
        Student* cur = m_head.next; // 省略了this指针,直接调用成员变量m_head
        Student* pre = &m_head;     // 省略了this指针,直接调用成员变量m_head
        while(cur)
        {
            if(copy->id < cur->id)  // 找到这个位置
                break;
            pre = cur;
            cur = cur->next;		// 找到最后一个对象 
        }
        
        // 插入到pre节点的后面
        copy->next = pre->next;
        pre->next = copy;
    }
    
    Student& DataStore::Find(int id)
    {
        Student* p = m_head.next;
        while(p)
        {
            if(p->id == id)
                return p;
            p = p->next; // 下一个对象
        }
        return NULL;
    }
    
    void DataStore::Print()
    {
        Student* p = this->m_head.next; // this指针可加可不加
        while(p)
        {
            printf("ID: %d, name: %s\n", p->id, p->name);
            p = p->next; // 下一个对象
        }
    }
    
    main.cpp
    #include <stdio.h>
    #include"DataStore.h"
    
    int main()
    {
        DataStore ds;
        Student nodeA = {13,"JWB",NULL}; 
        Student *p = &nodeA; 
        ds.Add(p);
        ds.Find(13);
        ds.print();
        return 0;
    }
    
  1. 构造与析构
  • 默认构造函数
    把那种不需要传参的构造函数,称为默认构造函数
    // 没有缺省值
    	Object();
    	// 所有参数都有缺省值
    	Object(int a = 10, int b = 11);
    
  • 有了默认构造函数之后,对象在构造时就可以不传递参数,Object obj;
    如果一个类没有默认构造函数,则无法构造数组
  • 如果一个类没有写任何构造函数,则编译器隐含地生成一个构造函数,相当于添加了 Object::Object() {},写了就不会添加
    如果没有写析构函数,则编译器隐含地生成一个析构函数,相当于添加了 Object::~Object() {}
  • 成员的初始化与析构
    (考虑成员变量本身也是class类型的情况)
    1. 当对象被构造时,成员变量也被构造(成员变量的构造函数被调用)
    2. 当对象被析构时,成员变量也被析构(成员变量的析构函数被调用)
  • 结论:
    1. 构造的时候:
      成员被依次构造:从前到后
      先执行成员的构造,再执行自己的构造函数
    2. 成员被一次析构:从后到前
      成员被依次析构
      先执行自己的析构函数,再执行成员的析构
  • 初始化列表
    可以在构造函数后面直接初始化
    1. 以冒号引导
    2. 使用小括号来初始化
// Object有两个成员变量x,y
Object:x(1),y(2)
{
}
/// ///
Object: m_child(1, 2)
{
}
  • 小结:
    1. 默认构造函数:不要传参的构造函数
    2. 构造函数与析构函数的顺序问题
    3. 初始化列表:在构造函数的位置指定成员的初始化列表
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值