各位好友,欢迎来到本期博客 !经过前几期的学习, 类 ~ 对象 ,正变得越来越有滋味 ! 如今,六大默认成员函数 --->还剩下两个重要的函数 --->拷贝复制 与 赋值重载
下面,开战 拷贝复制 !
----->拷贝构造函数:只有单个形参,该形参是对本类 类型对象的引用(常用 const 修饰)
意即,用 已经存在的类 -->类型对象创建对象时,由编译器自动调用 !
其实,前几次讲解 默认构造函数,最重要的还是其特性 !下面。来看看拷贝构造函数的特性 !
---->特性 :>
1. 拷贝构造函数是构造函数的一个重要的重载形式;
2. 拷贝构造函数的参数 有且只有一个 且必须是类 -->类型对象的引用,使用传值方式 编译器直接报错,因为会引发无穷递归调用 ;
3. 如果未显示定义,编译器会自动生成默认拷贝构造函数。默认拷贝构造函数对象 按照内存存储 以字节序完成拷贝
又称 -->浅拷贝,或者 值拷贝。
----->实现环节 --->初步探究:>
#include <iostream>
using std::cout;
using std::endl;
class Date
{
public:
Date(int year = 2018, int month = 06, int day = 07)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << " - " << _month << " - " << _day << " - " << endl;
}
//拷贝构造函数
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
d1.Print(); // 显然此处, Date 类 并没有显示定义,则编译器会生成默认构造函数 --->特性 3
Date d2(d1);
d2.Print(); // 此处是 拷贝构造函数的运用 与打印
Date d3(2023, 05, 07);
d3.Print(); // 该处 已经显示定义, 则编译器不会再生成默认构造函数
Date d5(d3);
d5,Print();
return 0;
}
为了方便好友们,有更好的观感体验,与更好的理解 !现附上有彩色的代码图样 :>
现 对上述部分 较为难以理解的点,进行讲解 !
----->以下是 正确用法:>
----->以下是 错误用法 :>
为了便于好友们,更加理解 与 掌握 上述代码 !以下采用递归展开图 -->进行讲解 :>
各位好友,上述话语中,已经有答案了 !😊不进行引用,而进行传值操作的话,会出现 无限递归的现象 !
----->如下:>
以上 形成的 无限递归展开图,就是传值造成的结果 !显然,避免上述情况,需要运用强大的引用 !
毕竟,引用的传入,不需要生成临时变量空间 !如果,部分好友 对引用,或传值,存在些许不理解 !
可自行查看前几期的博文,其实,这都是前几期重点梳理的 ! 那里,有你要找寻的答案 !😊
各位好友,一定要注意上述代码 对特性 3 指出的位置 !好方便理解 !有关特性 3 描述 --->无彩色的代码环节 !
对于 上述程序,还需要指明一点,请注意:>
在编译器生成的默认拷贝构造函数中, 内置类型是 按照字节方式直接拷贝, 而自定义类型是 调用其拷贝构造函数完成拷贝 !
各位好友,以下还有两点需要提及:>
a. 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了 !经由上述 --->日期类的显示实现,即使不书写定义的时候,编译器也会自动调用拷贝构造函数 !
对于 栈区 --->能否实现浅拷贝构造呢?是否 栈区的显示实现 也能经由编译器自动生成呢?
----->以下是探究过程:>
#include <iostream>
using std::cout;
using std::endl;
typedef int DateType;
class Stack
{
public:
Stack(int capacity = 6)
{
_a = (DateType*)malloc(sizeof(DateType) * capacity);
if(_a == nullptr)
{
perror("malloc::fail");
return ;
}
_capacity = capacity;
top = 0;
}
void Push(DateType x)
{
// 检查容量
// CheckCapacity();
_a[top++] = x;
}
~Stack()
{
free(_a);
_a = nullptr;
_capacity = 0;
top = 0;
}
private:
DateType* _a;
int _capacity;
int top;
};
int main()
{
Stack s;
s.Push(12);
s.Push(16);
s.Push(17);
s.Push(19);
s.Push(21);
return 0;
}
以上便是 栈区能否实现 --->编译器自己生成 显示实现 !
----->答案:> 程序会崩溃 !! 出于 对 VS 2019 编译器的保护,在这里就不演示了 !敬请谅解 !!🌹🌹
以下是 方便好友们,有更好的观感,与理解 !现 附上有彩色的代码图样 :>
出于 希望好友们 能从本篇博客中,学到更多新知识点的考量 !虽然 不进行运行结果的显示了,但是 核心本质 还是要解析一下的 !希望好友们,能加深对此处的理解,光靠文字说明,无法达到 --->理想的效果 !
----->解析:>
---->解析:>
1. s 对象会调用构造函数进行自身创建,在构造函数中申请了 6 个元素的空间;然后 又在里面存储了 5 个元素
21 19 17 16 12
2. 对象_s -->对象 s 进行拷贝构造,而 Stack 类没有显示定义的拷贝构造函数,则编译器会给 Stack 类自动生成一个默认拷贝构造函数; 又 默认拷贝构造函数是按值进行拷贝的, 即 将对象 s 中的内容原封不动的拷贝到 _s 中 。因此,对象 s 与 对象 _s 指向了同一块内存空间。
3. 当程序退出的时候, 对象_s 与 对象s 都要进行销毁 !但是,会有一个先后顺序,即 对象_s 先销毁,销毁的时候,自然会调用析构函数,此时将 0x88889858 地址所在的空间释放掉了 !但是 对象 s 并不知道,二者又是指向的同一块空间, 到了销毁对象 s 时,会将 0x88889858 地址所在的空间再一次释放掉 !同一块内存空间连续多次释放,自然,,系统会抗不住的 😊从而造成 --->程序崩溃 !
----->注意:>
类中 如果没有涉及到资源申请,拷贝构造函数是否写出都可以; 一旦涉及到资源申请,拷贝构造函数是一定要书写的,否则就是浅拷贝 !
b. 拷贝构造函数典型应用场景 :>
(1)使用已经存在的对象创建新的对象;
(2)函数参数类型为 类 -->类型对象;
(3)函数的返回值类型为 类 --->类型对象。
以下展示代码,进行实现 :>
#include <iostream>
using std::cout;
using std::endl;
class Date
{
public:
// 初始化函数, 又名 构造函数 ----->此处,不清楚的好友,可以自行查看前几期的博文
Date(int year, int month, int day)
{
cout << "Date(int year, int month, int day):>" << this << endl << endl;
}
// 本期重点:> 一直在讲解的拷贝构造函数
Date(const Date& d)
{
cout << "Date(const Date& d):>" << this << endl << endl;
}
private:
int _year;
int _month;
int _day;
};
// 精彩点 -->用存在的类对象 创建 新的对象
Date Test(Date d)
{
Date tamp(d);
return tamp;
}
int main()
{
Date _d(2018, 6, 7);
Test(_d);
return 0;
}
为了方便好友们,有更好的观感体验,与更好的理解 !现附上有彩色的代码图样 :>
----->测试环节:>
----->运行结果 :>
现在,对上述部分进行解析,其中,有好多是难以理解的知识点 !还有那至关重要的 逻辑过程 !
--->难点 -->精华 :>
其实,上述也有两大比较:传值 与 传引用 !--->效率最高 --->传引用 !
--->盲点 :>
各位好友,请注意上述红色框框 ! “this” 指针 --->内置版的隐藏指针 ,是指向不同类型对象所在的地址 !
至此,本模块下,拷贝构造函数 已讲解完成 !希望,各位好友能够 -->得到些 新的体验 与收获 !
下一期,开战 -->赋值运算重载函数 !那可是一块 “骨头汤” 啊 ! 是有一定难度的 !但是 更有营养 !尤其是 重载运算符(operator)引入 !
接下来将会进行 --->日期类的运算 !估计会有两篇博客写就 !又一次到了 说再会的时刻了 !
未来两期的博文内容,将有一定的挑战性 !估计,将会有 两到三天时间 用来打磨 !只希望 内容更加通俗易懂,便于学习 与理解 !终于,最后一个 -->重要的默认构造函数 就要来了 !😊😊
接下来, 这座小高山, 攀登顶部 -->风景肯定会非常美丽!而 C++ 魅力 也将得到再一次的体现 !
期待,各位好友,下一期,再相会 !😊😊