个人flash网站源码,仿糗事百科网站源码dede二次开发分享+评论+互动,wordpress反代cdn,多用户旅游网站开发拷贝构造函数 前言一、拷贝构造函数概念理解定义 二、拷贝构造函数的特征三、注意要点写法实践传址返回与引用返回的区别传址返回引用返回 传值返回和传址返回的对比总结测试 前言
类的6个默认成员函数#xff1a;如果一个类中什么成员都没有#xff0c;简称为空类。
空类中… 拷贝构造函数 前言一、拷贝构造函数概念理解定义 二、拷贝构造函数的特征三、注意要点写法实践传址返回与引用返回的区别传址返回引用返回 传值返回和传址返回的对比总结测试 前言
类的6个默认成员函数如果一个类中什么成员都没有简称为空类。
空类中真的什么都没有吗并不是任何类在什么都不写时编译器会自动生成以下6个默认成员函数。
默认成员函数用户没有显式实现编译器会生成的成员函数称为默认成员函数。
class Date {};一、拷贝构造函数概念
理解
在现实生活中可能存在一个与你一样的自己我们称其为双胞胎。 那在创建对象时可否创建一个与已存在对象一某一样的新对象呢
定义
拷贝构造函数只有单个形参该形参是对本类类型对象的引用(一般常用const修饰)在用已存在的类类型对象创建新对象时由编译器自动调用。
C拷贝构造函数是一种特殊的构造函数用于创建对象时使用一个已有对象的内容来初始化新的对象。它接受一个同类对象作为参数并按照该对象的数据成员的值来创建新的对象。
拷贝构造函数通常用于以下情况
在创建对象时使用同类已有对象的值来初始化新对象。以值传递方式将对象传递给函数。以值返回方式从函数返回对象。
拷贝构造函数的定义形式为 类名(const 类名obj)
{// 构造函数的实现
}其中类名是要创建的对象的类名obj是要拷贝的对象。
拷贝构造函数的工作原理是将obj的数据成员的值复制给新创建的对象。这意味着新对象的数据成员会与原对象具有相同的值但是它们是独立的改变其中一个对象的数据成员的值不会影响另一个对象的数据成员。
如果没有显式定义拷贝构造函数编译器会提供一个默认的拷贝构造函数。默认的拷贝构造函数执行的是浅拷贝即简单地将原对象的值复制给新对象的数据成员。如果类中包含指针类型的数据成员需要自己定义拷贝构造函数进行深拷贝确保指针指向的对象也被复制。
注意拷贝构造函数是类成员函数通常定义在类的公有部分。拷贝构造函数是通过对象名来调用的而不是通过函数名来调用。
二、拷贝构造函数的特征
拷贝构造函数也是特殊的成员函数其特征如下
拷贝构造函数是构造函数的一个重载形式。拷贝构造函数的参数只有一个且必须是类类型对象的引用使用传值方式编译器直接报错因为会引发无穷递归调用。
class Date
{
public:Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}// Date(const Date d) // 正确写法Date(const Date d) // 错误写法编译报错会引发无穷递归{_year d._year;_month d._month;_day d._day;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;Date d2(d1);return 0;
}3. 若未显式定义编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝这种拷贝叫做浅拷贝或者值拷贝。 和构造函数不一样构造函数内置类型不会初始化拷贝构造函数会初始化
class Time
{
public:Time(){_hour 1;_minute 1;_second 1;}Time(const Time t){_hour t._hour;_minute t._minute;_second t._second;cout Time::Time(const Time) endl;}
private:int _hour;int _minute;int _second;
};
class Date
{
private:// 基本类型(内置类型)int _year 1970;int _month 1;int _day 1;// 自定义类型Time _t;
};
int main()
{Date d1;// 用已经存在的d1拷贝构造d2此处会调用Date类的拷贝构造函数// 但Date类并没有显式定义拷贝构造函数则编译器会给Date类生成一个默认的拷贝构造函数Date d2(d1);return 0;
}注意在编译器生成的默认拷贝构造函数中内置类型是按照字节方式直接拷贝的而自定义类型是调用其拷贝构造函数完成拷贝的。
编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了还需要自己显式实现吗 当然像日期类这样的类是没必要的。那么下面的类呢验证一下试试
// 这里会发现下面的程序会崩溃掉这里就需要深拷贝去解决。
typedef int DataType;
class Stack
{
public:Stack(size_t capacity 10){_array (DataType*)malloc(capacity * sizeof(DataType));if (nullptr _array){perror(malloc申请空间失败);return;}_size 0;_capacity capacity;}void Push(const DataType data){// CheckCapacity();_array[_size] data;_size;}~Stack(){if (_array){free(_array);_array nullptr;_capacity 0;_size 0;}}
private:DataType* _array;size_t _size;size_t _capacity;
};
int main()
{Stack s1;s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);Stack s2(s1);return 0;
}注意类中如果没有涉及资源申请时拷贝构造函数是否写都可以一旦涉及到资源申请时则拷贝构造函数是一定要写的否则就是浅拷贝。
拷贝构造函数典型调用场景 使用已存在对象创建新对象函数参数类型为类类型对象函数返回值类型为类类型对象
class Date
{
public:Date(int year, int minute, int day){cout Date(int,int,int): this endl;}Date(const Date d){cout Date(const Date d): this endl;}~Date(){cout ~Date(): this endl;}
private:int _year;int _month;int _day;
};
Date Test(Date d)
{Date temp(d);return temp;
}
int main()
{Date d1(2022, 1, 13);Test(d1);return 0;
}为了提高程序效率一般对象传参时尽量使用引用类型返回时根据实际场景能用引用尽量使用引用。
三、注意要点
写法
class Date
{
public:Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}// Date(const Date d) // 正确写法Date(const Date d) // 错误写法编译报错会引发无穷递归{_year d._year;_month d._month;_day d._day;}
private:int _year;int _month;int _day;
};
int main()
{Date d1;Date d2(d1);return 0;
}除了下面这种写法外
Data d2(d1); 我们还可以写成这种写法也是拷贝构造
Data d2 d1;实践
如果没有管理资源一般情况下不需要写拷贝构造函数默认生成的拷贝构造函数就可以。如Date日期类如果都是自定义类型成员内置类型成员没有指向资源也类似默认生成的拷贝构造函数就可以。如MyQueue一般情况下不需要显示写析构函数就不需要写拷贝构造函数如果内部有指针或者一些值指向资源需要显示写析构释放通常就需要显示写构造函数完成深拷贝。如Stack Queue List等
传址返回与引用返回的区别
关于下面代码的展示VS2022编译器可能显示不出来因为编译器等级比较高像下面的情况编译器会自行优化使代码运行效率更高致使本来的结果显示不出来。
传址返回
传址返回会生成Date d 的临时拷贝进行返回
引用返回
引用返回会直接返回Date d的地址而不会进入临时拷贝
~Date(){cout ~Date() endl;_year -1;_month -1;_day -1;}出了函数作用域之后会调用析构函数致使结果为-1此时的ref类似野指针 依次类推我们再定义一个fx()函数在fx函数的空间里存放一些变量ret空间里的内容会被fx函数里的内容给覆盖 当出了作用域返回对象还在没有析构那就可以用引用返回减少拷贝比如用static修饰
传值返回和传址返回的对比
Date operator运用的下篇文章赋值运算符重载可以看到传值和传址在遇到不同问题时有不同的表现如下在运算符重载的问题下传址调用比传值调用的效率更高
总结
返回对象是一个局部对象或临时对象出了当前func函数作用域就析构销毁了那么不能用引用返回用引用返回时存在风险的因为引用对象在func函数栈帧已经销毁了
虽然引用返回可以减少一次拷贝但是出了函数作用域返回对象还在才能用引用返回
即下述情况
返回对象生命周期到了会析构传值返回返回对象生命周期没到不会析构传引用返回
测试
#includeiostreamusing namespace std;class Date
{
public:Date(int year 1900, int month 1, int day 1){cout Date(int,int,int): this endl;_year year;_month month;_day day;}Date(const Date d){cout Date(const Date d)this endl;_year d._year;_month d._month;_day d._day;}~Date(){cout ~Date() this endl;}
private:int _year;int _month;int _day;
};
Date func(Date d)
{Date temp(d);return temp;
}
//Date fun()
//{
// Date d(2024,4,14);
// return d;
//}
int main()
{Date d;func(d);//const Date ret func();return 0;
}
//class Date
//{
//public:
// Date(int year, int minute, int day)
// {
// cout Date(int,int,int): this endl;
// }
// Date(const Date d)
// {
// cout Date(const Date d): this endl;
// }
// ~Date()
// {
// cout ~Date(): this endl;
// }
//private:
// int _year;
// int _month;
// int _day;
//};
//Date Test(Date d)
//{
// Date temp(d);
// return temp;
//}
//int main()
//{
// Date d1(2022, 1, 13);
// Test(d1);
// return 0;
//}