C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载

C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载_第1张图片

目录

C++ 的函数传参 原理

简单介绍:

拷贝构造

概念: 

特征:

书写问题的解释:

传引用 &

 const

浅拷贝、深拷贝

运算符重载

概念: 

特点:

主函数部分调用写法:

 小细节:


C++ 的函数传参 原理

简单介绍:

在C++ 中 函数的 赋值 分为三个步骤 :

  1. 编译器自动生成临时变量
  2. 将需要的数值交给 编译器内部自动生成的 临时变量
  3. 被赋予数值的 变量从 临时变量中获取数值

C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载_第2张图片

这种赋值方式是C++语法中的一种规则,且 编译器自动生成的 临时变量 是具有常量性的,也就说 被赋予的 数值不能够被改变 ,也是因为这种赋值的方式和 & 取别名符号的语法和使用方式起冲突,于是就产生了另一种关键字const 

 int a = 0 ;

 int & b = a;
 
 b++;

如上代码所示,将a取了个别名b,同时b++,因为a和b本质上是同一个东西,所以b++的同时也就相当于是a++,但是如果想要b++呢?

const int & c = a;

如代码所示,c是a的别名,但是c不能修改a,换句话说,c变动了a不会变动,相当于一种拷贝复制。

C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载_第3张图片

错误

 正确

 换句专业的术语,我们可以叫他权限缩小,同时用了const的修饰后的变量不能取别名,这种叫权限放大。

拷贝构造

概念: 

只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存 在的类类型对象创建新对象时由编译器自动调用

 最后,拷贝构造本质上也是一种构造函数,当拷贝构造存在于类中时,默认的拷贝构造和默认的构造函数都不会存在!

特征:

1.拷贝构造函数是构造函数的一个重载形式。

2.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。

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;
}

书写问题的解释:

可以从上面的代码得知,拷贝构造的写法和构造函数的写法类似,函数名和类名一样,但不同的是函数的参数。

对于拷贝构造而言,加上const 和 & 是必须的行为,否则编译器会报错或者说会产生野指针。

传引用 &

C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载_第4张图片

在C++的语法中规定了任何自定义类型的函数,在进行函数传参传值的同时,会调用拷贝构造函数。

就如上图所示,调用了  func1 并在func1中传递了 自定义类型变量 d1 ,而在传递的同时 ,d1 内部的成员变量会进入到拷贝构造函数Date中,由Date 进行拷贝后 ,将拷贝的数值传递给 函数 func1,在func1 进行了函数内部的运行后,在把返回值交给了 主函数的 func1

这个步骤看似复杂,实际上解决了C语言中的传参内部的一些漏洞和缺陷,不过也正是因为 拷贝构造函数,我们更应该使用 传引用 ,而不是传值。

C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载_第5张图片

又如上图所示,如果进行传值,那么在传值交给拷贝函数的同时,在逻辑上也相当于是传值调用了拷贝构造函数,而C++的传值原理就是要把传递的数值先给到构造函数。

于是乎就有了一种现象,一个数值在传递给拷贝构造之前,要先进入另一个拷贝构造函数内部进行拷贝,然后传回返回值,但是在进入另一个拷贝构造函数前,上面的现象又会出现,而这种现象就是无穷递归。

而为了解决这种现象,C++语法规定了,自定义类型传值的同时最好传引用! 

 const

C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载_第6张图片

如图所示,在进行拷贝数值时,会很容易将自己的数值拷贝到另一个地方后,自己便清空了数值,也就说被改变了,所以为了防止这种改变,在传递上需要加上const 

浅拷贝、深拷贝

在拷贝构造函数中分为浅拷贝和深拷贝,二者的区别就是浅拷贝可以由编译器自动生成,但浅拷贝就是将需要拷贝的内容完完整整的复制一遍,而深拷贝一般由程序员自己手动进行调整,同时,深拷贝也可以由调用其他自定义类型中的深拷贝生成。 

同时,浅拷贝一般应用于简单的函数调用,而深拷贝一般应用于需要开辟空间的函数中

C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载_第7张图片

如图所示,这是应用了浅拷贝的栈,st2是st1的拷贝,但是呢,st2把st1中的指针拷贝下来了导致二者指向的是同一个空间,这就会导致最后销毁的时候销毁了st2 ,st1后销毁,但是st2在销毁时就把空间释放了,导致st1指向的空间没了st1变成了野指针,而st1在之后的操作中就会崩溃 

C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载_第8张图片

而对于这种问题就需要使用深拷贝,在深拷贝构造函数的内部写下开辟空间和指向空间的代码。

Stack(const Stack& s)
 {
 DataType* tmp = (DataType*)malloc(s._capacity *(sizeof(DataType)));
 if (tmp == nullptr)
 {
 perror("malloc fail");
 exit(-1);
 }

 memcpy(tmp, s._array, sizeof(DataType) * s._size);

 _array = tmp;
 _size = s._size;
 _capacity = s._capacity;
}

同时拷贝构造函数因为是特殊的构造函数,所以在编译器生成默认的拷贝构造函数中,它对是内置类型进行返回默认值的,同时对于自定义类型的变量回去寻找这个自定义类型的默认拷贝构造函数。 

C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载_第9张图片

这里的MyQueue的深拷贝是调用了stack的深拷贝,因为MyQueue里面两个都是自定义类型stack的成员变量,所以调用了它们的深拷贝

运算符重载

概念: 

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其 返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。 

C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载_第10张图片

如上图所示,这是一个比较的函数,但是因为函数名较为的复杂,在大型的项目中想要修改和得知其中的内容可能需要进行翻找,而运算符重载函数则解决了这个问题。

C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载_第11张图片

使用了 operator 关键字 配合运算符构成了一个易于识别大小加减乘除运算的一个显眼的函数名。

特点:

1.不能通过连接其他符号来创建新的操作符:比如operator@

2.重载操作符必须有一个类类型参数

3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义

4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐 藏的this

5 .    .* :: sizeof ?: . 注意以上5个运算符不能重载。 

主函数部分调用写法:

对于主函数部分进行使用时的写法有两种,如下图所示。 

C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载_第12张图片 

 第二种用法的原因是编译器帮我们传递了隐藏的operator

 小细节:

需要注意一下,如果需要使用成员变量,最好将成员变量变成公开的,或者说如果希望成员变量是私有的,那么可以将比较函数写在类中,且对比较函数的参数进行修改!

C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载_第13张图片

和之前的对比函数的差别是,const Date & x 消失了,是因为类的内部有一个this指针指向了const Date & x 所在的空间,所以不需要了。


C++ : 类的简单介绍(五)————— 拷贝构造函数 & 函数传参 & 运算符重载_第14张图片 

你可能感兴趣的:(C++,类,c++初阶知识,C++,c++,开发语言)