洛阳直播网站建设,宣传推广的作用,wordpress 重装教程视频教程,欧美网站设计文章目录 拷贝构造函数特点分析拷贝构造函数情景 赋值运算符重载运算符重载operator运算符重载 赋值运算符前置和后置重载 拷贝构造函数
在创建对象的时候#xff0c;是不是存在一种函数#xff0c;使得能创建一个于已经存在的对象一模一样的新对象#xff0c;那么接下… 文章目录 拷贝构造函数特点分析拷贝构造函数情景 赋值运算符重载运算符重载operator运算符重载 赋值运算符前置和后置重载 拷贝构造函数
在创建对象的时候是不是存在一种函数使得能创建一个于已经存在的对象一模一样的新对象那么接下来让我们一起去了解一下拷贝构造函数
特点
拷贝构造函数也算是特殊的成员函数特征如下 拷贝构造函数是构造函数的重载拷贝构造函数的参数只有一个,且必须是类类型对象的引用一般常用const修饰在用已存在的类类型对象创建新对象时由编译器自动调用使用传值的方法是会报错,因为会引发无穷的递归调用若没有显示定义编译器会生成默认的拷贝构造函数默认的拷贝构造函数对象按照内存存储按字节序完成拷贝这种拷贝叫做浅拷贝或值拷贝。 我们以日期类为例:
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;
}所以当拷贝构造函数的的调用要用传引用因为传引用的话就不需要拷贝也就是说能调用并进入拷贝构造函数当没有的时候形参进入就需要拷贝就会调用拷贝构造函数不进去在形参处就需要继续拷贝从而无穷递归使得编译器报错
对于默认拷贝构造函数的使用下面代码演示
class Date
{
public:Date(int year 1900, 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(d1);return 0;
}内置类型成员完成浅拷贝or值拷贝自定义类型成员会调用他的拷贝构造 举例说明
//1.全是内置成员变量
class Person{public:Person(int data,int age){_datadata;_ageage;}void Print(){cout_data _ageendl;}int _data;int _age;
};
//这个会调用默认构造函数
int main(){Person p1(10,20);Person p2(p1);
}
//2.都是自定义函数或者说是包括自定义函数可以使用浅拷贝因为我们需要的就是值的传递但是对于这样一种需要自行开辟空间如malloc的自定义类型Stack是需要进行深拷贝的就是需要显示拷贝构造函数 原因如下 以Stack为例里面malloc一个空间作为动态内存的开辟在实例化Stack对象之后我们会自动构造这个Stack最后用完这个对象之后会进行销毁~Stack析构函数里面是由free的如果说我们使用默认的拷贝构造函数之后两个对象是指向同一空间的但是只能free一次所以在第二次free的时候就会报错 所以对于这样的拥有动态内存的自定义类型需要我们来进行深拷贝自定义拷贝构造函数 总结 类中如果没有涉及资源申请时拷贝构造函数是否写都可以一旦涉及到资源申请时则拷贝构造函数是一定要写的否则就是浅拷贝。 分析拷贝构造函数情景
由下面的代码进行演示在由拷贝构造函数的情况下的程序运行步骤
class Person{public:Person(int data){coutPerson构造函数thisendl;}Person(const Person p){cout拷贝构造函数thisendl;}~person(){coutPerson析构函数thisendl;}int data;
};Person Test(Person p){Person tmp(p);return tmp;
}
int main()
{Person p(10);Test(p);return 0;
}为了提高程序效率一般对象传参时尽量使用引用类型返回时根据实际场景能用引用 尽量使用引用。
赋值运算符重载
赋值运算符重载可以使得自定义类型也可以像内置类型一样进行运算符运算以下所有的运算符重载都是以Date类为例
运算符重载
C为了增强代码的可读性引入了运算符重载运算符重载是具有特殊函数名的函数也具有返回值类型哈桑函数名字一级参数列表其返回值类型于参数列表于普通函数类似
格式返回值类型 operator操作符参数列表
函数名为operator需要重载的运算符符号 注意事项 1.不能通过连接其他符号来创建新的操作符比如operator需要是运算符不是任意符号 2.重载操作符必须有一个类类型参数因为我们就是对于自定义类型进行运算符运算类似于内置类型 3.用于内置类型的运算符其含义不能改变例如内置的整型不能改变其含义作为类成员函数重载时其形参看起来比操作数数目少1因为成员函数的第一个参数为隐藏的this 4.**.* :: sizeof ?: . ** 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。 //具体的操作如下 以Date为例、
class Date
{
public:Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}//private:int _year;int _month;int _day;
};
// 这里会发现运算符重载成全局的就需要成员变量是公有的那么问题来了封装性如何保证
对于封装的问题我们这里只能先使用public来解决后面学到友元之后就可以不用public
// 这里其实可以用我们后面学习的友元解决或者干脆重载成成员函数。
bool operator(const Date d1, const Date d2)
{return d1._year d2._year d1._month d2._month d1._day d2._day;
}void Test()
{Date d1(2018, 9, 26);Date d2(2018, 9, 27);cout (d1 d2) endl;
}
我们当然可以例如上述代码一样将operatorDate在类外面定义和声明但是这样的话我们如果不用友元的话就只能将成员变量权限改为public所以我们的方法为将运算符重载放在Date里面
class Date
{
public:Date(int year 1900, int month 1, int day 1){_year year;_month month;_day day;}bool operator(const Date d2){return _year d2._year _month d2._month _day d2._day;}
private:int _year;int _month;int _day;
};//bool operator(const Date d1, const Date d2)
//{
// return d1._year d2._year
// d1._month d2._month
// d1._day d2._day;
//}
void Test()
{Date d1(2018, 9, 26);Date d2(2018, 9, 27);cout d1.operator(d2) endl;cout (d1 d2) endl;
}
int main()
{Test();return 0;
}bool operator(const Date d2){return _year d2._year _month d2._month _day d2._day;}
//所以左操作数就是this指针右操作为形参operator运算符重载
下面代码进行演示
//主要进行演示流程
class Person{public:Person(int age){_ageage;}bool operator(const Person p){if(_agep.age) return true;else return false;}bool operator(const Person p){if(_agep._age) return true;else return false;}bool operator(const Person p){return (*this p) (*this p);}int _age;
};
int main()
{Person P1(10);Person P2(20);cout (P1 P2) endl;return 0;
}为了提高服用率我们通常将利用其他运算符重载来实现我们需要的运算符重载 bool operator(const Person p){if(_agep.age) return true;else return false;}
//只需要知道一个 一个 就能得到所有的比较大小的运算符重载bool operator(const Person p){if(_agep._age) return true;else return false;}bool operator(const Person p){return (*this p) (*this p);}
//我们一定注意的是运算符重载使用的的传引用这样可以提高效率赋值运算符
赋值运算符为operator这个运算符重载有一些细节要求 赋值运算符重载的格式 参数类型const T传递引用可以提高传参效率和上述保持一致返回值类型T返回引用可以提高返回的效率有返回值目的是为了支持连续赋值检测是否自己给自己赋值返回*this 要复合连续赋值的含义 //首先我们来看内置类型赋值的情况
int main()
{int n10;n20;int i,j,k;ijk0;//连续赋值0先给k返回的结果给了j再给了i以至于ijk//所以我们需要的返回值类型应该为类类型 例如Person//又因为传引用返回可以提高效率少一次拷贝构造返回所以我们选择的是类类型 如Personcoutnendl;//输出结果为n 这个会改变n的数值return 0;
}所以我们自定义类型的赋值运算符重载也要这样所以我们应该这样做
class Person{public:Person(int age){_ageage;}Person operator(const Person p){if(*this!p){//如果两个对象不是同一个对象_agep._age;//进行赋值}return *this;//这个this的生命周期不在类里面所以是可以传引用返回}int _age;
};
int main()
{Person p1(10);Person p2(20);coutp1p2endl;return 0;
}赋值运算符只能重载成类的成员函数不能重载成全局函数 Vs2022版本的编译器对于全局的赋值运算符重载会直接编译报错并提示operator必须是成员函数
这是因为我们规定返回类型为传引用但是如果是全局函数那么左值的生命周期在调用完函数之后把就结束的所以这个地方也有问题且“operator ”必须是非静态成员
原因解释 赋值运算符如果不显式实现编译器会生成一个默认的此时用户再在类外自己实现一个全局的赋值运算符重载就和编译器在类中生成的默认赋值运算符重载冲突了故赋值运算符重载只能是类的成员函数。 也就是说这个实际上就是存在默认的赋值运算符重载所以才不能用于全局函数
用户没有显式实现时编译器会生成一个默认赋值运算符重载以值的方式逐字节拷贝。注意内置类型成员变量是直接赋值的而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。 总结 对于赋值运算符重载在没有自定义的时候编译器会自动提供默认赋值运算符重载且这个默认函数是值拷贝也就是浅拷贝的内置类型成员变量是直接赋值的而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。对于类似于栈Stack这样的类是需要自定义的但是对于Date或者上述Person类是不需要的使用默认的即可如果类中未涉及到资源管理赋值运算符是否实现都可以一旦涉及到资源管理则必须要实现。 前置和后置重载
我们先看内置类型前置和后置的区别
int main()
{int n10;int an;//后这一行n还是为10 这个代码结束 后 n为11int bn;//先这一行n就为11couta bendl;return 0;
}所以自定义类型也需要按照内置类型的方式来实现前置后置
class Person {
public:void Print() {cout _age endl;}Person(int age) {_age age;}//我们规定前置为无参Person operator() {(*this)._age;return *this;}//后置 括号使用这个无参类型表示Person operator(int) {Person tmp *this;(*this)._age1;return tmp;//返回临时变量所以不能传引用返回}int _age;
};
int main()
{Person p(10);cout p._age endl;p;p.Print();cout p._age endl;p;cout p._age endl;return 0;
}