目录
一、默认成员函数
在声明一个类的时候,如果类中没有成员,那么这个类我们称之为空类。但是空类不空,编译器会给你生成以下6个默认成员函数(用户没有显式实现,由编译器自动生成的函数称为默认构造函数)。
class Time
{
//empty
};

二、构造函数
1.概念
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Date
{
private:
int _year;
int _month;
int _day;
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
};
int main()
{
Date d1;
d1.Init(2024, 3, 6);
d1.Print();
Date d2;
d2.Init(2023, 3, 7);
d2.Print();
return 0;
}
对于这样一个Date类的对象,我们可以通过公共接口使用Init函数对其进行赋值,创建一个变量调用一次Init函数还好,但是创建多个Date类型的对象时呢?那不就要多次调用Init函数了?每次都要对象名.Init()属实有点麻烦了。那我们能否在创建对象时,就对对象的属性进行初始化呢?答案是可以的。这里就引入了一个新的函数:构造函数。
构造函数是一个特殊的成员函数, 名字与类名相同,在创建对象时由编译器调用,且在对象的生命周期内只会被调用一次,用于对对象初始化。
2.实例
(1)无参构造函数
class Date
{
private:
int _year;
int _month;
int _day;
public:
Date()
{
_year = 1900;
_month = 1;
_day = 1;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
};
调用无参构造函数的时候,无需手动输入值,对象内属性值由构造函数来初始化。
(2)带参构造函数
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Date
{
private:
int _year;
int _month;
int _day;
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
};
调用带参构造函数时,需要在创建对象的时候就传入值,按顺序传入的值将给对象的属性初始化。
对于以上两种构造函数的调用方法,具体使用如下:
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Date
{
private:
int _year;
int _month;
int _day;
public:
Date()
{
_year = 1900;
_month = 1;
_day = 1;
}
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
};
int main()
{
//调用无参构造函数
Date d1;
d1.Print();
//调用带参构造函数
Date d2(2024, 3, 6);
d2.Print();
//可是这个呐?
//Date d();
//d.Print();
// warning C4930: “Date d(void)”: 未调用原型函数(是否是有意用变量定义的?)
return 0;
}
注意,在创建对象的时候如果调用无参构造函数初始化不要带括号,否则编译器会认为你是在声明一个函数,然后你又随即调用了对象的方法,可是d此时不是对象,而是返回值类型为Date的一个函数,于是编译器会给你报错。
(3)含缺省参数的构造函数
-
半缺省参数型构造函数
class Date
{
private:
int _year;
int _month;
int _day;
public:
Date(int year, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
};
int main()
{
Date d1(2024);
d1.Print();
return 0;
}
构造函数半缺省参数的传参方法与普通函数半缺省参数的传参方法一致,具体可以参考我C++的第一篇博客 C++入门:关键字、命名空间、C++输入输出和缺省参数-CSDN博客
- 全缺省参数型构造函数
class Date
{
private:
int _year;
int _month;
int _day;
public:
Date(int year = 2024, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
};
int main()
{
Date d;
d.Print();
return 0;
}
下面观察一串有关构造函数的函数重载代码,思考一个问题:这串代码的输出是什么?
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Date
{
private:
int _year;
int _month;
int _day;
public:
Date()
{
_year = 1900;
_month = 1;
_day = 1;
}
Date(int year = 2024, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
};
int main()
{
Date d;
d.Print();
return 0;
}
好叭,答案是:编译报错!原因出在了有关Date的两个构造函数重载上,当我创建变量但是不进行传参时,编译器会认为这两个函数都是默认构造函数,但是默认构造函数只能存在一个,就不知道该调用谁了。但是你将参数传入了之后,例如下面这样,编译器就会对其进行区分,又能成功运行了。
int main()
{
Date d1(1);
d1.Print();
Date d2(1,2);
d2.Print();
Date d2(1,2,3);
d2.Print();
return 0;
}
不管你传入了多少参数,只要在所控数目之内且大于0,编译器就会给你匹配到全缺省的函数上去,就不会报错了。
总结一下:
- 构造函数是成员函数之一,在创建对象的时候由编译器自行调用,且生命周期内只会被调用一次
- 构造函数的形式为:类名([参数]){//……}
- 构造函数允许函数重载,无返回值(不能写void),直接以类名开头
- 构造函数允许使用缺省参数,但是要注意全缺省参数(不进行传参时)和无参两种类型在创建对象时编译器会应不知道调用哪个函数而报错,此时的两个函数都具有默认构造函数的性质,但是默认构造函数只能存在一个

- 创建对象并调用无参的构造函数和全缺省但不传参的构造函数时,不要带(),否则会被认为是定义了一个函数,而非对象。
- 当没有显式定义构造函数的时候,编译器会默认生成一个构造函数(无参构造函数、全缺省构造函数与编译器生成的构造函数都被认为是默认构造函数)来对对象进行初始化,此时对象内部的属性值为随机值(这一步请读者自行求证)。
3.一些补充
各位老铁们初接触类时可能会觉得没显式定义构造函数时,编译器自己生成的默认构造函数没什么用,反正都是随机值。请将下面一串代码复制并运行,观察运行结果。
class Time
{
public:
Time()
{
cout << "Time()" << endl;
_hour = 0;
_minute = 0;
_second = 0;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本类型(内置类型)
int _year;
int _month;
int _day;
// 自定义类型
Time _t;
};
int main()
{
Date d;
return 0;
}
解答就是:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型,运行上面的代码,你就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认成员函数。
注意:C++11中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在 类中声明(注意:这是叫声明,不是定义)时可以给默认值。
class Time
{
public:
Time()
{
cout << "Time()" << endl;
_hour = 0;
_minute = 0;
_second = 0;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本类型(内置类型)
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定义类型
Time _t;
};
int main()
{
Date d;
return 0;
}
三、析构函数
1. 概念
析构函数的功能与构造函数相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
2.实例
class Date
{
public:
Date();
~Date();
private:
int _year;
int _month;
int _day;
};
Date::Date()
{
_year = 2024;
_month = 3;
_day = 8;
}
Date::~Date()
{
cout << "~Date()" << endl;
}
int main()
{
Date d;
return 0;
}
3.特性
- 析构函数的创建方式为:~+类名+()+{//……}
- 析构函数只能存在一个,不支持函数重载,无返回值(不用void)
- 析构函数若未显式定义,则编译器会自动生成默认析构函数
- 析构函数在对象生命周期结束时被调用,且在其生命周期内只会被调用一次
有关Stack的一个实例:
typedef int DataType;
class Stack
{
public:
Stack(size_t capacity = 3)
{
_array = (DataType*)malloc(sizeof(DataType) * capacity);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}
~Stack()
{
if (_array)
{
free(_array);
_array = NULL;
_capacity = 0;
_size = 0;
}
}
}
private:
DataType* _array;
int _capacity;
int _size;
};
void TestStack()
{
Stack s;
s.Push(1);
s.Push(2);
}
关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定义类型成员调用它的析构函数。
class Time
{
public:
~Time()
{
cout << "~Time()" << endl;
}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
private:
// 基本类型(内置类型)
int _year = 1970;
int _month = 1;
int _day = 1;
// 自定义类型
Time _t;
};
int main()
{
Date d;
return 0;
}
程序运行结束后输出:~Time()
可是在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?
这是因为我们在main函数中创建了Date对象d,而d中包含4个成员变量,其中_year, _month, _day三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t是Time类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。
但是main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数。
注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数
关于析构函数,如果类中没有申请资源(例如在堆上开辟的空间)时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如 Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。
创作不易,请多多点赞哦~
本文详细介绍了C++中的构造函数(包括无参、带参及含缺省参数的)、析构函数的概念、用法和特性,以及它们在对象初始化和资源清理中的作用。特别强调了默认构造函数和析构函数的重要性,以及如何避免资源泄漏。
——构造函数和析构函数&spm=1001.2101.3001.5002&articleId=136513956&d=1&t=3&u=11c387ebc9824c41b887d548cbdddc3d)

被折叠的 条评论
为什么被折叠?



