C++入门必看
程序员文章站
2024-02-02 22:11:04
...
一、C++介绍
贝尔实验的本贾尼.斯特劳斯特鲁普,于1979年在分析Linux系统分布内核流量分析时,希望有一个更加模块化的工具,于是他为C语言增加了类的机制(面向对象),于1983年完了C++的第一个版本。
特点:
1、C++完全兼容C语言的所有内容。
2、支持面向对象的编程思想(抽象、封装、继承、多态)。
3、支持函数、运行符重载。
4、支持泛型编程、模板技术。
5、技术异常处理机制。
6、类型检查更严格。
二、第一个C++程序
1、文件扩展名
.cpp .cc .C .cxx
2、头文件
#include <iostream> C++中把文件操作抽象成了类。
#include <stdio.h> 可以继续使用
#include <cstdio> 建议这样使用,里面删除了一些可能会引起冲突的宏。
3、增加了名字空间
using namespace std;
4、输入/输出
cout << 输出数据
cin >> 输入数据
cout/cin不需要对变量取地址,也不需要占位符,因为它们可以自动识别数据类型。
scanf/printf/sscanf/sprintf可以继续使用。
注意:scanf/printf是C标准库函数,cin/cout是C++标准库中的类对象,被定义在std名字空间中。
5、编译器
g++ file.cpp -> a.out
大多数Linux系统都需要额外安装,ubuntu系统安装命令:
sudo apt-get update
sudo apt-get install g++
gcc编译器也可以继续使用,需要额外增加参数:-xc++ -lstdc++
三、名字空间
1、为什么需要名字空间
在项目中函数名、全局变量、结构、联合、枚举、宏等,非常有可能有命名冲突,而C++之父设计名字空间就是为了解决这些命名冲突,名字空间可以把这些命名划分到不同逻辑空间里,从而解决命名冲突。
2、定义名字空间
namespace name {
定义变量
定义函数
定义类、结构、联合、枚举
}
名字空间可以重复,同名名字空间会自动合并。
3、使用名字空间
使用空间中的单个标识符,空间名::标识符 使用麻烦,但安全。
导入空间中的所有标识符,using namespace 空间名,虽然方便但依然有冲突的可能。
4、名字空间嵌套
名字空间的内部可以再定义名字空间,这种定义方式叫名字空间嵌套。
内层的标识符可以与外层的重名,内层的标识符会自动屏蔽外层的标识符。
namespace n1
{
int num = 10;
namespace n2
{
int num = 20;
namespace n3
{
int num = 30;
}
}
}
多层的名字空间使用时要逐层分解。
n1::n2::n3::num
使用多层名字空间时为了精简长度可以取别名
namespace n123 = n1::n2::n3;
5、匿名空间
默认定义全局的标识符归属到匿名空间中,可以直接使用域限定符::访问匿名空间中的标识符。
四、C++的数据类型
1、布尔类型
C++中有真正的布尔类型,不需要添加头文件,bool true false 可以直接使用。
2、结构、联合
1、不再需要typedef,在定义结构、联合变量时可以省略 struct union关键字。
2、成员可以是函数,成员函数中可以直接访问成员变量,而不再需要 . ->
3、有四个隐藏的成员函数(构造、析构、拷贝构造、赋值构造)。
4、可以对成员的访问权限进行管理,public/protected/private。
5、结构还可以被继承。
3、枚举
1、不再需要typedef,在定义枚举变量时可以省略 enum 关键字。
2、使用方法与C语言基本一致,不能与整型数据混用。
4、void*
1、在C语言中void*可以与任意类型的指针自动转换(万能指针)。
void* p = NULL;
int* p1 = p;
p = p1;
2、C++中void*不能再直接给其它类型的指针变量赋值,只能强制类型转换后才能赋值,但其它类型指针可以直接给void*类型的指针赋值。
void* p = NULL;
int* p1 = p; // error
3、C++为什么要修改void*
为了安全,另外C++可以自动识别类型,所有对万能指针的需求不再那么强烈。
4、C++中的字符串
1、C++中把字符串封装成了string类,实现在string头文件中,但已经被iostream包含,定义在std名字空间中。
2、常见的字符串操作不再需要函数,可以使用运算符:
= strcpy
== strcmp
+= strcat
3、计算字符串长度使用size成员函数。
4、也可以与转换成C语言中的char*
c_str() const char*
data() char*
五、C++函数与C函数的区别
1、函数重载
1、什么是函数重载
C++中,在同一作用域下,形参列表不同的同名函数构成重载关系,且不会冲突。
2、重载实现的机制
C++代码在编译时函数的参数类型会添加函数名中,也就是说C++的函数名在编译时经历的换名的过程,借助这个方式实现了函数的重载。
注意:由于C++和C函数的编译机制不同,所以C++代码不调用C编译器所编译出的函数。
3、extern "C" {}
功能是告诉C++编译器按照C语言的机制声明函数,这样C++中的代码就可以调用C编译编译出的函数了(C++目标文件与C的目标文件才能合并出可执行文件)。
4、重载和作用域
函数的重载关系一定发在同一作用域下,不同作用下的同名函数构成的是隐藏关系。
5、重载函数的调用
当调用重载函数时,编译器会根据实参的数据类型选择合适的重载函数,实参与形参匹配情况有三中:
1、编译器找到实参与形参完全匹配的函数,编译器会生成调用指令。
2、编译器找到多个匹配函数,但没有一个最佳的,编译器会产生二义错误。
绝大数情况下都编译器都能找到一个最佳的匹配函数,但如果没有,编译器就会进行类型提升,这样备选函数中就可能有多个可调用的版本,然后二义性错误就产生了。
3、编译器找不可调用的重载函数,会直接产生错误。
6、指针类型也影响函数的重载
C++函数的参数如果指针类型的,编译时就会在函数的末尾添加Px。
7、如果参数是指针或引用,是否加const也会影响函数的重载。
注意:函数重载是面向对象编程思想的多态(多种形态,根据实参情况对指令作出相应的反应)的体现,具体调用哪个版本的函数是在编译期间就确定了,所以这种也叫编译时多态。
2、默认形参
1、在C++中函数的参数可以设置默认值,在函数调用时,如果调用者没有提供实参,则使用默认的形参。
2、如果只一部分参数设置了默认形参,则设置默认形参的靠右排列。
3、如果函数的声明在定义分开实现,则只需要在函数声明时设置默认形参。
4、默认形参会影响函数的重载,慎重给重载函数设置默认形参。
3、内联函数
1、普通函数会在调用位置生成调用(跳转)指令,当执行到调用代码时会跳转到函数所在的代码段位置执行。
2、内联函数就是把函数编译好的二进制指令拷贝到调用位置,也就没了跳转和返回的过程。
3、内联函数的优缺点:
与普通函数相比,内联函数的执行速度更快。
大量使用内联会使可执行文件变大,造成冗余。
4、显式内联和隐式内联
显示内联:在函数的返回值类型前加 inline (C语言也支持)
隐式内联:结构、联合、类的成员函数会默认优化成内联函数。
注意:是内联由编译决定(部分编译器没有实现内联的功能)。
5、宏函数在调用时会把函数休替换到调用位置,与内联函数一样用空间来换取时,所以宏函数与内联函数的区别(优缺点)?
1、宏函数函数仅仅是代码替换不是真正的函数,也就不会有参数传递、入栈、出栈、返回值,而且所有类型都可以使用,但不会进行类型检查,有安全隐患。
2、内联函数是真正的函数调用,函数在调用时会进行参数类型检查、传参、压栈、出栈,也可以有返回值,还可以重载、设置默认形参,但这样就不能通用了。
6、什么样的情况适合内联函数
由于使用内联函数会使用可执行文件变大,增加内存开销,只有调用频繁且内容短小的函数适合作为内联函数。
调用比较少且内容多的函数,内联后并不显著提升性能,不足以抵销特性空间带来的损失所以不适合内联。
带有递归特性,和动态绑定特性的函数,无法实施内联,因此编译会自动忽略inline关键字。
六、C++的堆内存管理
1、在C++中使用 new/delete 管理堆内存
相当于C语言中的malloc/free
使用方法:
new 类型;
new会自动计算类型所需要的字节数,然后从堆内存申请相应字节数的内存块,并返回内存块的首地址(有类型地址)。
delete 地址;
释放堆内存中的内存块;
注意:new/delete 与malloc/free不能混能,因为 new/delete 除了管理堆内存还有其它工作要做。
2、数组的分配与释放
new 类型[n];
n表示数组的长度
delete[] 地址;
注意:通过 new[] 申请的内存,必须使用 delete[] 释放,与就是不能new/delete混用。
3、重复释放
delete/delete[] 不能重复释放同一块内存。
delete/delete[] 释放野指针的后果不确定,但释放空指针是安全的。
4、内存申请失败
当内存申请过大,没有能满足需要的整块内存时会抛出异常"std::bad_alloc"。
new/delete 与malloc/free的区别
new/delete malloc/free
身份:运算符 函数
参数:类型(自动计算字节数) 字节数(手动计算)
返回值:带类型的地址 void*
申请失败:抛异常 返回NULL
调用函数:构造/析构 无
相同点:
1、都能管理堆内存
2、不能重复释放
3、都可以释放空指针
七、强制类型转换
C++中有一套更安全的强制类型转换,分别是
1、静态类型转换 static_cast<目标类型>(源类型)
源类型和目标类型只要有一个方向可以隐式转换,那么两个方向都可以做静态类型转换,如果不能则报错。
2、动态类型转换 dynamic_cast<目标类型>(源类型)
源类型和目标类型必须同是引用或指针,且目标类型的源类型之间存在继承关系,否则报错。
3、去常类型转换 const_cast<目标类型><源类型>
源类型和目标类型必须同是引用或指针,且目标类型的源类型只有常属性的区别,否则报错。
4、重解释类型转换 reinterpret_cast<目标类型><源类型>
源类型和目标类型必须是指针,或者一个指针一个整数,否则报错。
本贾尼.斯特劳斯特鲁普:如果让我重新设计,它们会更长。
原因:C++之父不希望程序员使用强制类型转换,如果程序中用到了强制类型转换则说程序设计的不完善,程序员应该去优化程序的设计而不应该强制类型转换。
上一篇: mysql建立自定义函数的问题