从零开始的C++

一、C++介绍

本贾尼 贝尔实验室 在分析UNIX系统分布内核流量的时候,希望有一种有效的更加模块化的工具
1979年10月完成了预处理器Cpre,为C增加了类机制,也就是面向对象。
1983年完成了C++的第一个版本,C with classes也就是C++
C++与C的不同点:
1、C++完全兼容C的所有语法
2、支持面向对象的编程思想
3、支持运算符,函数重载
4、支持泛型编程,模版
5、支持异常处理
6、类型检查严格

二、第一个C++程序

1、文件扩展名
.cpp .cc .C .cxx
2、编译器
g++ 大多数系统不带的,需要额外安装,ubuntu系统下的安装命令:
sudo apt-get updata
sudo apt-get install g++
gcc也可以继续使用但需要增加参数 -xC++ -lstdc++
3、头文件
#include
#include<stdio.h>可以继续使用
#include也可以使用
4、输入/输出
count << 输出数据
cin >> 输入数据
以上两个会自动识别类型
scanf/printf也可以继续使用
注意:cout和cin是类对象,而scanf/printf是标准库函数
5、增加了名字空间
std::count
using namespace std;
所有的标准类型,对象,函数都位于

三、名字空间

1、为什么需要名字空间
在大型项目中,函数名、全局变量、结构、联合、枚举、类,非常有可能名字冲突,而名字空间就对这些命名进行逻辑空间划分(不是物理单元划分),为了解决命名冲突
2、什么是名字空间
在C++中经常使用多个独立开发的库来完成项目,由于库的作者或开发技术人员根本没见过面,因此命名冲突在所难免,C++之父为防止命名冲突给C++设计一个名字空间机制
通过使用namespace XXX把库中的变量,函数,类型,结构等包含在名字空间中,形成自己的作用域,避免名字冲突
namespace XXX
{

}//这个位置没有分号
注意:名字空间也是一种标识符,在同一作用域下不能重名
3、同名的名字空间会自动合并(为了申明和定义分开写)
同名的名字空间中如果有重名的依然会命名冲突

4、名字空间的使用方法
::域限定符
空间名::表示符//使用麻烦 但非常安全
using namespace 空间名;把空间中定义的标识符导入到当前代码中

5、无名名字空间
不属于任何名字空间的标识符,隶属于无名名字名字空间。
无名名字空间中的成员使用::标识符 进行访问
如果访问被屏蔽的全局变量

6、名字空间的嵌套
名字空间内部可以在定义名字空间 这种名字叫做空间嵌套
内层名字空间与外层的名字空间的成员可以同名,内层会屏蔽外层的
多层名字空间在使用时逐层分解
n1::n2::n3::n4
namespace n1
{ int num = 1;
namespace n2
{
int num =2;
}
}
7、取别名
由于名字空间可以嵌套,这样就会导致在使用内层成员时过于麻烦,可以给名字空间取别名来解决这类问题
namespace n123 = n1::n2::n3;

四、C++的结构

1、不再需要 typedef,struct 关键字可以省略
2、成员可以是函数(成员函数),在成员函数中可以直接访问成员变量,不需要.或->,但是C的结构成员可以是函数指针
3、有一些隐藏的成员函数(构造,析构,拷贝构造,赋值构造)
4、可以继承,可以设置成员的访问权限(面向对象)

五、C++的联合

1、不再需要 union ,在定义结构变量时,可以省略 union 关键字
2、成员可以是函数
3、有一些隐藏的成员函数

六、C++的枚举

1、定义,使用方法与C语言基本一致
2、类型检查比C语言跟家严格

七、C++的布尔类型

1、C++中有真正的布尔类型,bool 是C++中的关键字,在C语言中使用布尔类型需要导入头文件stdbool.h(在c11中boll加入)
2、true false 在C++中是关键字,而在C语言中不是
3、C++中 true false 是1字节,而在C语言中是4字节

八、C++的void*

1、C语言中void* 可以于任意类型的指针
2、C++中void* 不能给其他类型的指针直接赋值,必须强制类型转换,但是其他类型的指针可以自动给void赋值
3、C++为什么这样修改void

为了更安全,所以C++类型检查更加严格
C++可以自动识别类型,对万能指针的需求不再那么强烈

九、操作符别名

某些特殊的语言,键盘上没有~ &等符号,所以C++标准委员会为了让C++更有竞争力,为符号定义了一批别名,让小语种也能写C++

十、函数重载(重载、隐藏、重写覆盖)

