在C语言中,我们写栈,链表等等,需要自己手动初始化,手动销毁。
在c++中,一个空类中什么都不写的时候,编译器会自动生成以下6个默认成员函数。
默认成员函数:用户没有显式定义的时候,编译器会默认生成的成员函数。
class date
{
//...
};
#include
using namespace std;
class test
{
public:
void init(int a, int b)
{
_a = a;
_b = b;
cout << _a << ' ' << b << endl;
}
private:
int _a;
int _b;
};
int main()
{
test m1;
m1.init(1, 2);
test m2;
m2.init(3, 4);
return 0;
}
如以上场景,我们创建了2个对象m1和m2,需要对这2个对象进行初始化,那么每次创建完对象后,就需要调用一次print函数,是不是显得有点麻烦,能不能在我们创建好对象的同时就能进行初始化呢?
下面引出构造函数:
是一个特殊的成员函数,名字与类名相同,创建类类型的对象编译器自动调用,以保证每个数据都有一个合适的初始值,并且对象整个生命周期只调用一次。
函数名与类名相同。
无返回值,不需要添加void。
对象实例化的时候自动调用。
可以重载。
带参的构造函数定义与调用。
class test
{
public:
test(int a, int b)
{
_a = a;
_b = b;
cout << _a << ' ' << b << endl;
}
private:
int _a;
int _b;
};
int main()
{
test t1(5,6);
return 0;
}
无参的构造函数定义与调用。
class test
{
public:
test()
{
//...
}
private:
int _a;
int _b;
};
int main()
{
test t1;
return 0;
}
调用无参的构造函数的时候不要加括号!否则变成函数声明。
概念:当用户没有显式定义构造函数的时候,系统会自动生成无参的默认构造函数,一旦显式定义,编译器将不再生成。
class test
{
public:
/*void test(int a, int b)
{
_a = a;
_b = b;
cout << _a << ' ' << b << endl;
}*/ //这边先注释掉
void print()
{
cout << _a << ' ' << _b << endl;
}
private:
int _a;
int _b;
};
int main()
{
test t1;
return 0;
}
我们没有定义任何构造函数,创建类类型对象的时候应该编译器会生成默认构造函数帮助我们的_a _b成员变量初始化。
进行测试后发现,这2个成员变量均为随机值,那么问题来了,编译器默认生成的构造函数是不是没有意义?
解答:不是没有意义,在C++的类中,成员变量分为内置类型和自定义类型,在调用默认构造函数的时候,只会对自定义类型初始化,什么意思呢,我们看下面一段代码。
class egg
{
public:
egg()
{
cout << "egg类调用成功" << endl;
}
};
class test
{
public:
/*void test(int a, int b)
{
_a = a;
_b = b;
cout << _a << ' ' << b << endl;
}*/ //这边先注释掉
void print()
{
cout << _a << ' ' << _b << endl;
}
private:
int _a;
int _b;
egg _t;
};
int main()
{
test t1;
return 0;
}
我们再创建一个类egg,然后在test的成员变量里面添加一个egg类的对象_t,那么此时这个t就是自定义变量。
在c++中,创建类类型的对象的时候,**会对其中的自定义类型成员调用它(**自定义类型成员内部的)默认构造函数。
无参数的构造函数
class wjw
{
public:
wjw()
{
cout << a << b << endl;
}
private:
int _a;
int _b;
};
无参数并且参数为缺省值的构造函数
class wjw
{
public:
wjw(int a = 1, int b = 2)
{
cout << a << b << endl;
}
private:
int _a;
int _b;
};
不显式定义构造函数。
class wjw
{
public:
/*wjw(int a = 1, int b = 2)
{
cout << a << b << endl;
}*/
private:
int _a;
int _b;
};
int main()
{
wjw test;
return 0;
}
总结:默认构造函数就是没有参数的构造函数。
:与构造函数功能相反,在对象销毁的时候自动调用析构函数,完成对象中的资源清理工作。
构造函数在类类型对象创建的时候系统会调用,并且会对其中的自定义类型调用它的默认成员函数
对于析构函数,是否也会自动对类中的自定义类型调用它的默认成员函数(这里用析构函数演示)?
#include
using namespace std;
class test1
{
public:
~test1()
{
cout << "调用t1的函数成功" << endl;
}
};
class test2
{
public:
private:
int _a;
int _b;
test1 _t;
};
int main()
{
test2 wjw;
return 0;
}
如图,调用成功。
但是,在main函数中,没有创建test1类的对象,为什么能调用test1类的析构函数?
其实结果是这样:
只有单个形参,该形参是对本类类型对象的引用(常用const修饰),在用已存在的类类型对象创建新对象时,由编译器自动调用。
如果没有显式定义拷贝构造函数,编译器会默认生成一个拷贝构造函数,默认的拷贝构造函数会按照内存存储 按字节序完成拷贝,如下代码。
class test1
{
public:
test1()//构造函数
{
_a = (int*)malloc(sizeof(int) * 4);
if (_a == nullptr)
{
perror("malloc fail");
}
_b = 4;
}
~test1()//默认析构函数
{
free(_a);
}
private:
int* _a;
int _b;
};
int main()
{
test1 wjw;
test1 zqh(wjw);
return 0;
}
先创建一个test1类类型对象wjw,test1类中定义2个成员变量,一个指针,一个_b。
再创建一个test1类类型对象zqh,并且拷贝wjw对象,因为我没有在test1类中显式定义拷贝构造函数,所以编译器会自动按字节序拷贝wjw对象,在test1类中我定义了一个析构函数,当return 0的时候会调用这个析构函数。运行结果是这样的。
运行崩溃了!这是为什么?
这是浅拷贝的后果。
在wjw对象创建好的同时,他其中的成员变量_a指针开辟了一块内存空间。
在zqh对象复制wjw的时候,直接浅拷贝,指向_a开辟的同一块空间。
当return 0这一步的时候,wjw对象和zqh对象都会自动调用析构函数free释放掉_a指向的内存空间,一块内存空间被释放2次,就会造成运行崩溃了。
结论:当涉及内存申请的时候,要自己写构造函数,否则系统默认的浅拷贝构造函数可能会造成同一内存空间的多次释放,造成运行崩溃。
函数原型:返回值类型 operator 操作符(参数列表)
class date
{
public:
date(int day, int month, int year)
{
_day = day;
_month = month;
_year = year;
}
bool operator ==(const date& data)//比较是否相等的运算重载
{
return _day == data._day
&& _month == data._month
&& _year == data._year;
}
private:
int _day;
int _month;
int _year;
};
int main()
{
date test(25,9,2022);
date test2(24, 9, 2022);
int k = test == test2;
cout << k << endl;
return 0;
}
注意bool函数定义这一块,为什么直接使用内部成员变量==data对象中的_day呢,前面为什么不需要写什么对象._day呢?
这一块前面说到了,运算重载的时候形参第一个默认是隐藏的this指针,指向的是调用该重载函数的类类型对象。
上面的代码表示的test和test2对象的日期是否相同。
class date
{
public:
date(int day, int month, int year)
{
_day = day;
_month = month;
_year = year;
}
bool operator ==(const date& data)//比较是否相等的运算重载
{
return _day == data._day
&& _month == data._month
&& _year == data._year;
}
date& operator =(const date& data)//赋值运算重载
{
if (this != &data)
{
_day = data._day;
_month = data._month;
_year = data._year;
}
return *this;
}
void print()
{
printf("%d %d %d\n", _year, _month, _day);
}
private:
int _day;
int _month;
int _year;
};
int main()
{
date test(25,9,2022);
test.print();
date test2(24, 9, 2022);
test2.print();
test = test2;//赋值运算重载
test.print();
test2.print();
return 0;
}
看看打印结果
就是将test2对象的日期赋值给test对象。
本篇博客到此结束,感谢阅览!