C++类和对象(思维导图小结)

思维导图分享

初识类和对象

预备

命名空间

关键字:namespace

作用:对变量命名进行保护,防止一个工程中出现变量的命名冲突问题

使用形式:namespace 变名空间的名字{命名空间的变量成员}

1.正常使用
2.命名空间可以嵌套
3.命名空间的名字可以是重复的,在编译的时候会合并成一个命名空间

一个命名空间定义了一个新的作用域

命名空间的使用

加命名空间名称及作用限定符 ::
使用using 将命名空间中某个成员引入 using 命名空间名称::成员名
使用using namespace 命名空间引入 (展开命名空间)

C++的输入和输出

头文件 iostream中的cin 和 cout

输入 cin>>

输出 cout<<

cin和cout在命名空间名称为std中,使用方法

1.using namespace std
平时练习的时候使用
2.std::cin std::cout
3.using std::cin

using std::cout

大工程项目的时候使用,放在变量命名的污染

cout 是ostream的类型对象

cin 是istream的类型对象

cout和cin能自动识别数据类型,这和流插入<<和流提取>>的运算符重载有关,函数名相同,参数类型不同——函数重载

缺省参数

概念:在函数声明或者定义的时候给参数一个缺省值,给指定的数据就用,没有就用该参数的缺省值

分类

全缺省参数
半缺省参数

注意

参数缺省要从右到左,因为参数的传递是从左到右传递的,不能指定传参。
缺参数不能在函数声明和定义的时候同时出现,当函数声明和定义分离的时候,缺省参数要在函数声明里面

缺省值必须是常量或者是全局变量

C语言不支持(编译器不支持)

函数重载

函数重载概念:在同一个作用域里面,函数名字相同,功能相似,参数类型不同(参数类型不同或顺序不同或个数不同)

函数重载原理

源文件到可执行程序步骤
一个文件先通过预处理阶段,展开相应的头文件,替换宏等
接着到编译阶段检查语法是否合规
再到汇编阶段将文件生成目标文件,变成汇编语言到二进制语言,形成符号表
在通过链接阶段生成可执行程序
在汇编阶段,函数依据函数名修饰规则生成相应的符号表,到时通过符号表的合并找到相应的函数定义位置。
C语言中函数名修饰规则只是记录函数的名字,不记录参数
C++中函数名修饰规则是记录函数名字和参数的类型——所以可以通过这个规则对函数进行函数重载
(注意不能通过函数返回类型进行函数重载)因为汇编的时候是先找函数的声明的,此时是不能知道函数的返回值类型的,这样就无法通过汇编,到不了链接的阶段

引用

概念:引用不是定义一个新的变量,是一个已知变量的别名,和其共用一块内存空间

使用形式:类型& 引用变量名(别名)= 实体对象

注意:类型要和实体对象的类型相同

引用特性

1.引用在定义的时候必须初始化
2.一个变量可以有多个引用
3.引用一旦引用一个实体,就不能在引用其它的实体(在定义外的 = 表示的是赋值,而不是重新引用)

引用原则

1.权限可以平移或者缩小
2.权限不能扩大

注意:临时的变量具有常性;在类型转化的时候,会生具有常性的临时变量,函数值返回的时候也会产生常性的临时变量。常量只能读不能写,所以要用

const 类型&接受

使用场景

引用做函数参数(基本上都可以使用)
在函数里面不修改值加上const限制
要修改就不用添加
引用做函数的返回值
出了函数作用域,对象还在则用引用返回
出了函数作用域,对象不存在,就用值返回

传引用会比传值的效率高

引用和指针的区别

在语法层面:引用没有开辟新的空间
在底层逻辑:引用和指针实现的一样
不同点
1.引用概念上定义一个变量的别名,指针存储一个变量的地址
2.引用在定义的时候必须初始化,而指针没有要求
3.引用在定义的初始化后,不能在改变指向;而指针可以改变指向
4.没有NULL引用,而有NULL指针
5.在sizeof的含义下不同:引用的结果和引用对象类型相同,而指针一直都是地址空间字节数的大小4/8
6.引用自增是实体自增,而指针自增是向后偏移一个类型的大小
7.有多级指针,没有多级引用
8.访问实体方式不同,指针需要显示解引用,引用编译器会自己处理
9.引用比指针用起来更加安全

内联函数

关键字:inline

概念:用inline关键修饰的函数是内联函数,编译器会在调用该函数的时候展开,没用函数调用栈帧建立的开销,效率高

特性

1.以空间换时间
2.inline只是一种建议,编译器会更据函数体的指令大小来确定是否构成内联函数
3.函数体小的(编译器实现不同),递归、循环等的情况不会变成内联函数
4.内联函数的声明和定义不能分开,因为inline被展开,没有函数的地址,链接的时候找不到

使用场景

频繁使用某个短小的语句或功能的函数,将该函数写成内联函数

C++内联函数和C语言的宏函数关系

宏的优缺点
优点
  • 1.增强代码的复用性

  • 2.提高性能

缺点
  • 1.不方便调试宏(因为编译的时候进行了宏替换)

  • 2.导致代码可读性差,可维护性差,容易误用(括号太多了)

  • 3.没有类型安全的检查

