命名空间
概念 : 命名空间的主要作用是创建一个新的作用域 里面可以放函数 变量 定义 为了防止命名冲突
实现 : 通过使用namespace + 空间名 {} 在大括号中添加 内容
-
这里命名空间允许嵌套
-
在同一个工程中允许存在多个同名的命名空间 在最后编译器会将它们合成同一个命名空间
test。c 和test。h 中的同名命名空间最后和合成一个同名命名空间
使用:命名空间的使用有三种方式:
1、加命名空间名称及作用域限定符 N::成员名
2.使用using将命名空间中某个成员引入 using N ::成员名 该成员就可以直接在文件中使用 而其他成员 仍然需要 N::成员名 这种方式来使用
3、使用using namespace 命名空间名称 引入 这种使用方式最广泛 优点命名空间中的所有成员可以在文件中直接使用 缺点: 在当前文件中可以回发生命名冲突的问题
对于同名函数 或者变量的访问规则 是
1.首先在局部作用域中寻找 是否存在
2.在全局作用中寻找 是否存在 也就是说 如果局部和全局同时存在的情况下 会优先访问局部
-
在命名空间中的同名成员 没有被使用的情况下 是不会去访问空间的中的成员的 只会按照 以上一二优先级进行寻找
-
如果通过 N::成员的方式进行访问 那么就会跳过局部和全局 直接访问命名空间中的成员
-
如果通过 using N :: 成员 或者 是 using namespace N; 的方式声明 那么此时命名空间中成员的优先级 是与全局中的优先级相同的 也就是 在局部存在的情况下 会优先访问 如果局部 不存在 会在全局 和命名空间中同时找 如果此时 全局 和命名空间中都存在 那么就会发生 命名冲突
缺省函数
概念 : 是声明或者定义一个函数是外函数的参数制定一个默认值 在调用该函数 时 如果没有对应的参数 那么就会使用默认值 否则就会使用指定的实参
分类 : 全缺省函数:所有的参数都带缺省值
半缺省函数:部分参数带有缺省值(这里的缺省值必须是从右都左依次给出)
注意事项
-
半缺省函数只能从右向左 不能隔着给
-
在声明和定义分离的函数中 不能同时在声明和定义中 加缺省值 要写在声明中 定义中不要加缺省值
3.缺省参数 必须是常用和全局变量 在编译的过程中 要能明确知道缺省值的内容
4.c语言不支持
函数重载
概念 :相同作用域 函数名相同 返回类型相同 但是参数列表不相同 ( 参数的数量 参数的类型 参数的顺序 有至少一个不同)
调用原理 : 函数名相同 编译器在最后进行链接时 会对建立函数名 和函数定义之间的映射 而同名的函数 通过参数列表对函数名进行修饰 就可以对应的一个函数名 找到一个对应的地址 这样就可以实现函数重载
extern "C" 的作用 在c++函数前如果使用 extern "C"进行修饰 表明告诉编译器 该函数按照c的编译
引用
概念 引用 就是相当于给一个已经存在的变量起别名 是c++中独有的 是编译器不会在为引用单独开一个空间 而是与这个引用的变量公用一个空间
特性 1.引用在创建时必须进行初始化
-
一个变量可以有多个引用
-
一个引用一旦初始化完成就不能修改其引用的内容了 且引用时 不能进行空引用
const 引用 使用const引用时 该引用的变量是不能进行修改的
引用的场景 1. 实现简单普通的取别名 比如 结构体套用结构体 想要使用里层结构体中的成员 不方便可以直接去别名
-
做函数参数 尤其是需要传自定义类型的参数 或者说 想要通过形参改变外部的实参 这里有可能是输出型参数
-
做函数返回值 这里需要注意的是引用返回的内容的生命周期不能随着函数结束而销毁 这样的话返回的引用指向的空间就是一个已经释放的错误空间
引用和指正的区别
-
引用在创建时必须进行初始化 而指针可以不初始化
-
引用在一旦初始化完成之后就不能再更改 而指针的指向是可以进行修改的
-
引用不能进行空引用 而指针可以进行空指针创建
-
在sizeof中的含义不同 引用的大小是指向空间的类型大小 但是指正的大小是始终固定的 (32为下 4字节)
5.引用的++ 和-- 是对引用空间的内容实际上++或者-- 但是指针++ 和-- 则是对指向的空间地址进行++ 和--
- 存在多级指针 但是不存在 多级引用
7.访问实体方式不同 指正同过解引用的操作访问 引用通过编译器解决
8.引用比指针使用起来更加安全
相同点 两者的底层实现上是相同的 引用的底层使用的是指针的底层实现的
内联函数
概念
内联函数是一种通过inline
修饰的函数,编译时会在调用的地方展开,没有函数压栈的开销,可以提高程序运行效率。
特性
-
inline
是一种空间换时间的做法,省去了调用函数的额外开销 ,适合短小的函数。对编译器而言,
inline
只是一个建议,编译器会根据具体情况优化 比如一个函数体特别大的函数进行内联修饰 这时候编译器就不会采用在调用的地方展开的方式。 -
不建议声明和定义分离,否则可能导致链接错误。 因为inline 被展开 就没有函数地址 链接找不到
宏函数
优点
- 在预处理阶段展开,减少函数调用开销。
缺陷
-
编译前展开,若出错难以定位。
-
参数无类型检查,安全性低 不能进行类型检查。
-
可读性差,可能引起副作用。
-
不能调试。
-
可能引起代码膨胀。
-
编译前展开,增加预处理时间。
内联函数
优点
-
参数有类型,安全性高。
-
提高代码运行效率。
-
调试方便。
-
无副作用。
缺陷
-
可能引起代码膨胀。
-
inline
是建议性关键字,编译器可能不处理。 但是编译器是否真的会进行展开处理不得而知 容易给用户代开困惑
内联函数和宏函数区别
-
内联函数在编译阶段展开,宏函数在预处理阶段展开。
-
内联函数参数有类型,宏函数没有。
-
内联函数安全性高,宏函数安全性低。
-
内联函数可调试,宏函数不可调试。
-
内联函数可能引起代码膨胀,宏函数也可能导致膨胀。
类和对象中的8个默认生成函数之中的最常用的4个默认成员函数
首先是介绍析构函数 和构造函数
首先是构造函数 它的作用不是创建一个对象 而是去初始化一个对象
- 它的函数名 与类名相同
2.它没有返回类型 不用写void
3.如果没有显示定义构造函数 那么编译器就会自动生成默认构造函数 在创建对象时 编译器会自动调用构造函数
- 调用构造函数 如果是无参 那么就不需要括号(这里无参不能加括号 会与一些函数声明起冲突) 如果有参数 那么需要传参括号 这里的格式是类名 + 对象名 ();
5.构造函数 可以成重载
-
对于初始化方面 如果是定义了 就是按照对应的定义初始化 如果没有定义 是默认生成的 就会对内置类型不做处理 对自定义类型的成员去调用它的默认构造函数(这里的默认构造函数 并不是单指默认生成构造函数 而是 可以不传参数进行构造的函数 有 无参构造函数 全缺省构造函数 默认生成构造函数 并且三种默认构造函数 只能同时存在一个 )
-
那么默认生成的构造函数如果对内置类型不做处理的话 默认生成函数是否就是没有用的 ?
这里的原因就是第六点 回答默认生成构造函数的作用 另外在c++11中 可以使用在类成员声明的方式来对成员进行初始化
接着是析构函数 析构函数的作用也不是去销毁一个函数 而是为了清理资源
1.它的函数名 与类名相同 在函数名前面加一个~
- 同样没有返回值 不用写void
3。析构函数不能进行函数重载
- 析构函数是没有参数的
5.如果没有显示定义析构函数 那么系统会默认生成析构函数 在对象的生命周期结束时 会自动去调用析构函数 对于默认的析构函数 对于内置类型成员 会不对进行处理 如果是自定义类型成员 那么会调用 其对应的析构函数
- 对于是否要手动定义析构函数
以下两种情况是吧不需要进行手动定义的
-
该类中的成员内置类型的成员没有需要清理的资源 且剩下的都是 自定义成员
-
该类中成员中没有需要清理的资源
当类的成员中存在资源时 就需要进行清理 这时就需要手动写析构函数
接下来我们介绍拷贝构造函数
-
它是构造函数的函数重载的一个形式
-
它的参数固定有一个 且一定是这个类类型的引用类型 (这里有两点 1.一个参数是为了传入对应的要被拷贝的对象 拷贝的对象通过this指正来指向 2. 这里使用引用是为了防止发生不停的进行构造函数造成死循环 应为如果是传值传参的话 就会调用拷贝构造函数 这样会无线递归 )
3.若是没有显示调用拷贝构造函数 那么系统会生成默认拷贝构造函数 这里的默认拷贝构造函数会按照字节一个个进行浅拷贝 但是如果需要调用析构函数来清理资源时之前有浅拷贝 就会导致调用·多次析构函数 从而导致报错 所以这里的规则是 如果需要手动写析构函数的类 就需要手动写拷贝构造函数 否则就使用默认拷贝构造函数就足够了
实践总结中
1.没有需要管理资源的成员的类不需要手写拷贝构造函数
- 一般情况下 不需要写析构函数的类 也不用写深拷贝
3.如果都是自定义类型且内置类型中没有需要资源管理的成员 就不要写生拷贝
4.如果内部有一些指正或者指向资源的成员 就需要写析构函数 和深拷贝
这里显示调用拷贝构造函数的方式有两种
-
N n1(n2);
-
N n1 = n2 ; (这里的方式是通过编译器进行转换 使用更加方便)
接下来介绍赋值拷贝函数
在介绍赋值拷贝函数之前首先介绍 首先介绍运算符重载函数
在都是内置类型的情况下是 可以直接使用运算符的 而自定义类型想要使用运算符 那么就需要使用运算符重载
运算符重载是具有特殊函数名的函数 它的函数名是由 operator +运算符构成的 其他位置保持不变
这里有一些关于运算符重载的注意
-
对于 特殊函数名 operator@ 这样是不行的 中间要有一个空格 不能连在一起
-
对于自定义类型的运算符重载 它是需要作用域在类之中的 不能写成全局的 (这里是为了访问到私有成员变量 另外也是一种规定)
-
对于自定义类型的参数 需要使用引用传值 是为了防止递归调用 此外还可以使用const修饰自定义类型的参数 并且该参数列表中至少要存在一个·自定义类型的参数
-
运算符重载的参数数量是会比正常的数量-1 减少的那一个是通过this指针隐式指向
-
对于运算符重载的功能不能随意乱改 (这里不是硬性规定 但是一般要与原来的运算符保持功能相同)
-
.* . sizeof :: ?: 这五个是不能进行运算符重载的
此外这里还需要介绍引用返回 尤其是需要返回自定义类型的时候 是需要使用引用返回的
-
相比与普通的传值返回 引用返回会减少拷贝构造的次数 因为在传值返回时无论是返回值 还是接受返回值的对象都需要调用构造函数 来创建对象 而使用引用是可以减少损耗
-
对于自定义类型 在函数的作用域结束后 是会进行销毁的 而会调用析构函数 对资源进行清理 所以对于使用引用返回时 要确定返回的对象的生命周期是否会跟随这个函数 结束 如果返回内容会随函数进行清理 那么就不能使用引用返回 引用返回只适用于生命周期不会随着函数结束的对象
如 this指针就可以使用引用返回 (返回this指正是为了 满足某些函数的连续赋值功能)
3.对于赋值拷贝函数如果没有显示实现 那么就会生成默认赋值拷贝函数 此函数会以浅拷贝的方式一个一个字节来进行拷贝 对于内置类型直接赋值 对于自定义类型会自动调用它的赋值拷贝函数
所以这里的使用方式与拷贝构造函数类似 如果需要内置类型管理资源 那么就要写析构函数 拷贝构造函数 赋值拷贝函数 显示实现 如果不需要管理资源 那么使用默认生成的成员函数就足够了