类和对象(中)

类和对象(中)

  1. 类的六个默认成员函数

默认成员函数:用户没有显示实现,编译器会生成的函数称为默认成员函数。

如果显示实现了,那么就不会自动生成了

如果一个类里面什么都没有,我们称之为空类。

空类中并不是真的什么都没有,任何类在什么都不写时,编译器会自动生成六个默认成员函数。

(1). 构造函数:

构造函数是特殊的成员函数。

注意: 构造函数虽然名为构造,但是构造函数的主要任务并不是开辟空间创造对象,而是初始化对象

其特征如下:

  1. 函数名和类名相同
  2. 没有返回值(返回值连void都没有)
  3. 对象实例化时编译器自动调用对应的构造函数
  4. 构造函数可以重载
  5. 默认构造函数只能存在一个

Eg:

写过之后,那么在main函数中,不调用Date.Init(); 也会用Date()自动初始化

内置类型/基本类型: char/int/double/…/指针

自定义类型: struct / class /…

C++规定:编译器自动生成构造函数,对内置类型成员变量不作处理。对于自定义类型成员变量会调用该成员变量的类的默认构造函数来初始化该成员。

实质上无参构造函数(无论是默认的还是自定义的,自定义的无参构造函数是可以自己决定对那些资源进行初始化的)会初始化自定义类型成员变量(初始化自定义类型的成员变量是通过调用该成员变量的类的默认构造来初始化的),而不会对内置类型成员变量进行处理。

构造函数函数带参时的调用:函数名 对象名(参数); 

此处main函数里的 A a1(19); 就是创建对象a1,并执行A(int n)

Eg

值得注意的是如果类中的构造函数都是含参的,那么A a2; 就会报错原因是当我们显式定义了构造函数后,编译器就不会再自己生产默认的构造函数了,而是使用显示定义处的构造函数。 而 A  a2; 这种是会调用构造函数A() , 但只显式定义了A(int n); 那么就会运行错误,原因是a2没有可调用的构造函数

Eg:

注意:调用构造函数A() 时,只能写为A a2; 不能写为 A a2();

一般情况下,建议每个类,都写一个全缺省的的构造函数。原因是因为好用 

默认构造函数:无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造只能有一个。

注意:无参构造函数(包括显式定义的无参构造函数),全缺省构造函数(包括显式定义的全缺省构造函数),我们没有写但编译器默认生成的构造函数,都可以认为是默认构造函数

此处的 _t 调用的是 Time类的默认构造函数来初始化_t;

为了避免调用了编译器自动生成而使得内置类型没有被处理而生成随机值。

选择了打补丁

即:

给内置类型的成员变量缺省值。   若该类有显式的构造函数且对内置类型进行初始化,那么内置类型的成员是先走缺省值然后再进入构造函数中,对其进行初始化(实质上是覆盖)

总结:

  1. 一般情况下,构造函数都需要我们显式的去实现
  2. 只有少数的情况下可以让编译器自动生成构造函数。类似:MyQueue里的成员全是自定义类型,因此不需要

(2)析构函数:

构造函数是特殊的成员函数。

注意:与构造函数相反,析构函数不是完成对对象本身的销毁,局部对象的销毁工作是由编译器完成的。而对象中销毁时会自动调用析构函数,完成对象中资源的销毁工作。

其特性如下:

  1. 析构函数名是在类名前加上字符 ~
  2. 无参数也无返回类型
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成一个默认的析构函数。注意:析构函数不能重载
  4. 对象生命周期结束时,C++编译系统 系统自动调用析构函数
  5. 系统自己生成的析构函数 不对内置类型成员进行处理,自定义类型成员调用其自己的析构函数

析构也可以显式调用(但并不常用,需要显式定义析构函数的时候是否规范,有的调用两次会出错)

Stack st;

st.~Stack;

实践中总结:

  1. 有资源需要显式清理的时候,需要写析构。 例如: Stack , List
  2. 有两种情况是不需要写析构的。

(1). 没有资源需要清理。 例如: Date  即时间,里面都是内置类型成员

(2). 内置类型成员没有资源需要清理,剩下的都是自定义类型。例如:MyQueue

拷贝构造函数: 拷贝构造是一种特殊的构造函数。 本质是用同类型的对象进行拷贝初始化

注意:自定义类型当作参数进行传递时,实质上是调用拷贝构造,拷贝到临时变量里,然后赋值给形参,然后在函数中运用。 即当函数的参数类型为 类 类型的时候,传参要调用该类的拷贝构造。

调用: Date d3(d2);  此处d2是已经定义过的.

         也可以写为  Date d3 = d2; 这也是调用拷贝构造

特性:

  1. 拷贝构造是构造函数的一种重载
  2. 拷贝构造的参数只有一个必须是 类 类型对象的引用,使用传参方式编译器直接报错,因为会引发无穷递归调用。(因为参数类型如果不是引用,而是类 类型的话,在传参时要调用拷贝构造,而拷贝构造的参数是类 类型,调用的时候传参需要调用拷贝构造,因此调用拷贝构造要调用拷贝构造,即形成无穷递归调用)

Eg:

为了规避在写的时候 出现 d.year = _year; 的错误情况。拷贝构造的参数常为const 类& 自定义形参名。

拷贝构造中也有this 。 此处的this是d3

Eg:

  1. 若未显式定义,编译器会生成默认的拷贝构造函数。对于内置类型成员:默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。(因此不能拷贝指向数组的指针,也不能拷贝链表等等。 因为拷贝过去后,两个对象指向了同一个空间,程序结束时,两个对象都调用了析构函数,对同一个空间销毁了两次,导致程序崩溃), 自定义类型成员是调用该成员的拷贝构造进行拷贝

总结:

  1. 如果没有资源管理,一般不需要写拷贝构造,默认生成的拷贝构造就可以。例如: Date
  2. 如果都是自定义类型,内置类型没有指向资源,也类似默认生成的拷贝构造就可以。例如:MyQueue
  3. 一般情况下,不需要写析构函数,就不需要写拷贝构造
  4. 如果内存有指针或者一些值 指向资源,需要显式写析构释放,通常就需要显式写构造完成深拷贝。如:Stack Queue, List 等等

运算符重载:

1.Operator关键字: operator+运算符可以组成函数名

Eg:

Bool operator< (const Date& la , const Date& lb)

{

    …

};

此后比较 Date 类型就可以用 < 进行比较

然后再定义一个

Bool operator> (const Date& la ,const Date& lb)

{
    …

};

此后比较Date类型就可以用 < 和 > 进行比较了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值