C++宏的替代
1.常量定义:用enum枚举来定义
2.短小函数换成内联函数

auto关键字 C++11

作用:自动识别变量的类型

auto定义变量的时候一定要初始化,auto更据初始的表达式来推导auto的实际类型,auto是一个占位符,编译时会将真正的类型填到这占位符上

使用细则

1.指针和引用类型
用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
2.定义多个变量
在一行定义的多个变量的类型要相同,因为编译器只对第一个表达式进行推导,然后用推导出变量类型来定义其它的变量

auto不能推导的场景

1.auto不能作为函数的参数
2.auto不能直接用来声明数组
3.为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法
4. auto在实际中最常见的优势用法就是跟以后会讲到的C++11提供的新式for循环,还有lambda表达式等进行配合使用。

基于范围的for循环 C++11

语法糖:

for循环后的括号由冒号“ :”分为两部分:第一部分是范

围内用于迭代的变量,第二部分则表示被迭代的范围。

范围for使用范围

1.迭代的范围必须确定
2. 迭代的对象要实现++和==的操作

指针空值–nullptr C++11

问题:NULL有时候是字面常量0,有时候又被定义成(void*)0常量,麻烦

nullptr是C++11的关键字,就只是(void*)0常量

上篇

面向对象的基本认识

C语言是面向过程的,注重的过程,分析解决问题的步骤,通过函数的调用逐步解决问题

C++关注的面向对象,关注的对象,将一件事拆分成多个对象,通过对象之间的交互完成这件事

类的引入

C语言中struct只能定义变量,而C++可以定义变量,也可以定义函数

在C++中,用关键字class来建立类

类的定义

类的定义:class classname{成员变量和成员函数};

成员函数的定义与说明
1.声明和定义全部放在类里面,需要注意:成员函数如果定义在类中,编译器可能会自动处理成内联函数
2.类声明在.h文件中,成员函数定义在.cpp文件中,注意:成员函数名前需要加类名::
成员变量的命名建议
类的成员变量名前加一个下划线_

类的访问限定符及封装

访问限定符

作用:对C++的对象起到封装的作用
封装方式:用类将对象的属性和方法结合在一起,让类更加完善,通过访问限定符选择性提供给外部的用户使用
关键字
private(私有的)
public(共有的)
protected(保护的)
说明
1.public修饰的成员外部可以直接访问
2.protected和private修饰的成员在类外是不能被访问的
3.访问权限作用域从该限定符出现的位置开始到下一个访问限定符或者到}末尾结束
4.class默认的访问权限是private,而struct默认的访问权限是public的

封装

面向对象的三大特性:封装、继承、多态
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。封装本质上是一种管理,让用户更方便使用类。

类的作用域

类定义了一个新的作用域,类的所有成员都在类的作用域中。在类外定义成员的时候,需要使用::作用域操作符指明成员属于哪个类域

类的实例化

用类类型创建对象的过程,称为类的实例化

说明

1.类是对对象的描述,是一个模型的东西,限定了类有什么成员,定义一个类并没有分配实际的空间来存储它
2.一个类可以实例化出多个对象,实例化出对象占物理的存储空间,存储类成员变量

类的对象大小的计算

存储方式:只存储类的成员变量,成员函数放在一个公共的代码区

类按照内存对齐的方式进行存储(结构体的内存对齐相似)

空类和空成员变量,用一个字节来表示这种类

类成员函数的this指针

用来和实例化的对象进行绑定,可以访问实例化出来对象的成员变量

this是成员函数的参数,编译器默认自己传递,不能显示传参

this特性

1.this是指针类型,有*const修饰,不能修改this指向
2.只能“成员函数的内部使用”,对象中不存储this指针
3.this是形参
4. this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递

中篇

类的6个默认成员函数

默认成员函数:在没有显示定义的情况下,编译会一定的规则自动生成相应的默认成员函数

分类

初始化和清理
构造函数主要用来完成初始化操作
析构函数主要用来完成清理工作
拷贝复制
拷贝构造是用同类对象初始创建对象
赋值运算符重载主要是把一个对象赋值给另一个对象
取地址重载
主要是普通对象和const对象取地址,我们很少实现

构造函数

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

特性

1.函数名和类名相同
2.无返回值
3.对象实例化的时候自动调用对应的构造函数
4.构造函数可以重载
5. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
6.编译器默认生成的构造函数,对内置类型来说看成是不做处理的,对自定义类型的来说调该自定类型的构造函数。
7.在C++11中,可以对内置类型赋予缺省值
8.无参参数的构造函数和全缺省构造函数都成为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数。(用默认构造函数实例化对象的时候,不能在对象后面加括号,因为这样编译器就不知道是函数声明还是调用默认构造函数了)

构造函数的对象初始化

在构造函数体中对成员变量的=是赋值,不是初始化,因为初始化只能初始化一次,而赋值可以赋值多次
初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
注意
1.每一个成员只能在初始化列表中出现一次
2.类中的成员一定要出现在初始化列表中,引用成员变量,const成员变量,自定义类型(没有默认构造函数的时候)
3.初始化列表编译器都会默认走一遍,所以尽量都是要初始化列表对成员变量初始化
4. 成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

