网站底部版权信息模板,广告设计工作室,公司业务推广,浙江 网站建设文章目录 #x1f308; 前言#x1f308; Ⅰ 构造函数1. 构造函数概念2. 构造函数特性3. 初始化列表 #x1f308; Ⅱ 析构函数1. 析构函数概念2. 析构函数特性 #x1f308; Ⅲ 拷贝构造1. 拷贝构造概念2. 拷贝构造特性3. 深度拷贝构造 #x1f308; Ⅳ 赋值重载1. 运算符… 文章目录 前言 Ⅰ 构造函数1. 构造函数概念2. 构造函数特性3. 初始化列表 Ⅱ 析构函数1. 析构函数概念2. 析构函数特性 Ⅲ 拷贝构造1. 拷贝构造概念2. 拷贝构造特性3. 深度拷贝构造 Ⅳ 赋值重载1. 运算符重载2. 赋值运算符重载 前言
1. 默认成员函数介绍
一个什么成员都没有的类简称为空类编译器会自动为空类生成几个默认成员函数。默认成员函数用户不写出来的话编译器就会生成的成员函数称为默认成员函数。编译器自动生成的默认成员函数一般都比较挫因此在大多数情况下就需要我们自己去编写这些个默认成员函数的执行逻辑。
2. 默认成员函数分类
函数功能构造函数主要完成对成员变量的初始化工作析构函数主要完成对成员变量的清理工作拷贝构造使用同类对象初始化新创建的对象赋值重载把一个对象赋值给另一个对象 Ⅰ 构造函数
1. 构造函数概念
现定义一个日期 (date) 类
class date
{
public:void Init(int year, int month, int day){_year year;_month month;_day day;}
private:int _year;int _month;int _day;
};int main()
{date d1, d2;d1.Init(2024, 2, 6); // 调用公有成员函数 Init 为对象 d1 内的成员变量初始化d2.Init(2024, 2, 7); // 调用公有成员函数 Init 为对象 d2 内的成员变量初始化return 0;
}对于 date 类可以使用 Init 公有成员函数来给对象设置日期但如果每次创建对象时都需要调用该函数来进行成员变量的初始化需要写两行且很容易忘记此时构造函数就诞生了。构造函数是一个特殊的成员函数该成员函数的名字与类名一致实例化对象时由编译器自动调用。用来保证每个对象内的成员变量都有一个适当的初始值且在对象整个生命周期内只调用一次。
2. 构造函数特性
构造函数的主要任务是初始化对象。
1. 构造函数特性
函数名和类型相同。没有任何返回值。实例化对象时编译器自动调用对应的构造函数。构造函数也支持重载。构造函数也支持缺省参数。
2. 构造函数示例
class date
{
public:// 构造函数函数名和类名相同没有返回值支持缺省参数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; // 不指定初始值时就用缺省参数初始化对象date d2(2024, 2, 7); // 在实例化对象的同时顺带就能初始化对象return 0;
}d1 使用缺省参数完成了对象初始化d2 使用给的值完成了对象初始化。都自动调用了构造函数。
3. 初始化列表
1. 为何使用初始化列表
在构造函数的函数体内对成员变量初始化称为初始化赋值并不是正儿八经的初始化是赋值就存在多次赋值的问题。初始化赋值的问题在构造函数的函数体内没办法解决。因此在构造函数时可以使用一种叫做初始化列表的方式进行初始化。用以确保每个成员变量都只被初始化一次。
class date
{
public:date(int year, int month, int day){_year year;_month month;_day day;_year 2023; // 成员变量 _year 被初始化了 2 次这咋个整}
private:int _year;int _month;int _day;
};2. 初始化列表语法格式
类名(形参列表):成员变量1(成员变量 1 的初始值),成员变量2(成员变量 2 的初始值),成员变量n(成员变量 n 的初始值)
{}3. 初始化列表示例
class date
{
public:date(int year 1, int month 1, int day 1):_arr((int*)malloc(4 * sizeof(int))),_year(year),_month(month),_day(day){cout 这是一个构造函数 endl;}
private:int* _arr;int _year;int _month;int _day;
};4. 初始化列表的特性
每个成员变量在初始化列表中只能出现一次 (只能初始化一次)。以下成员变量必须放在初始化列表中进行初始化 (在函数体内对这些成员变量初始化会报错)。 引用成员变量 const 成员变量 自定义类型成员 (且该类没有默认构造函数时) 尽量使用初始化列表进行初始化因为编译器会优先使用初始化列表。成员变量的声明顺序就是成员变量在初始化列表中初始化的顺序。 Ⅱ 析构函数
1. 析构函数概念
1. 概念
构造函数将对象内的成员变量初始化那么析构函数就是将其销毁。析构函数不是完成对对象本身的销毁对象是在出了对象所在的作用域或者程序结束时自动销毁。而对象在销毁时会自动调用析构函数完成对象中资源的清理工作。
2. 格式
~类名()
{ // 函数体
}2. 析构函数特性
1. 析构函数特性
析构函数的函数名由 ~ 和 类名 两部分构成。析构函数没有任何参数也没有返回值。一个类中只能由一个析构函数。析构函数不能实现函数重载。对象的声明周期结束时编译器会自动调用析构函数。如果类初始化时没有申请资源 (开辟空间)析构函数可以不写反之一定要写。
2. 析构函数示例
现有一个为了实现栈而定义的类。
class stack // 定义一个用于实现栈的类
{
public:stack(int capacity 4) // 构造函数:_array((int*)malloc(4 * sizeof(int))), _top(-1),_capacity(capacity){cout stack(int capacity 4) endl;}~stack() // 析构函数对象的生命周期结束时自动调用析构函数{free(_array); // 如果有动态开辟的空间就不用怕最后会忘记释放了_top 0;_capacity 0;cout ~stack() endl;}
private:int* _array;int _top;int _capacity;
};使用上述 stack 类定义出对象的话就肯定要动态开辟空间如果没有析构函数自动将开辟的空间释放掉而自己又忘了将开辟的空间手动释放内存泄漏这不就来了。 Ⅲ 拷贝构造
1. 拷贝构造概念
1. 拷贝构造概念 在实例化对象时可以不给初始值让构造函数使用缺省参数也可以给初始值让构造函数对对象进行初始化。拷贝构造就是用一个现有的同类对象去初始化另一个对象。 拷贝构造函数只有一个形参 (只显示一个形参this 指针不显示)该形参是对本类类型对象的引用 (一般常用 const 修饰)在用已存在的同类对象创建新对象时自动调用。
2. 拷贝构造语法格式
类名(const 类名 形参名) // 实际上还是有两个形参第一个形参为隐藏的 this 指针
{// 拷贝构造的函数体
}3. 拷贝构造函数示例
class date
{
public:// 构造函数date(int year 1, int month 1, int day 1): _day(day) ,_year(year),_month(month){}// 拷贝构造函数date(const date d):_year(d._year),_month(d._month),_day(d._day){}
private:int _year;int _month;int _day;
};int main()
{date d1(2024, 2, 8); // 调用构造函数对 d1 进行初始化date d2(d1); // 调用拷贝构造使用 d1 对 d2 进行初始化return 0;
}2. 拷贝构造特性
拷贝构造函数是构造函数的一个函数重载形式本质还是构造函数。拷贝构造函数的显示参数只有一个且必须是对实参对象的引用。如果自己不写拷贝构造函数编译器会自动生成默认的拷贝构造函数执行的是浅拷贝。拷贝构造函数最常用的调用场景 使用现有对象初始化创建新对象。 函数参数类型为类类型对象。 函数返回值为类类型对象。
3. 深度拷贝构造
1. 默认的拷贝构造函数执行的是浅拷贝
浅拷贝如果某个对象内的一个成员变量是一个指向一块连续空间的指针那么浅拷贝就是将该地址拷贝给另一个对象的。两个对象各自的成员变量指向同一块空间。深拷贝为新对象重新开辟一块同样大小的空间并且将已有对象内的值拷贝过去。 2. 深度拷贝构造示例
class stack
{
public:stack(int capacity 4) // 构造函数{_array (int*)malloc(sizeof(int) * capacity);assert(_array);_top -1;_capacity capacity;}stack(const stack s) // 拷贝构造this 是 st2s 是 st1{int* tmp (int*)malloc(s._capacity * sizeof(int));assert(tmp);// 将 st1 的 array 中的有效数据拷贝给 st2 的 arraymemcpy(tmp, s._array, sizeof(int) * (s._top 1));_array tmp;_top s._top;_capacity s._capacity;}
private:int* _array;int _top;int _capacity;
};Ⅳ 赋值重载
1. 运算符重载
1. 运算符重载概述
有些时候函数名无法一眼看出该函数是为了实现什么功能 (如 func1、func2 这种函数名完全看不出该函数是要用来干什么)。运算符重载是具有特殊函数名的函数是为了增强代码的可读性而被引入。
2. 运算符重载格式
函数返回值类型 operator操作符(形参列表)
{函数体
}3. 赋值运算符示例
现在要判断两个日期类的对象是否相等内置操作符无法直接进行判断。因此将 进行重载以表示这是一个判断对象是否相等的成员函数。
class date
{
public:date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}// 将 重载成判断两个类类型对象是否相等的运算符bool operator (const date d) // 此处的 this 表示 d1d 表示 d2{return _year d._year _month d._month _day d._day;}
private:int _year;int _month;int _day;
};4. 运算符重载本质
上述的 d1 d2 实际上在编译器看来是 d1.operator(d2)本质上还是调用对应的成员函数然后将 d1 的地址传给 this 指针形参 d 引用了 d2。
5. 运算符重载特性
不能通过连接其他符号来创建新的操作符如 operator 和 组成的 operator 不是一个新的操作符。重载操作符必须有一个自定义类型的参数。不能改变用于内置类型的运算符的含义如 不能将 加法 的含义重载成 减法。除了以下 5 种运算符其余运算符都能被重载 点星 ( .* )域作用限定符 ( :: )计算大小 ( sizeof )三目运算符 ( ?: )点 ( . )
2. 赋值运算符重载
1. 赋值重载功能
实现类类型对象之间的赋值现在有两个已经被实例化好的对象 A、B赋值重载就是将 对象 A 的值赋值给 对象 B。和拷贝构造不一样拷贝构造是用一个定义好的对象去初始化一个未被定义的对象。
2. 赋值重载格式
形参类型const 类名传引用可以提高传参效率。返回值类型类名设置返回值是为了支持连续赋值 (A B C 这样)。检测是否自己给自己赋值。返回 *this为了实现连续赋值。
3. 赋值重载示例
class date
{
public:date(int year 1, int month 1, int day 1){_year year;_month month;_day day;}date operator(const date d) // this 指针指向 d1d 表示 d2{if (this ! d) // 避免自己给自己赋值{_year d._year;_month d._month;_day d._day;}return *this; // 返回对 d1 的引用}
private:int _year;int _month;int _day;
};