C++类的六个默认成员函数
默认C++类中共有6个默认成员函数,分别是构造函数、析构函数、拷贝构造函数、赋值运算符重载、普通对象取地址及const对象取地址操作符重载
构造函数
对于对象初始化的时候,构造函数的作用是对变量进行赋值
#include "iostream"
using namespace std;
int main() {
int a(10);
cout <<"a = " << a << endl;//输出 a = 10
}
<1>构造函数名与类名相同,且无返回值。根据初始化参数列表,由编译器自动调用
//代码二
#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;
}//构造函数,初始化列表
};
int main() {
Date d1(2012, 12, 12);//初始化
}
<2>构造函数可以重载。
#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;
cout << _year <<"-" << _month << "-" << _day << endl;
}
Date(int year, int month) {
_year = year;
_month = month;
cout << _year << "-" << _month << endl;
}
};
int main() {
Date d1(2012, 12, 12);//输出 2012-12-12
Date d2(1990, 10);//输出 1990-10
}
构造函数的初始化过程中分为很多中方式,根据入参不同调用不同的初始化函数,所以对于构造函数而言,可以重载
<3>如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,否则编译器不会自动生成。如果还需要使用默认构造函数,需要用户自己添加一个无参构造函数。
#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;
cout << _year <<"-" << _month << "-" << _day << endl;
}
};
int main() {
Date d1(2012, 12, 12);
Date d2;//报错
}
<4>无参的构造函数和全缺省的构造函数都被称为默认构造函数,并且默认构造函数有一个。注意:无参构造函数、全缺省构造函数、我们没写而编译器默认生成的构造函数,都可以认为是默认成员函数。当无参构造函数和全缺省构造函数都被显式定义,将会报错。
#include "iostream"
using namespace std;
class Date {
private:
int _year;
int _month;
int _day;
public:
Date(int year = 2020, int month = 12, int day = 20) {
_year = year;
_month = month;
_day = day;
cout << "全缺省构造函数被调用";
}
Date() {
cout << "无参构造函数被调用";
}
};
int main() {
Date d1;//报错
}
<5>疑问:如果不实现构造函数,编译器会生成默认的构造函数,但默认构造函数生成的成员变量是随机值,
#include "iostream"
using namespace std;
class Time {
private:
int _hour;
int _minute;
int _second;
public:
Time() {
cout << "Time构造函数被调用" << endl;
}
};
class Date {
private:
int _year;
int _month;
int _day;
Time t1;
};
int main() {
Date d1;//输出 Time构造函数被调用
}
构造函数特征:
<1>函数名和类名相同
<2>无返回值。
<3>对象实例化时编译器自动调用对应的构造函数。
<4>构造函数可以重载。
<5>构造函数在对象的生命周期内只调用一次。
<6>如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,否则编译器不会自动生成。如果还需要使用默认构造函数,需要用户自己添加一个无参构造函数。
<7>无参的构造函数和全缺省的构造函数都被称为默认构造函数,并且默认构造函数有一个。
<8>构造函数虽然名称叫构造,但是需要注意构造函数的主要任务并不是开辟空间创建对象,而是初始化对象。
2.析构函数
析构函数:与构造函数功能相反,析构函数不是完成对象的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成类的一些资源清理工作。
析构函数也是默认成员函数之一,但我们有时仍然需要手动实现,因为编译器提供的默认析构函数并不能处理所有情况,可能会造成内存泄露。
注:各类对象生命周期
1.全局对象—程序退出时销毁
2.函数体内部的局部对象—函数退出时 或者 出了该函数的作用域
3.函数体内部的static修饰的局部对象—程序退出时被销毁
4.堆上创建的对象—程序员控制
3拷贝构造函数
概念:只有单个形参,该形参是本类类型对象的引用(一般用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。
#include "iostream"
using namespace std;
int main() {
int a = 10;
int b = a;
int c(a);
cout << "b=" << b << endl;//输出 b=10
cout << "c=" << c << endl;//输出 c=10
}
(2)性质与验证
<1>拷贝构造函数时构造函数的一个重载函数。(所以拷贝构造函数的函数名也是类名)
<2>拷贝构造函数的参数只有一个且必须使用引用传参,使用传值方式会引发无穷递归调用。(建议将参数设置为 const 类名* 引用变量,避免手误将引用变量修改)
#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;
}
Date(const Date& d) {
_year = d._year;
_month =d._month;
_day = d._day;
}
void Print() {
cout << _year <<"-" << _month <<"-" << _day << endl;
}
};
int main() {
Date d1(2020,12,12);
Date d2(d1);
Date d3 = d1;
d1.Print();//输出 2020-12-12
d2.Print();//输出 2020-12-12
d3.Print();//输出 2020-12-12
}
若未显式定义,系统生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储,按字节拷贝,这种拷贝叫做浅拷贝,或者值拷贝。(定义有些不好理解,可以这么说,默认拷贝构造函数会将对象中所有东西一摸一样的拷贝给另一个对象)
浅拷贝:多个对象共用同一份资源,当这些对象销毁时,资源会被释放多次而引起程序崩溃。
#include "iostream"
#include "malloc.h"
using namespace std;
class Test {
public:
int* arr;
public:
Test() {
arr = (int*)malloc(10);
}
};
int main() {
Test t1;
Test t2 = t1;
cout <<"t1中arr指向的空间地址:" << t1.arr << endl;
cout <<"t2中arr指向的空间地址:" << t2.arr << endl;
return 0;
}
下图是打印结果:可以看到两个对象的arr指针指向的空间是相同的。arr是对象中的一个指针,保存了一个空间的地址,但是由于浅拷贝,所以两个对象中的arr保存的地址相同,导致两个arr指向了同一块空间。
用匿名对象做返回值
#include "iostream"
using namespace std;
class Test {
public:
int _a;
public:
Test(int a) {
_a = a;
cout << "构造函数调用" << endl;
}
Test(const Test& t) {
_a = t._a;
cout << "拷贝构造函数调用" << endl;
}
};
Test Tes1() {
Test t (20);
return t;
}
Test Tes2() {
return Test(30);
}
int main() {
cout << "这是Tes1函数打印结果:" << endl << endl;
Test t1 = Tes1();
cout<< "=================" << endl;
cout << "这是Tes2函数打印结果" << endl << endl;;
Test t2 = Tes2();
return 0;
}
使用匿名对象做返回值,系统将不会调用拷贝构造函数构造临时对象返回,效率要更高,所以推荐使用匿名对象返回
4.赋值运算符重载
运算符重载:C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值与参数列表和普通的函数相似。
为什么要重载运算符?内置类型比如int类型的变量,可以进行判断是否相等,比较大小等操作,而自定义的类类型的对象也想实现这些操作,但内置的 > < == 等符号显然不能满足要求,那么我们可以写特定的函数来实现这些功能,但是普通的函数需要通过函数名调用,看起来不直观,所以引入了运算符重载。
#include "iostream"
using namespace std;
class Test {
public:
int _a;
public:
Test(int a) {
_a = a;
}
bool Comper(const Test& t) {
return _a > t._a;
}
};
int main() {
Test t1(12);
Test t2(90);
if (t1.Comper(t2)) {
cout << "t1大" << endl;
}
else{
cout << "t2大" << endl;
}
}