Bootstrap

【C++】构造函数与析构函数

很高兴你能看到这篇文章,同时我的语雀文档也更新了许多嵌入式系列的学习笔记希望能帮到你
语雀嵌入式系列学习笔记

构造函数

作用

  • 只要你 新建对象,类的构造函数都会被自动调用(对象不能主动调用构造函数)
  • 构造函数专门用来新建对象的
  • 实际应用:往往用来初始化对象

语法

 类名(形参列表)
  {
       代码
  }
------------------------- 示例 ---------------------
class Cat
{
public:
    Cat()
    {
        cout << "我是一个构造函数" << endl
    }
};

特点

  • 构造函数的名字必须跟类名相同
  • 构造函数没有返回值类型
  • 如果程序员没有定义构造函数,系统会自动帮你生成一个无参的构造函数
  • 构造函数可以重载,重载构造函数是为了实现创建对象的多样化

示例:计算两点之间的距离

#include <iostream>
#include <cmath>
using namespace std;
//两点间距离公式 : (x1-12)² + (y1-y2)²然后开根号
class CalculateDistance
{
public:
    CalculateDistance(int _x,int _y)
    {
        x = _x;
        y = _y;
    }
    void getDistance(CalculateDistance &a)
    {
        int absx = this->x - a.x;
        int absy = this->y - a.y;
        cout << "两点距离是:" << sqrt(absx*absx + absy*absy) << endl;
    }
private:
    int x;
    int y;
};
int main()
{
    //创建两个点
    CalculateDistance a(1,2);
    CalculateDistance b(3,4);
    //计算两点之间的距离
    a.getDistance(b);
    return 0;
}

构造函数的参数列表初始化

注意:参数列表初始化的方式只能在构造函数中使用,普通函数无法使用!

语法

语法: 
class  类名 
{
    public:  
    类名(参数1,参数2,参数3...):成员1(参数1),成员2(参数) ... 
}

初始化列表的缺陷

class person
{
public:
    person(const char *name, int a, int h) : age(a), height(h)
    {
        // this->name = name;// 数组名(地址常量)❌ 这是错误的,无法使用 = 直接赋值
        strcpy(this->name, name);// ✅ 正确方式,复制字符数组
    }
private:
    char name[1024];
    int age;
    float height;
};
 
//如果类中的数据成员,无法直接通用 =等于号赋值 ,那么就不能写入参数列表中! 

默认构造函数

当用户《不编写》类的构造函数时,该类会自动生成一个无参的构造函数,叫作默认构造函数

当用户《编写》类的构造函数后,该类就不会自动生成一个无参的构造函数 (默认构造函数)!

带默认参数的构造函数

优点

更灵活的对象初始化 代码简洁 提高可读性

#include <iostream>
using namespace std;

class Cat {
public:
    // ✅ 添加默认参数,使得无参调用也可以工作
    Cat(float _w = 10.0, int _age = 2);

private:
    float weight;
    int age;
};

// ✅ 使用初始化列表初始化成员变量 weight 和 age
// : weight(_w), age(_age) 等价于 weight = _weight; age = _age;
Cat::Cat(float _w, int _age) : weight(_w), age(_age) {
    cout << "猫的体重: " << weight << "  年龄: " << age << endl;
}

int main() {
    Cat c1;             // ✅ 现在可以调用无参构造(使用默认参数)
    Cat c2{};           // ✅ 使用 {} 让编译器识别它是对象,而不是函数
    Cat c3(13.5, 5);    // ✅ 传递参数创建对象

    return 0;
}

分析不同方法初始化对象

Cat c3(12.5,5); //传统的 C++ 方式(普通构造函数调用)
Cat c4 = Cat(13.5,6);//❌不推荐使用,等价于 Cat c4(13.5,6);
                     //但是多了一次拷贝或移动(C++11 可能优化)
Cat c5{14.5,7};  // C++11 及以后支持
                 //这个 {} 方式会 优先匹配 std::initializer_list<T> 
                //构造函数,如果没有,就匹配普通构造函数。
Cat c6 = {15.5,8};  // C++11 及以后支持
方式语法适用范围C++98/03C++11+额外拷贝?是否推荐
直接构造Cat c3(12.5,5);任何类✅ 推荐
拷贝构造Cat c4 = Cat(13.5,6);任何类可能有❌ 不推荐
列表初始化 Cat c5{14.5,7};任何类✅ 推荐
等号列表初始化 Cat c6 = {15.5,8};任何类✅ 推荐

析构函数

作用

当对象的地址空间释放的时候,会自动调用析构函数(对象可以主动调用析构函数)
        栈空间 --》函数调用结束,自动释放,自动析构
        堆空间 --》函数调用结束不会主动释放,除非你主动调用delete
实际应用:往往用来做收尾工作
          比如:
               构造函数--》初始化工作,使用open打开一个文件
                                      给指针分配堆空间
               析构函数--》收尾工作,使用close关闭文件
                                      主动释放

语法

析构函数的特点:
    1.析构函数名字与类名相同 ,且在函数名前添加 ~ 
    2.析构函数没有返回值,没有参数 
    3.析构函数无需调用,在对象销毁的时候会自动调用。 
    4. 析构函数不可以重载,只有一种写法
        
class  类名 
{
    public: 
     ~类名() 👈 这就是析构函数
     {
               
     }
}    
---------------示例--------------------
class base 
{
    public:  
    ~base()
    {
         cout <<  "我是一个析构函数" << endl;       
    }
}; 

示例:析构释放构造函数中分配的资源

#include <iostream>
using namespace std;
#include <unistd.h>
class base
{
public:
    // 构造函数
    base()
    {
        this->p = new int(100);
        cout << "分配堆空间:" << this->p << "空间上的内容:" << *(this->p) << endl;
    }
    // 析构函数
    ~base()
    {
        cout << "释放堆空间:" << this->p << endl;
        delete this->p;
    }
 
private:
    int *p; // 指向一块堆空间
};
 
int main()
{
 
    base tmp; // 栈空间,局部变量
    cout << "创建tmp对象成功" << endl;
 
    sleep(10);
 
    return 0; // 程序结束,base 的对象tmp 空间就会被销毁
}

;