C++类的默认成员函数

一、构造函数

在我们用C语言实现栈或队列等数据结构时,我们通常要写一个函数 Init() 来初始化它们,每次使用前都要调用一次,在我们使用的过程中容易忘记初始化导致程序错误,于是在C++中有了类的构造函数来帮助我们初始化编译器会在创建类对象时自动调用构造函数完成初始化,十分方便。

那么构造函数应该怎么定义呢?

1、构造函数的名字与类名相同

2、构造函数没有返回值

3、构造函数可以写多个构成重载

(就像有时我们只要一个空栈,有时需要用数组初始化而成的栈)

4、如果我们不写构造函数,那么编译器会自动生成一个默认的构造函数

      如果我们显式写了构造函数,编译器就不再默认生成,而是用我们写的构造函数初始化.

      编译器自动生成的默认构造函数对于内置类型会初始化,对于自定义类型会去调用它的构造函数初始化。 

5、我们写的无参数的构造函数、全缺省的构造函数、编译器默认生成的构造函数都叫默认构造函数,默认构造函数只能有一个。

6、默认构造函数都是不用传参数的构造,构造时不能加 () 。如下例所示 

7、构造函数一般要写在公有区域(public),如果写在私有(private)那么无法在类外创建类对象

8、默认构造函数不会为我们开辟空间,所以我们写类似顺序表类的类时,如果想初始化就开空间,要自己写构造函数。

 以日期类为例:

C++类的默认成员函数_第1张图片

运行结果:

 C++类的默认成员函数_第2张图片

也可以构成重载,结果与上面一样,无参调用默认构造函数,有参数调用第一个。 

我们可以写多个重载。

调用编译器自动生成的默认构造函数时:

 新写一个类date,对于自定义类型Date去调用它的构造函数,对于内置类型int默认初始化为随机值。C++类的默认成员函数_第3张图片

因此,如果我们有类的成员全是自定义类型,那么我们可以不用写构造函数,直接用默认的 

二、析构函数 

在C语言中,我们顺序表、链表等数据结构通常都要写一个Destroy()函数,帮助我们释放内存,为了避免很多人忘记写这个函数,C++类里面也添加了析构函数帮我们释放空间,就不用手动释放。 

析构函数和构造函数类似,一个初始化,一个销毁,都是默认成员函数。

析构函数的特点:

1、无参数、无返回值

2、函数名是 :~类名 

3、不能重载(因为无参数)

