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

简介: 【C++构造函数与析构函数】

❀构造函数

我们先来看原始的Date类:

class Date {
public:
  void Init(int year, int month, int day)
  {
    _year = year;
    _month = month;
    _day = day;
  }
  void Print()
  {
    cout << _year << "-" << _month << "-" << _day << endl;
  }
private:
  int _year;
  int _month;
  int _day;
};
int main()
{
  Date d1;
  d1.Init(2023, 4, 25);
  d1.Print();
  Date d2;
  d2.Init(2022, 4, 25);
  d2.Print();
  return 0;
}

上面的Date类实现中,每创建一个对象就得对对象Init一次,这样就显得很麻烦,而且我们还经常会忘记初始化。那么有没有什么办法能够不用每次都Init呢?答案是有的,那就是我们今天所要介绍的主题【构造函数】。


构造函数是一个特殊的成员函数,他的名字和类的名字相同,创建类的对象的时候编译器会自动调用该函数,以保证对每个数据成员都有初始值,并且在整个对象生命周期中只出现一次。


构造函数的特性

构造函数是特殊的成员函数,虽然名字叫做构造函数,但他的任务可不是为对象创建而开空间,他的主要任务是初始化对象


特征:

  1. 函数名与类名相同。
  2. 无返回值(也不需要写void)
  3. 对象实例化的时候编译器会自动调用对应的构造函数。
  4. 构造函数可以重载。
class Date
{
public:
  // 1.无参构造函数
  Date()
  {}
  // 2.带参构造函数
  Date(int year, int month, int day)
  {
    _year = year;
    _month = month;
    _day = day;
  }
private:
  int _year;
  int _month;
  int _day;
};
int main()
{
  Date d1; // 调用无参构造函数
  Date d2(2015, 1, 1); // 调用带参的构造函数
  // 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
  // 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
  // warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?)
  Date d3();
  return 0;
}

5. 如果类中没有显示定义构造函数,则C++编译器会自动生成一个午餐的默认构造函数,一旦用户显示定义百年一起将不再生成。

class Date
{
public:
  // 如果用户显式定义了构造函数,编译器将不再生成
  void Print()
  {
    cout << _year << "-" <<_month << "-" << _day << endl;
  }
private:
  int _year;
  int _month;
  int _day;
};
int main()
{
  Date d1;
  d1.Print();
  return 0;
}

690ea795086d47c380e96c0e1c991f70.png

我们可以看到自动生成无参的默认构造函数,并没有对变量进行初始化,打印出来的是随机值。


6.我们不写,编译器自动生成的默认构造函数,内置类型不做处理,自定义类型会去调用他自己的默认构造。

1.内置类型/基本类型,语言本身定义的基础类型例如:char/int/double/指针变量等。

2.自定义类型。(struct/class等定义的类型)

3.有些编译器会自己也会处理内置类型,但那是个性化行为,并不是所有的编译器都会处理。

【注意】:C++11中针对内置类型成员不初始化的缺陷,又打了个补丁,即:内置类型成员变量在类中声明时可以给默认值。


class Date
{
public:
  // 如果用户显式定义了构造函数,编译器将不再生成
    Date(int year, int month, int day)
  {
    _year = year;
    _month = month;
    _day = day;
  }
  void Print()
  {
    cout << _year << "-" << _month << "-" << _day << endl;
  }
private:
  int _year=2023;
  int _month=4;
  int _day=28;
};
int main()
{
  Date d1;
  d1.Print();
  return 0;
}

c78113466af247f5b57dc87aef30ecc7.png

7.无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能由一个。

【注意】:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

class Date
{
public:
  Date()
  {
    _year = 1900;
    _month = 1;
    _day = 1;
  }
  Date(int year = 1900, int month = 1, int day = 1)
  {
    _year = year;
    _month = month;
    _day = day;
  }
private:
  int _year;
  int _month;
  int _day;
};
int main()
{
  Date d1;
  return 0;
}

上面的代码编译时会报错>

d62e56ec9ae24c219cace2f4473c0d57.png

  1. 因为创建d1对象的时候要调用构造函数,这时候类里面的两个构造函数都可以不传参,这时候编译器就不知道要调用那个构造函数。

