推广 热搜: 行业  设备    系统  参数  经纪    教师  机械  中国 

C++初阶—类与对象(中篇)

   日期:2024-12-11     作者:iyz7x    caijiyuan   评论:0    移动:http://mip.xhstdz.com/mobile/news/11123.html
核心提示:如果一个类中什么成员都没有,简称为空类。 空类中真的什么都没有吗?并不是,任何类在什么都不写时,

如果一个类中什么成员都没有,简称为空类。

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

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

 

对于Date类,可以通过 Init 公有方法给对象设置日期,但如果每次创建对象时都调用该方法初始化,用完后还要销毁。可能会忘记这些步骤。所以引出构造函数。

构造函数是一个特殊的成员函数名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象

其特征如下

1. 函数名与类名相同。

2. 无返回值。(不需要写void

3. 对象实例化时编译器自动调用对应的构造函数。

 

4. 构造函数可以重载。(可以写多个构造函数,提供多种初始化方式

 

5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

 

6. 关于编译器生成的默认成员函数,会产生疑惑:不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用

解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型,看看下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认构造函数。

 
 

注意:C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即内置类型成员变量在类中声明时可以给默认值。
 
 

7. 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。

不传参就可以调用的就是默认构造函数,三个只能存在一个,否则调用存在歧义

编译器生成的默认构造函数的特点

1.如果不写才会生成,否则写了任意一个构造函数就不会生成了
 
 

2.内置类型的成员(即int、char之类关键字定义)不会处理(C++11,声明支持给缺省值
 
 

3.自定义类型的成员(即类、结构体)才会处理,会去调用这个成员的默认构造函数
 
 

总结:一般情况下都需要我们自己写构造函数,决定初始化方式。

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

析构函数是特殊的成员函数,其特征如下

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
  5. 编译器生成的默认析构函数,对自定类型成员调用它的析构函数。
  6. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

示例1

 
 

示例2

 
 

有了构造函数和析构函数可以重新对括号匹配问题进行改写

1. 创建并初始化栈
2. 数组指针遇到左括号入栈
3. 数组指针遇到右括号份2种情况
    a. 栈为空,返回假。(说明之前没有遇到左括号)
    b. 栈不为空,获取栈顶元素与数组指针指向元素比较是否匹配。(注意匹配条件,找为假的情况)
重复上述步骤直到遍历完数组。
4.最后判断栈是否为空。空为真,非空为假。
 

C语言版本

 
 

C++版本

 
 

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

拷贝构造函数也是特殊的成员函数,其特征如

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

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

 
  

3. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。 

 

注意:在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。

4. 编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗?当然像日期类这样的类是没必要的。那么下面的类呢

 
 

注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。 

栈类的正确版本

 

5. 拷贝构造函数典型调用场景

  • 使用已存在对象创建新对象
  • 函数参数类型为类类型对象
  • 函数返回值类型为类类型对象
为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用尽量使用引用。

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

函数名字为:关键字operator后面接需要重载的运算符符号

函数原型返回值类型 operator操作符(参数列表)

注意

  • 不能通过连接其他符号来创建新的操作符:比如operator
  • 重载操作符必须有一个类类型参数
  • 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
  • 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
  • .*   ::   sizeof   ?:   .   注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

全局版

 
 

待解决问题:成员变量不在是私有
将比较函数改为成员函数

 
 

1. 赋值运算符重载格式

  • 参数类型:const T&,传递引用可以提高传参效率
  • 返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
  • 检测是否自己给自己赋值
  • 返回*this :要复合连续赋值的含义
 
 

2. 赋值运算符只能重载成类的成员函数不能重载成全局函数

原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数

3. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。

内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,所以像日期类这样的类是没必要的。

注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。
 
  
  
  
 
 
 

 
 

 
 

将const修饰的“成员函数”称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

 

以顺序表作为例子

 

上方数组下标运算符重载函数在添加了打印数据函数后出现新的问题:打印数组是不会修改数组的数据,所以打印函数的参数一般都加const

 

当打印函数的参数加上const后出现新问题:打印函数的参数sl是const对象,不能调用非const成员函数 size_t size(); 和下标重载运算符函数。所以size函数和运算符重载函数都要加const

 

上方代码仍有bug未修复,虽然打印函数里sl对象被const修饰,但仍可以修改数组的数据

 

解决方法:对[]函数 构造函数重载。这里的const对象调用有const修饰返回值的运算符重载函数,这样可以避免对象被修改。也就是说即需要一个可读可写的下标重载运算符函数(PushBack函数需要,也需要一个只能读的下标重载运算符函数(Print函数需要

 

在 C++ 中,成员函数的 const 修饰符用于控制对象的可变性和访问权限
【非 const 对象】
可以调用 const 和非 const 成员函数。
const 成员函数承诺不修改对象的状态,因此它们可以被非 const 对象调用,也可以确保在调用时对象的状态不会被修改。
【const 对象】
只能调用 const 成员函数。因为 const 对象不能修改其状态
所以只能调用那些不会修改对象内部数据的成员函数。非 const 成员函数可能会改变对象的状态,这对于 const 对象是不允许的。
总结:const 成员函数保证不会修改对象的状态,适用于 const 对象;非 const 成员函数可以修改对象状态,适用于非 const 对象。

【const 成员函数】:必须保证不修改对象的状态(即不能修改成员变量)。
因此,const 成员函数中只能调用其他的 const 成员函数,以确保不违反这一保证。
如果 const 成员函数尝试调用非 const 成员函数,编译器会报错,因为非 const 成员函数可能会修改对象的状态,从而与 const 成员函数的承诺冲突。
【非 const 成员函数】:没有限制,可能会修改对象的状态,因此它可以调用 const 成员函数。
调用 const 成员函数不会引发矛盾,因为 const 成员函数本身不会修改对象的状态,不会对非 const 成员函数的修改行为产生影响。
 

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。

 

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容

A.Data operator+(Data);
B.Data operator(Data);
C.operator+(Data,Data);
D.Data+(Data);

答案:A
A.正确
B.语法错误,缺少运算符+
C.成员函数参数过多
D.没有运算符重载关键字operator

A.赋值运算符只能作为类的成员函数重载
B.默认的赋值运算符实现了“深层复制”功能
C.重载的赋值运算符函数有两个本类对象作为形参
D.如果己经定义了复制拷贝构造函数,就不能重载赋值运算符

答案:A
A. 赋值运算符在类中不显式实现时,编译器会生成一份默认的,此时用户在类外再将赋值运算符重载为全局的,就和编译器生成的默认赋值运算符冲突了,故赋值运算符只能重载成成员函数
B.默认的赋值运算符是按成员成员,属于浅赋值
C.参数只有一个,另一个通过this指针传递
D.两个函数的调用场景不同,相互没有影响

A.*
B.()
C.. (点)
D.[]
E.->

答案:C
A.可以,例如重载对象取值,典型有以后学到的智能指针
B.可以,例如以后学到的仿函数就是通过重载()实现的
C.不能,不能被重载的运算符只有5个, 点号. 三目运算?: 作用域访 问符:: 运算符sizeof 以及.*
D.可以,例如重载对象的指向,典型有以后学到的智能指针

A.无操作数的运算符
B.二元运算符
C.前缀一元运算符
D.后缀一元运算符

答案:C
A.重载为成员函数时,其函数的参数个数与真实的函数参数个数会减少1个,减少的则 通过this指针进行传递,所以无参  则说明有一个参数,故错误
B.无参成员函数相当于有一个参数的全局函数,不能是二元运算符
C.正确
D.区分前缀后缀时,后缀运算需要加一个int参数

A.a.operator++()
B.a.operator++(0)
C.a.operator++(int)
D.operator++(a,0)

A.正确
B.operator++()传递了整形参数,故为后置++,错误
C.调用函数传递类型,导致语法错误
D.参数过多,语法错误

A.可能被改变
B.已经被改变
C. 受到函数调用的影响
D.不变

本文地址:http://mip.xhstdz.com/news/11123.html    物流园资讯网 http://mip.xhstdz.com/ , 查看更多

特别提示:本信息由相关用户自行提供,真实性未证实,仅供参考。请谨慎采用,风险自负。

 
标签: 函数 成员
 
更多>同类最新文章
0相关评论

文章列表
相关文章
最新动态
推荐图文
最新文章
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报  |  鄂ICP备2020018471号