4、不写的话编译器也会生成默认的析构函数。

 (默认析构函数,只释放内置类型的资源,对于自定义类型,会去调用它的析构函数

5、因此,对于不需要手动释放空间的类(没有开辟空间),没必要写析构函数。

 日期类的析构函数: 在类生命周期结束后自动调用

因为我们没有手动开辟空间,所以析构函数里面没必要写,编译器会自动释放空间。

C++类的默认成员函数_第4张图片

 运行结果:

默认析构函数: 只释放内置类型的资源,对于自定义类型,会去调用它的析构函数

C++类的默认成员函数_第5张图片

析构函数析构的顺序:同一位置 (在相同的域创建) 后构造的先析构,是从后往前的顺序 

而全局变量与静态变量最后析构,它们的顺序也是后构造的先析构。

C++类的默认成员函数_第6张图片

 C++类的默认成员函数_第7张图片

三、拷贝构造 

拷贝构造是构造函数的重载,它的参数类型是它所属类的引用。

1、以Date类为例:Date(Date& d){//代码实现}

参数为什么用引用?

因为用Date的话会形成无限递归原因是如果不传引用,形参也是实参的拷贝,也要调用拷贝构造,调用拷贝构造又要拷贝实参....一直无限递归而用Date*没有Date&方便,所以就用Date&了

2、拷贝构造的使用方法:Date d1(d);   d1就成为了d的拷贝。

3、如果不写,编译器会生成默认的拷贝构造

(默认的拷贝构造函数也叫做浅拷贝或值拷贝,它只会把成员变量的值拷贝一份放在新的类中,对于内置类型会直接值拷贝,对于自定义类型会去调用它的拷贝构造)

4、与析构函数类似,对于没有开空间的类没必要写构造函数,编译器默认的就够了而对于有int*等通过手动开空间的类则必须要写因为默认的拷贝构造只会进行值拷贝,在析构时就会同一块空间释放两次。

如栈类假设里面有一个成员函数  int* _a;  需要开空间:Stack s1(s);

如果不写,s1._a = s._a ,析构函数运行时会free(s1._a)又会free(s._a)

两者值相等,也就是同一块空间释放两次,就会报错。  

5、类似上述栈类的类就要写拷贝构造,手动开辟一块空间,再把值拷贝进去,这样的拷贝也叫做深拷贝

默认拷贝构造:进行值拷贝

C++类的默认成员函数_第8张图片

C++类的默认成员函数_第9张图片

此时s._a与s1._a值相同,析构函数会同一块空间释放两次,然后报错。

 加上自己写的拷贝构造:

C++类的默认成员函数_第10张图片

 此时两者的地址不一样,完成了深拷贝,代码就成功运行了!

四、运算符重载 

对于内置类型如:int,我们有 int + int 、int - int 、 ++int、int == int 等等一系列运算符操作

那如果我们想要对日期类进行这些操作,如:两个日期相差多少天,d1-d2,日期是否相等

d1==d2等操作我们应该怎么做到呢,这时候就可以用运算符重载来实现。 

运算符重载:

1、使用方法:返回值  operator操作符(参数){//代码实现} 

      如:bool operator!=(const Date& d) {...};详情请看下面代码

2、运算符重载可以写在类里面,也可以写在类外面赋值运算符重载例外,它是默认成员函数,只能写在类里面)。

3、对于两个操作数的操作符如:+、- 等在类里面只有一个参数,类外有两个参数,因为运算符重载也是函数,类里面的函数默认第一个参数都是this指针,所以类里面在上层看只有一个参数 。

4、前置++与后置++的重载

      前置++是Date& operator++(){}

      后置++是Date operator++(int){} 

后置++之所以参数要带上int,是因为无法区分前置++与后置++,语法的规定用来辨别的

5、不可重载的五个运算符

      1、"." 点 取类成员的运算符 d._year

      2、"::"  域作用限定符 详情可以看我前面的命名空间文章哦

      3、".*" 点星 class A{ int* _a }; A a; a.*_a; 这个操作符用于取类里面的指针内容

      4、"sizeof" 看占多少空间 sizeof(int);

      5、"? : " 三目操作符  a == 0 ? a+1 : a-1;

放一个日期加减天数以及判断日期是否相等的重载,其余的类似实现即可。 

C++类的默认成员函数_第11张图片

前置++与后置++的实现,前置--与后置--类似 

C++类的默认成员函数_第12张图片 五、赋值运算符的重载

赋值运算符的重载与拷贝构造类似,只是使用形式不同

赋值运算符是 d1=d; 实现拷贝,拷贝构造是 d1(d);

1、赋值运算符重载也是默认成员函数,不自己写编译器会默认生成,与拷贝构造类似,但编译器默认生成的是浅拷贝或叫值拷贝,必要时可以自己完成深拷贝。

2、赋值运算符重载只能写在类里面(编译器才能判断你写没写,生不生成默认的) 

3、生成方法:以日期类为例,因为可能要连续赋值,所以返回引用

      Date& operator=(const Date& d){}详情请看下面代码实现哦。

4、其余特性参考上面的拷贝构造就可以了。

C++类的默认成员函数_第13张图片

 与拷贝构造类似,更多例子可以看看上面的拷贝构造哦

默认成员函数有六个,构造、析构、拷贝、赋值、取地址、const取地址

后两个一般直接用编译器默认生成的。

感谢大家观看!

你可能感兴趣的:(c++,开发语言)