关键字:explicit

作用:阻止类型转化初始化对象
构造函数不仅可以构造与初始化对象,对于单个参数或者除第一个参数无默认值其余均有默认值的构造函数,还具有类型转换的作用

析构函数

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

特性

1.析构函数名是类名前面加字符~
2.无参数无返回值
3.一个类只能有一个析构函数。若未显示定义,编译器会自动生成。析构函数不能重载
4.对象生命结束的时候自动调用析构函数
5. 关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员调用它的析构函数。
6. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

拷贝构造函数

作用:用一个已经存在的对象,初始化另一将要创建的对象

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

特征

1.拷贝构造函数是构造函数的重载
2.拷贝构造函数的参数只有一个且必须是类类型对象的引用,不能使用值的方式传递,会造成编译报错或者无向递归。
3.若没有显示定义拷贝构造函数,编译器会默认生成拷贝构造函数。默认拷贝构造对内置类型按字节序完成拷贝,称为浅拷贝或值拷贝。自定义类型调用其的默认拷贝构造函数
4.对于没有动态开辟空间的类,可以使用默认拷贝构造函数,如果存在动态开辟内存空间,需要我们显示定义拷贝构造函数。
5.拷贝构造的使用场景
使用已经存在的对象初始化创建新的对象
函数参数类型为类类型对象
函数返回值为类类型对象
传参用引用传参,返回值更据是否存在来使用引用返回

赋值运算符重载

运算符重载

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
注意
不能通过链接其他符号来创建新的操作数:如operator@
重载操作符必须有一个类类型参数
对于内置类型的运算符,其含义不能改变
作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
.* sizeof :: ?: .
  • 这五个操作符能重载

赋值运算符重载

赋值运算符重载格式
参数类型:const T&,传递提高效率
返回值类型:T&,返回引用可以提高效率,有返回值的目的是可多次拷贝
检测是否自己给自己赋值
返回*this:要复合赋值的含义
赋值运算符重载必须成员函数,不能重载成全局函数
原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。
用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。
前置++和后置++
前置++没传参
后置++传了int类型的参数
对于自定义类型的前置++效率高

const成员函数

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

在函数的后面加上const,在函数体里面不修改类的成员变量的值,建议都加上const修饰

取地址以及const取地址操作符重载

没有特殊情况下,编译器自动处理即可

下篇

static成员

概念

声明为static的类成员称为类的静态成员,用static修饰的成员变量,称之为静态成员变量;用static修饰的成员函数,称之为静态成员函数。静态成员变量一定要在类外进行初始化

特性

1. 静态成员为所有类对象所共享,不属于某个具体的对象,存放在静态区
2. 静态成员变量必须在类外定义,定义时不添加static关键字,类中只是声明
3. 类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问
4. 静态成员函数没有隐藏的this指针,不能访问任何非静态成员
5. 静态成员也是类的成员,受public、protected、private 访问限定符的限制

友元

关键字:friend

作用:可以访问类中私有成员变量,但是增加了偶联,破坏了类的封装,建议少用

友元函数

问题:现在尝试去重载operator<<,然后发现没办法将operator<<重载成成员函数。因为cout的输出流对象和隐含的this指针在抢占第一个参数的位置。this指针默认是第一个参数也就是左操作数了。但是实际使用中cout需要是第一个形参对象,才能正常使用。所以要将operator<<重载成全局函数。但又会导致类外没办法访问成员,此时就需要友元来解决。operator>>同理。
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。
说明
友元函数可以访问类的私有和保护的成员变量,但不是类的成员函数
友元函数不能用const修饰
友元函数可以在类中任何地方声明,不受类访问限定符的限制
一个函数可以是多个类的友元
友元函数的调用和普通函数的调用类似

友元类

友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
友元关系是单向的,不具有交换性。
友元关系不能传递
友元关系不能继承

内部类

概念:一个类定义在另一个类的内部,就叫做内部类

内部类是一个独立的类,不能通过外部类访问内部类的成员

内部类天生就是外部类的友元类,内部类可以访问外部类的所有成员,而外部类不行

特性

1.内部类可以定义在外部类的任何访问限定符里面
2.内部类可以之间访问外部类的static成员,不需要外部类的对象/类名
3. sizeof(外部类)=外部类,和内部类没有任何关系。

匿名对象

形式,类名加括号;不需要对类进行取名字

匿名对象的生命周期就只是在那一行

对拷贝对象的编译器的优化

隐式类型,连续构造+拷贝构造->优化为直接构造

一个表达式中,连续构造+拷贝构造->优化为一个构造

一个表达式中,连续拷贝构造+拷贝构造->优化一个拷贝构造

一个表达式中,连续拷贝构造+赋值重载->无法优化

函数传参的时候用引用传参;

函数返回的时候依据情况用引用返回:
用拷贝构造初始化一个对象,不要用赋值运算符重载进行赋值:
可以的话,用匿名对象做返回值

猜你喜欢

转载自blog.csdn.net/zxj20041003/article/details/130576748