目录
类的6个默认成员函数
构造函数
语法
特性
析构函数
特性
类的6个默认成员函数
问题:一个什么成员都没的类叫做空类,空类中真的什么都没有吗?
基本概念:任何类在什么都不写时,编译器会自动生成以下六个默认成员函数(无参的)
定义:用户没有显式实现,编译器会生成的成员函数称为默认成员函数
注意事项:如果我们自己实现了这些函数,那么编译器就不会生成默认的成员函数
构造函数
产生原因:初始化容易被遗忘,且有时候会太麻烦
作用:完成初始化工作(Init)
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(2022, 7, 5);
d1.Print();
Date d2;
d2.Init(2022, 7, 6);
d2.Print();
return 0;
}
对于Data类,之前我们会通过Init公有方法给对象设置日期,但每次创建对象时都需要调用该方法设置信息就显得有点麻烦,C++的构造函数就可以做到在对象创建时,就将信息设置进去
语法
1、构造函数是特殊的成员函数
2、构造函数的主要任务是在类实例化对象时初始化对象,而不是去开空间创建对象
3、构造函数的函数名与所在类域的类名相同(类名是A,构造函数的名字就叫A,客随主便)
4、构造函数无返回值(不是void,而是连void都没)
5、对象实例化时编译器会自动调用合适的构造函数(对象后没括号就调无形参的构造函数,有实参就调用有形参的构造函数)
#include <iostream>
using namespace std;
class Date
{
public:
// 1.无参构造函数
Date()
{
_year = 1;
_month = 1;
_day = 1;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1; // 调用无参构造函数
d1.Print();
return 0;
}
6、构造函数可以重载,构造函数的形参可有可无
#include <iostream>
using namespace std;
class Date
{
public:
// 1.无参构造函数
Date()
{
_year = 1;
_month = 1;
_day = 1;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
// 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; // 调用无参构造函数
d1.Print();
Date d2(2015, 1, 1); // 调用带参的构造函数
d2.Print();
return 0;
}
7、构造参数没有形参时,对象实例化时不能在对象后加括号
8、 对象实例化时必须调用构造函数,没有或者没有合适的构造函数也会报错
#include <iostream>
using namespace std;
class Date
{
public:
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
// 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;//d1这里原本想要调用无参的构造函数,但是只有带参的构造函数
d1.Print();
return 0;
}
9、可以利用缺省参数将无参和有参的构造函数合并减少代码量
#include <iostream>
using namespace std;
class Date
{
public:
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Print();
return 0;
}
10、无参的构造函数和全缺省的构造函数在理论上因为构成函数重载可以同时存在,但是在实践中不可以同时存在,因为会造成调用歧义
#include <iostream>
using namespace std;
class Date
{
public:
Date()
{
_year = 1;
_month = 1;
_day = 1;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Print();
return 0;
}
特性
1、实例化对象时我们不写构造函数,编译器就会自动生成无参的构造函数,从而初始化对象,但是实际上编译器默认生成的无参的构造参数什么也不干,为什么?(我们程序员没有定义一个构造参数是我们的问题,但是你编译器既然为我们自动生成了一个无参的构造参数,你总得让它起点作用吧,就是只让成员变量初始化为0也行啊)
#include <iostream>
using namespace std;
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;
}
这是因为,C++将数据分为了内置(基本)类型和自定义类型:
- 内置类型:语言自身定义的类型(int、char、double、任何类型的指针等)
- 自定义类型:程序员根据自己需求定义的类型(struct、class等)
2、C++98规定默认生成的构造函数,对内置类型不做处理,对自定义类型会去调用它的默认构造
#include <iostream>
using namespace std;
//编译器对自定义类型会调用它的默认构造
class A
{
public:
A()
{
cout << "A()" << endl;
_a = 0;
}
private:
int _a;
};
//编译器对内置类型不做处理
class Date
{
public:
//这里没有自定义构造函数,编译器默认生成构成函数
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
A _aa;
};
int main()
{
Date d1;
d1.Print();
return 0;
}
大致步骤如下:
①Date d1:实例化对象d1
②Data{}:Date类没有定义构造函数,编译器自动生成构造函数,并开始对成员变量开始初始化
③__aa:_aa的类型是A类,属于自定义类型,调用A类的提供的构造函数A,打印A(),给a赋值
如果A类也没提供构造函数,那么_aa也是随机值:
#include <iostream>
using namespace std;
class A
{
public:
int getA() // 添加一个公共方法以获取 _a 的值
{
return _a;
}
private:
int _a;
};
class Date
{
public:
void Print()
{
cout << "Year: " << _year << ", Month: " << _month << ", Day: " << _day << endl;
cout << "Value of '_aa' member variable in class 'Date': " << _aa.getA() << endl; // 打印_aa对象内部_a变量的值
}
private:
int _year;
int _month;
int _day;
A _aa;
};
int main()
{
Date d1;
d1.Print();
return 0;
}
④_year、_month、_day:是int类型,属于内置类型,不做任何操作,结果为随机值
为什么在调试时,先初始化对象_aa?
答:C++中,类的成员变量初始化顺序是由它们在类中声明的顺序决定的,而不是由它们在构造函数初始化列表中出现的顺序决定
注意事项:有些新的编译器会对内置类型也做处理,但是C++的标准没有规定
3、C++11规定内置类型的成员变量声明时可给默认值(为C++98打补丁,没给的依然是随机值)
#include <iostream>
using namespace std;
class A
{
public:
A()
{
cout << "A()" << endl;
_a = 0;
}
private:
int _a;
};
class Date
{
public:
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
//声明时给缺省值
int _year = 2024;
int _month =3 ;
int _day;
A _aa;
};
int main()
{
Date d1;
d1.Print();
return 0;
}
4、 无参构造函数、全缺省构造函数、编译器生成的构造函数称为默认构造函数,有且只有一个
#include <iostream>
using namespace std;
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;
}
#include <iostream>
using namespace std;
class Date
{
public:
Date(int year = 1, int month , int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
~Date()
{
cout << this << endl;
cout << " ~Date()" << endl;
}
private:
//声明给缺省值
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Print();
return 0;
}
#include <iostream>
using namespace std;
class Date
{
public:
Date(int year = 1, int month , int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
~Date()
{
cout << this << endl;
cout << " ~Date()" << endl;
}
private:
//声明给缺省值
int _year = 1;
int _month = 1;
int _day =1;
};
int main()
{
Date d1;
d1.Print();
return 0;
}
为什么成员变量在声明时已经给了缺省值,为什么Data还要再给?
答:虽然为
Date
类中的私有成员变量_year
,_month
, 和_day
提供了默认值(1, 1, 1),但这些默认值仅在无参构造函数中的参数未初始化或没有自定义的结构参数时才会被用于初始化成员变量。实例化d1
时,类中定义了一个貌似是全缺省的构造函数,因此编译器不会自动生成默认构造函数(那个位置上已经有人了,只是那个人有点缺陷),且因为该貌似的全缺省构造函数缺少两个缺省值所以报错
结论:绝大多数场景下都需要自己实现构造函数,且更推荐用全缺省构造函数
析构函数
产生原因:需要人为销毁的空间容易忘记销毁,内存泄漏可能不会报错
#include <iostream>
using namespace std;
class Date
{
public:
void Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
~Date()
{
cout << this << endl;
cout << " ~Date()" << endl;
}
private:
//声明给缺省值
int _year = 1;
int _month = 1;
int _day = 1;
};
void func()
{
Date d2;
}
class Stack
{
public:
Stack(size_t capacity = 4)
{
_array = (int*)malloc(sizeof(int*) * capacity);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(int data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
//~Stack()
//{
// cout << "~Stack()" << endl;
// if (_array)
// {
// free(_array);
// _array = nullptr;
// }
// _size = _capacity = 0;
//}
private:
int* _array;
int _capacity;
int _size;
};
int main()
{
func();
Date d1;
Stack st1; //内存泄漏
return 0;
}
如果Stack类中没有自定义的析构函数就会出现内存泄漏,因为我们即没有定义Destory函数去销毁在堆上开辟的空间,析构函数也不起作用
作用:完成清理工作(Destory)
基本概念:析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的,而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作
特性
1、析构函数名是在类名前加上字符~
2、析构函数无参数无返回值类型
3、一个类只能有一个析构函数,若未显示定义,编译器会自动生成默认的析构函数
4、析构函数不能重载(清理一遍数据就应该被清理完成)
5、对象生命周期结束(函数结束)时,程序会自动调用析构函数
#include <iostream>
using namespace std;
class Date
{
public:
~Date()
{
cout << this << endl;
cout << " ~Date()" << endl;
}
private:
//声明给缺省值
int _year = 1;
int _month = 1;
int _day =1;
};
void func()
{
Date d2;//实例化对象d2
}//函数结束,调用析构函数销毁数据
int main()
{
func();
Date d1;//实例化对象d1
return 0;
}//函数结束,调用析构函数销毁数据
6、编译器生成的析构函数,对内置类型成员不做处理,对自定义类型会调用它的析构函数
7、后定义的先析构(定义->开辟帧栈->压栈,栈后进先出)
#include <iostream>
using namespace std;
class Date
{
public:
~Date()
{
cout << this << endl;
cout << " ~Date()" << endl;
}
private:
//声明给缺省值
int _year = 1;
int _month = 1;
int _day = 1;
};
class Stack
{
public:
Stack(int capacity = 4)
{
_array = (int*)malloc(sizeof(int*) * capacity);
if (NULL == _array)
{
perror("malloc申请空间失败!!!");
return;
}
_capacity = capacity;
_size = 0;
}
void Push(int data)
{
// CheckCapacity();
_array[_size] = data;
_size++;
}
~Stack()
{
cout << this << endl;
cout << "~Stack()" << endl;
if (_array)
{
free(_array);
_array = nullptr;
}
_size = _capacity = 0;
}
private:
int* _array;
int _capacity;
int _size;
};
int main()
{
Date d1;
Stack st1;
return 0;
}
8、如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数(Date类);有资源申请时,一定要写,否则会造成资源泄漏(Stack类)
~over~