总结:
一般情况下,有内置类型成员,就要自己写构造函数,不能使用编译器自己生成的。
全部是自定义类型时,可以考虑让编译器自己生成。


❀析构函数

类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。

析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。

析构函数的特性

析构函数是特殊的成员变量。


特征:

  1. 析构函数名是在类名前面加上字符~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数,如果没有自己定义析构函数,那么系统会自动生成默认的析构函数。
    【注意】:析构函数不能重载。


4.对象生命周期结束时,C++编译系统会自动调用析构函数。


typedef int DataType;
class Stack
{
public:
  Stack(size_t capacity = 3)
  {
    cout << "Stack()" << endl;
    _array = (DataType*)malloc(sizeof(DataType) * capacity);
    if (NULL == _array)
    {
      perror("malloc申请空间失败!!!");
      return;
    }
    _capacity = capacity;
    _size = 0;
  }
  void Push(DataType data)
  {
    // CheckCapacity();
    _array[_size] = data;
    _size++;
  }
  // 析构函数
  ~Stack()
  {
    cout << "~Stack()" << endl;
    if (_array)
    {
      free(_array);
      _array = NULL;
      _capacity = 0;
      _size = 0;
    }
  }
private:
  DataType* _array;
  int _capacity;
  int _size;
};
int main()
{
  Stack s;
  s.Push(1);
  return 0;
}

0edf4181f7c64af6b443f941a159f575.png

  1. 系统生成的析构函数对内置成员不做处理,自定义类型会去调用它的析构函数。

总结:


3c80fa2f90494fd3b9fa01a71ac1ee4e.png

🍀小结🍀

今天我们学习了构造函数和析构函数相关的细节相信大家看完有一定的收获。



相关文章
|
6天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
29 4
|
2月前
|
编译器 C++
C++ 类构造函数初始化列表
构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。
73 30
|
1月前
|
编译器 C语言 C++
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
19 1
|
1月前
|
C++
C++构造函数初始化类对象
C++构造函数初始化类对象
17 0
|
1月前
|
C++
C++入门4——类与对象3-2(构造函数的类型转换和友元详解)
C++入门4——类与对象3-2(构造函数的类型转换和友元详解)
21 0
|
3月前
|
编译器 C++
C++的基类和派生类构造函数
基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数,类的构造函数不能被继承。构造函数不能被继承是有道理的,因为即使继承了,它的名字和派生类的名字也不一样,不能成为派生类的构造函数,当然更不能成为普通的成员函数。 在设计派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数完成,但是大部分基类都有 private 属性的成员变量,它们在派生类中无法访问,更不能使用派生类的构造函数来初始化。 这种矛盾在C++继承中是普遍存在的,解决这个问题的思路是:在派生类的构造函数中调用基类的构造函数。 下面的例子展示了如何在派生类的构造函数中调用基类的构造函数:
|
4月前
|
C++ 运维
开发与运维函数问题之析构函数在C++类中起什么作用如何解决
开发与运维函数问题之析构函数在C++类中起什么作用如何解决
42 11
|
4月前
|
编译器 C++
【C++】详解构造函数
【C++】详解构造函数
|
5月前
|
存储 编译器 C++
【C++】类和对象④(再谈构造函数:初始化列表,隐式类型转换,缺省值
C++中的隐式类型转换在变量赋值和函数调用中常见,如`double`转`int`。取引用时,须用`const`以防修改临时变量,如`const int& b = a;`。类可以有隐式单参构造,使`A aa2 = 1;`合法,但`explicit`关键字可阻止这种转换。C++11起,成员变量可设默认值,如`int _b1 = 1;`。博客探讨构造函数、初始化列表及编译器优化,关注更多C++特性。
|
5月前
|
存储 编译器 C语言
【C++】类和对象②(类的默认成员函数:构造函数 | 析构函数)
C++类的六大默认成员函数包括构造函数、析构函数、拷贝构造、赋值运算符、取地址重载及const取址。构造函数用于对象初始化,无返回值,名称与类名相同,可重载。若未定义,编译器提供默认无参构造。析构函数负责对象销毁,名字前加`~`,无参数无返回,自动调用以释放资源。一个类只有一个析构函数。两者确保对象生命周期中正确初始化和清理。