1、函数重载
在同一个作用域下,函数名相同,参数列表不同的函数,构成函数重载关系
2、重载实现的机制
C++代码在编译时会把函数的参数类型添加到参数名中,借助这个方式来实现函数重载,就是C++的函数在编译期间经历换名的过程
因此,C++代码不能调用C语言编译出的C函数(gcc)
3、extern “C”
告诉C++编译器按照C语言的方式申明函数,这样C++就可以调用C编译器编译出的函数了(C++目标文件可以与C目标文件合并生成可执行程序)
如果C想调用C++编译出的函数,需要将C++函数的定义用extern"C"包括一下
注意:如果两个函数名一样,一定会冲突
4、重载和作用域
函数的重载关系发生在同一作用域下,不同的作用域下的同名关系叫做隐藏
5、重载解析
当调用函数时,编译器根据实参的类型和形参的匹配情况,选择一个确定的重载版本,这个过程叫做重载解析
实参的类型和形参的匹配情况有三种:
1、编译器找到与实参最佳的匹配函数,编译器将生成调用代码
2、编译找不到匹配函数,编译器将给出错误信息
3、编译器找到好几个匹配函数,但是没有一个是最佳的,这种错误叫做二义性。
在大多数情况下编译器都能立即找到一个最佳的调用版本,但如果没有,编译就会类型提升,这样被选函数中,可能会有多个可调用的版本,这样就可能产生二义性错误。
6、重载确定的三个步骤
1、候选函数
函数调用的第一步就是确定所有可调用的函数的集合(函数名、作用域),该集合中的函数就是候选函数
2、选择可行性函数
从候选函数中选择一个或多个函数,选择的标准是参数个数相同,而且通过类型提升实参可被隐式转换为形参
3、寻找最佳匹配
优先每个参数都完全匹配的方案,其次参数完全匹配的个数,在其次是浪费内存的字节数。
7、指针类型也会对函数的重载造成影响
C++函数在编译时,如果形参类型是指针,编译时函数名中追加Px。

十一、默认形参

在C++中,函数的形参可以设置默认值,调用函数,如果没有提供实参,则使用默认形参
如果形参只有一部分设置了默认形参,则必须靠右排列
函数的默认形参是在编译阶段确定的,因此只能使用常量,或者是常量表达式,或者全局变量做默认值。
如果函数的声明和定义需要分开,只需要在函数声明时设置默认参数即可
设置默认形参时一定要慎重,默认形参对函数重载造成应影响

十二、内联函数

1、普通函数调用时是生成调用指令(跳转),然后当代码执行到调用位置时跳转到函数所在的代码段中执行
2、内联函数就是把函数编译好的二进制指令直接复制到函数的调用位置
3、内联函数的优点就是提高程序的运行速度(因为没有跳转,也不需要返回),但这样会导致可执行文件增大(冗余),也就是牺牲空间来换取时间
4、内联分为:显式内联和隐式内联
显式内联:在函数前 inline(C语言C99标准也支持)
隐式内联:结构、类中内部直接定义的成员函数,则该类型函数会被优化成内联函数。
5、宏函数在调用时会把函数体直接替换到调用位置,与内联函数一样也是使用空间来换取时间
宏函数与内联函数的区别(优缺点)
1、宏函数不是真正的函数,只是代码的替换,不会有参数压栈,出栈以及返回值,也不会检查参数类型,因此所有的类型都可以使用,但这样会有安全隐患。
2、内联函数是真正的函数,函数调用时,会进行传参、压栈、出栈,可以有返回值,并会严格检查参数类型,但这样就不能通用,如果想补多种类型调用需要重载。
6、内联适用的条件
由于内联会造成可执行文件变大,并且增加内存开销,因此只有频繁调用的简单函数适合作为内联函数。
调用比较少的复杂函数,内联后并不显著提高性能,不足以抵消牺牲空间带来的损失,所以不适合内联
带有递归特性和动态绑定特性的函数,无法实施内联,因此编译器会忽略声明部分的 inline 关键字

十三、引用

引用就是取艺名
1、引用的基本特性
引用就是取别名,申明一个标识符为引用,就表示该标识符是另一个对象的外号。
1、引用必须初始化,不存在空引用,但有悬空引用(变量死了,名还留着)
2、可以引用一个无名对象和临时对象配合 const 使用
3、引用不能更换目标
引用一旦完成了定义和初始化就和普通变量名一样了,他就代表了目标,一经引用终身不能在引用其他目标

猜你喜欢

转载自blog.csdn.net/weixin_45050225/article/details/99601042