⭐本篇为C++学习第6章,主要了解默认成员函数,构造函数,析构函数
⭐本人Gitee C++代码仓库:yzc的c++学习: 小川c++的学习记录 - Gitee.com
目录
一. 类的6个默认成员函数
对于一个空类来说,其大小是1,但是空类中真的什么都没有吗??
其实,任何类在什么都不写时候,编译器会默认生成6个成员函数
1. 完成类对象的初始化和清理工作:
构造函数用于完成对象的初始化
析构函数用于完成对象的清理
2. 完成类对象的拷贝和赋值:
拷贝构造用于完成类初始化时候使用一个对象拷贝另一个对象
赋值运算符重载用于使用一个对象赋值给另一个对象
3. 取地址重载:
普通对象和const对象的取地址重载,我们很少自己实现
注意:
1. 默认生成的构造函数和析构函数。针对成员变量:内置类型不会处理,但是成员变量是其他类的话,会调用这个类的构造函数。
2.默认生成的拷贝构造和赋值运算符重载。只会按字节序拷贝(浅拷贝)
也就是说,很多类不需要我们去实现这些函数,编译器会自动生成。
但是对于拷贝构造和赋值运算符重载。由于C++默认生成的是浅拷贝,这会造成错误。如:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
using namespace std;
class A
{
public:
A(int a = 0)
{
_a = &a;
}
~A()
{
free(_a);
}
private:
int* _a;
};
int main()
{
A a;
A b(a);
A c(2);
a = c;
return 0;
}
由于默认的浅拷贝,对象a,b,c中的_a指向同一块空间。
当调用析构函数的时候,这块空间会被析构多次,就会报错。
所以,我们需要自己实现拷贝函数和赋值运算符重载的深拷贝,以避免出现这种问题。
二. 构造函数
我们以时间类为例,逐步讲解。
我们知道,在C语言可以在结构体中实现一个Init函数,用于初始化结构体
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
using namespace std;
class Time
{
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << "/" << _month << "/" << _day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Time t1;
t1.Init(2024, 9, 24);//完成初始化工作
t1.print();
return 0;
}
但是这种方法有点麻烦,我们能不能在创建对象的时候就初始化呢?
这个时候就需要使用构造函数来完成这个功能
构造函数的名称和类名是一样的!
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
using namespace std;
class Date
{
public:
//无参的构造函数
Date()
{
_year = 0;
_month = 0;
_day = 0;
}
//有参的构造函数
Date(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 t1;
Date t2(2024, 9, 24);
t1.print();
t2.print();
return 0;
}
运行结果:
使用构造函数,我们能够方便地对对象进行初始化
对于有参和无参构造函数,我们还能使用缺省值的特点来合并。
//带缺省值的构造函数,合并了有参构造和无参构造
Date(int year = 0, int month = 0, int day = 0)
{
_year = year;
_month = month;
_day = day;
}
注:
1. 构造函数(在对象初始化调用的函数) ---> 完成类的初始化工作,
a 函数名和类名相同,无返回值
b 对象初始化的时候自动调用类中的构造函数
c 构造函数可以重载,完成不同对象的初始化(有参和无参)
d 如果类中没有自定义构造函数,编译器会默认生成一个无参的构造函数
e 如果用户自定义了一个有参的构造函数,那么系统不会自动生成,这个时候需要用户自己定义一个无参的默认构造函数,
默认构造函数针对内置类型成员变量没有初始化,针对自定义类成员变量,会调用其的构造函数进行初始化,如:
class A { public: A(int a = 0) { _a = a; } private: int _a; }; class B { int _b; A a; };B的默认构造函数会调用A的相应构造函数
在实际应用中,我们更倾向于使用全缺省的默认构造函数
f 无参的构造函数和全缺省的构造函数统称为默认构造函数,但是默认构造函数只能存在一个,所以这两个函数只能定义一个,两个同时存在会造成歧义
三.析构函数
析构函数用于完成对象的清理工作。当对象的生命周期结束后,就会自动调用。
class Date
{
public:
//带缺省值的构造函数,合并了有参构造和无参构造
Date(int year = 0, int month = 0, int day = 0)
{
_year = year;
_month = month;
_day = day;
}
//析构函数,由于没有使用new,malloc等开辟空间。函数内部为空
~Date()
{}
void print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
注:
析构函数(用于完成对象的清理工作)
a 析构函数无返回值,无参数。
b 析构函数名字的在类名前加~
c 一个类只有一个析构函数,若用户没有自定义,编译器会默认生成一个
d 当对象的生命周期结束后,会自动调用其析构函数
e 和构造函数类似,默认生成的构造函数不会处理内置类型,对于类成员变量,会调用该类的析构函数
f 析构函数完成的是对象的清理,不是对象的销毁
g 注意析构的顺序,后创建的对象先析构
四 多个对象调用构造函数和析构函数的顺序
#include<iostream>
using namespace std;
class A
{
public:
A()
{cout << "调用A构造函数A()" << endl;}
~A()
{cout << "调用A析构函数~A()" << endl;}
};
class B
{
public:
B()
{cout << "调用B构造函数B()" << endl;}
~B()
{cout << "调用B析构函数~B()" << endl;}
A a;
};
int main()
{
A a1;
B b1;
A a2;
B b2;
return 0;
}
分析一下,上面的的代码输出是什么??
应该是:
调用A构造函数A() //a1
调用A构造函数A() //b1
调用B构造函数B() //b1
调用A构造函数A() //a2
调用A构造函数A() //b2
调用B构造函数B() //b2
调用B析构函数~B() //b2(后创建,先析构)
调用A析构函数~A() //b2
调用A析构函数~A() //a2
调用B析构函数~B() //b1
调用A析构函数~A() //b1
调用A析构函数~A() //a1
运行结果如下

即构造函数先完成类内对象的构造函数,在调用自己的构造函数。
析构函数先完成自己的析构再去调用类内成员的析构函数。

&spm=1001.2101.3001.5002&articleId=142497635&d=1&t=3&u=89068acb45de44df8f9211274807ac1e)
1142

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



