一、C++介绍
本贾尼 . 尼特芝斯特鲁普, 于1979年4月份在贝尔实验室在分析UNIX系统分布内核的流量时,特别希望有一种更加模块化的工具,于是在1979年10月开始着手开发一种新的编程语言,在C的基础上增加了面向对象机制、这就是C++,在1983年完成了C++的第一个版本。
C++与C的重要区别:
1、C++完全兼容C的所有内容
2、支持面向对象的编程思想
3、支持运算符重载、函数重载的编译时多态机制
4、泛型编程、模板
5、支持异常处理
6、类型检查更严格
注意:学习C++重点是学习面向对象的编程思想,而不是花里胡哨的语法。
二、第一个C++程序
#include <iostream>
using namespace std;
int main(int argc,const char* argv[])
{
cout << "Hello World!" << endl;
return 0;
}
1、文件扩展名由 .c 变为 .cpp .cc .C .cxx
2、编译器由gcc编程g++,gcc也可以继续使用,需要增加编译参数-xC++ -lstdc++
3、C++语言的头文件不带.h,iostream意为in out stream,在C++中输入、输出被封装为流操作,C语言中的头文件还可以继续使用,但建议文件名前加C,并去掉 .h,不带 .h 的C语言头文件中删除了大量不需要宏,防止与C++命名冲突
4、输入输出
cout 用于输出
cin 用于输入
不需要占位符,会自动识别数据类型
printf/scanf是C语言标准库函数
cout/cin 是C++标准库中的类函数
5、增加了名字空间
所有标准库的容器、类、对象、函数都位于std名字空间中。
名字空间技术是C++解决全局变量、函数、类、结构、联合、枚举、宏命名冲突的。
三、C++与C数据类型的不同
1、结构不同
a、不再需要 typedef 关键字,设计好结构后,定义结构变量时不需要 struct 关键字
b、结构体中可以有成员是函数,结构变量或结构指针使用 . 或 -> 访问成员函数,在成员函数中可以直接访问结构体的成员变量
c、里面有一些隐藏的成员函数(构造、析构、拷贝构造、赋值函数)
d、可以继承其他结构,也可以被继承
e、可以给成员设置访问属性
public 公开的
protected 保护的
private 私有的
2、联合的不同
a、不再需要 typedef 关键字,设计好联合后,定义联合变量时不需要 union 关键字
b、可以定义成员函数,联合变量,指针使用 . 或 -> 访问成员函数
3、枚举的不同
a、不再需要 typedef 关键字,设计好枚举后,定义枚举变量不需要 enum 关键字
b、是一种独立的数据类型,不能与整型进行隐式转换了
4、bool类型的不同
a、C++中有真正的布尔类型,bool 是C++的关键字,不需要包含stdbool.h头文件
b、true、flase也是C++中的关键字,而在C语言中不是
c、true、flase在C++中是1字节,而在C语言中是4字节
注意:无论是C还是C++的bool类型变量,只能存储 0 | 1
5、字符串的不同
a、C++中的字符串被封装成了string类,但可以与C语言的字符串进行转换
b、string类被封装在string头文件中,但已经被包含在 iostream 头文件中,属于std名字空间
c、使用 string 字符串,可以以运算符的方式操作,C语言中的 string.h 系列函数也可以继续使用
= strcpy
+= strcat
== strcmp
size() strlen
6、void*不同
在C语言中 void* 可以与任何类型的指针进行转换
而C++中 void* 不可以自动转换成其他类型的指针,如果要赋值给其他类型的指针,需要强制类型 转换
注意:其他类型的指针可以转换成void* 类型的指针,C++之所以保留该功能,是因为C语言标准库、 操作系统接口采用大量的void* 作为函数的参数,如果不保留该功能,C++调用这类函数时会非常麻烦
四、名字空间
1、为什么需要名字空间
由于C++完全兼容C语言,标准库中自带大量类和函数,且支持继承语法,导致全局标识符大量增加,因此命名冲突的风险就大大增加,因此命名冲突的风险就大大增加。
2、什么是名字空间
C++中设计一种对命名空间进行逻辑划分单一的技术叫名字空间,它是一种解决命名冲突的一种机制。
namespace xxx{
变量;
函数;
结构联合枚举;
类;
}
定义名字空间就形成一个封装的作用域
3、如何使用
1、直接导入
using namespace xxx;
可以直接使用名字空间里的内容,虽然方便,但不建议这样用
2、域限定符
xxx::标识符
4、名字空间合
不同位置的同名的名字空间会自动合并
a.cpp
using namespace n1{
}
b.cpp
using namespace n1{
}
main.cpp
using namespace n1; //会把a.cpp 和 b.cpp 中的标识符全部导入到main.cpp中
5、声明和定义可以分开
a.h
namespace n1{
extern int num;
}
a.cpp
int n1::num;
但是在定义需要使用域限定符
6、匿名空间
所有的全局标识符都归属于一个名字空间,如果没有指定则默认属于匿名空间 ::
如:局部变量把同名全局变量屏蔽后,可以使用 :: 变量 指定使用全局变量
7、名字空间可以嵌套
namespace n1{
int num = 1;
namespace n2{
int num = 2;
namespace n3
{
int num = 3;
}
}
}
采用逐层分解的方式使用
n1::n2::n3::num
也可以导入指定的层的名字空间
using namespace n1::n2;
8、给名字空间取别名
namespace n123 = n1::n2::n3::num;
五、C++的堆内存管理
1、C++中有专门管理堆内存的语句,而C语言中只能使用标准库中提供的函数
new 分配内存,相当于C语言中的malloc
delete 释放内存,相当于C语言中的free
new[] 分配多块类型相同的内存,相当于C语言中的calloc
delete[] 专门释放new[]所申请的内存
注意:不能混用!
2、new分配内存时允许对内存进行初始化
int* p = new int(234);
3、new和delete不能与malloc/free混合使用
虽然语法允许,但不要这样用
使用new分配内存会自动调用类型的构造函数,而delete会自动调用类型的析构函数
4、数组的分配和释放
new[] 分配多块类型相同的内存,相当于C语言中的calloc
相当于在堆内存分配一个数组,并且会自动调用多次构造函数
delete[] 专门释放new[]所申请的内存,并且它会自动调用多次析构函数。
使用new[]申请到的内存前4个字节记录着,需要调用多少次构造函数,申请的次数
5、重复释放
delete可以释放空指针,但不能重复释放,这一点和free一样
6、内存分配失败
malloc分配失败会返回NULL;
而new分配失败会抛出异常 std::bad_alloc
7、malloc和new的返回值
new会返回一个有类型的指针
malloc返回一个无类型的指针
重点掌握:malloc/free 与 new/delete 区别
malloc/free | new/delete | |
---|---|---|
身份 | 函数 | 运算符/关键字 |
返回值 | void* | 带类型的指针 |
参数 | 字符个数(手动计算) | 类型,自动计算字节数 |
失败 | 返回NULL | 抛异常 std::bad_alloc |
构造/析构 | 不调用 | 自动调用 |
初始化 | 不能初始化 | 可以初始化指定的值 |
头文件 | 包含头文件 | 直接使用 |
面试题:现有一块内存,如何让一个类、结构对象使用这块的内存
六、函数重载
1、什么是函数重载
在同一作用域下,函数名相同,参数列表不同的函数,构成重载关系。
函数重载与返回值类型、参数名无关。
2、C++是如何实现函数重载的
通过g++ -S xxx.cpp生成xxx.s汇编代码可知,编译器会把函数的参数类型缩写追加到函数名的末尾,也 就是编译函数时会进行换名
3、extern “C”
因为C++编译器在编译函数调用的语句时,会找换名后的函数调用,这样就无法调用到C编译器编译出 的函数了
而 extern “C” 会让C++编译器按C语言的格式翻译函数,这样函数的声明和定义就匹配了,可以正确 用C标准库、系统库函数了
4、重载和隐藏
只有同一个作用域下的同名不同参的函数构成重载关系,而不同作用域(父子)下同名函数,遵循名字隐 藏原则
5、参数的类型转换
当调用函数时,编译时会优先调用类型最精准的函数,如果没有会对参数类型进行提升,而不是直接出错