本贾尼.斯特劳斯特卢普,于1979年在贝尔实验室分析UNIX系统分布式内核的流量时,特别希望有一种更加模块化的工具,于是在1979年10月时开始着手开发一块新的编程语言,在c语言的基础上增加了面向对象的机制,这就是C++。1983年完成了C++的第一个版本
① C++是完全兼容C语言的所有内容
② C++支持面向对象的编程思想
③ C++支持运算符重载、函数重载的编译时多态机制
④ C++支持泛型编程、模板机制
⑥ C++支持异常处理
⑦ C++的类型检查更加严格
⑧ C++增加了名字空间
注意:学习C++的重点是学习面向对象的编程思想,而不是花里胡哨的各种特殊语法
#include
using namespace std;
int main(int argc,const char* argv[])
{
cout << "hello world" << endl
return 0;
}
注意:
① 文件后缀名由.c改为.cpp .cc .cxx .C
② 编译器由gcc改为g++,原来的参数还是一样。但是gcc也可以编译.cpp,需要加编译参数
gcc xxx.cpp -xC++ -lstdc++或者gcc -xc++ xxx.cpp -lstdc++
③ C++的头文件不带.h结尾,iostream意为in out stream 在C++中输入、输出被封装成流操作,C语言的头文件还可以继续使用,但是建议使用C++重新编写的C语言头文件,原C头文件名前面+c,并去掉.h,为了删除C头文件中大量定义好的宏,然后重新放入名字空间中,为了防止与C++命名冲突stdio.h -> cstdio,stdlib.h -> cstdlib
④ C++增加了名字空间的技术:为了解决命名冲突而新增的一项技术
⑤ 输入和输出不同:cout 用于输出 cin用于输入 需要配合运算符
cout << num << num2 << num3 << endl; endl是换行
cin >> num >> num2 >> num3;
⑥ 不需要占位符,默认会自动识别基础类型
printf/scanf 是C语言标准库函数
cout/cin 是C++标准库中的类对象(类似结构变量)
不需要typedef对类型进行重定义,因为C++中设计好结构后,定义结构变量时不再需要struct关键字
结构中可以有成员函数,结构变量、指针通过.或者->访问成员函数,在成员函数中可以直接访问成员变量、成员函数,不再需要.或者->
结构中默认下一定有一些隐藏的成员函数(构造、析构、拷贝构造、赋值函数)
结构可以继承其它结构,也可以被其他结构继承
可以给成员设置访问控制属性
public 公开的(默认)
protected 保护的
private 私有的
不需要typedef对类型进行重定义,因为C++中设计好联合后,定义联合变量时不再需要union关键字
联合中可以有成员函数,联合变量、指针通过.或者->访问成员函数,在成员函数中可以直接访问成员变量、成员函数,不再需要.或者->
联合中默认下一定有一些隐藏的成员函数(构造、析构、拷贝构造、赋值函数)
可以给成员设置访问控制属性
public 公开的(默认)
protected 保护的
private 私有的
不需要typedef对类型进行重定义,因为C++中设计好枚举后,定义枚举变量时不再需要enum关键字
是一种独立的数据类型(C语言用int来模拟枚举类型),不能与整型做隐式转换,只能用枚举常量
C++中有真正的布尔类型,bool是C++中的关键字,不需要包含stdbool.h头文件,true和false也是C++的关键字
true和false在C++中是1字节,并且是真正的布尔类型,而c语言中是int类型模拟的,是4字节,但是bool类型字节数都是1
注意:无论C还是C++,bool类型只能存储0或1
C++中的字符串都被封装在string类中,可以与C语言的字符串进行转换 (.c_str()从string转为char*)
string类被封装在string头文件中,该头文件被包含在了iostream头文件中,属于std名字空间
使用string字符串可以通过运算符的方式直接操作字符串,原来C语言string.h系列函数也可以继续使用
注意:C++中用string类没有规定必须以字符'\0'结尾,编译器在实际中可能在末尾加,也可能不加,这是由编译器决定的。因为string是一个类类型,它的字符串长度信息已经封装在了类的私有成员变量中,可以得知长度,不需要加'\0'了
在C语言中,void*可以与任意类型的指针进行转换;在C++中,void*不可以自动隐式转换成其他类型的指针,如果需要赋值给其他类型的指针时,必须强制类型转换,为了提高指针的安全性考虑;在C++中,其他类型指针可以自动隐式转换成void*,C++之所以保留这个转换,是因为C语言标准库、操作系统接口函数采用了大量的void*作为函数参数,如果不保留该转换,C++在调用此类函数时会非常麻烦。例如free/realloc/bzero/mmap
由于C++完全兼容C语言,标准库中自带了大量的宏、函数、结构、类,而且还支持继承语法,导致全局的标识符大量增加,命名冲突的风险也大大增加。
C++中设计一种对命名空间进行逻辑划分单位的技术,称为名字空间,是一种解决命名冲突的机制。定义名字空间后,就形成了命名的封闭作用域
namespace xxx{
变量;
函数;
结构、联合、枚举;
类;
}
① 域限定符访问 ::
xxx::标识符;
② 全部直接访问
using namespace xxx;
注意:可以直接访问xxx名字空间中的所有标识符,虽然方便,但是不建议都这样用
③ 同名名字空间可以合并
a.cpp
namespace n1{
int a;
}
b.cpp
namespace n1{
int b;
}
main.cpp
using namespace n1;// 会把a.cpp的a和b.cpp的b一起倒入到main.cpp中
同名名字空间可以多处定义,不同位置的同名名字空间编译器会自动合并
④ 名字空间中的标识符可以声明与定义分离
a.h
namespace n1{
extern int num; // 声明
}
a.cpp
int n1::num; // 定义
注意:需要在定义时使用域限定符进行说明
⑤ 匿名名字空间
所有的全局标识符都属于同一个名字空间,该名字空间没有名字,称为匿名名字空间,只要不加域限定符说明,那默认都属于匿名名字空间的标识符
匿名名字空间中的标识符通过 ::标识符名 来访问
例如:同名局部变量屏蔽了同名全局变量后,可以使用 ::变量名 指定访问全局变量
⑥ 名字空间可以嵌套
namespace n1{
int num = 10;
namespace n2{
int num = 20;
namespace n3{
int num = 30;
}
}
}
采用逐层分解的方式访问:
n1::n2::n3::num;
导入整个名字空间:
using namespace n1::n2::n3;
给名字空间换别名
namespace n123 = n1::n2::n3;
C++中有专门管理堆内存的语句,而C语言中只能使用标准库提供的函数
语法格式:
类型* p = new 类型名;
new 分配内存,相当于C语言的malloc
delete p;
delete 释放内存,相当于C语言的free
类型* p = new 类型名(val);
new在分配内存时允许直接对内存进行初始化
new/delete 不能与 malloc/free 混合使用
类型* p = new 类型名;
free(p); // 虽然有些情况允许不报错,但不要这样做
使用new分配内存时会自动调用类型的构造函数,而delete会自动调用类型的析构函数
但是malloc/free不会调用,因此混用时可能会内存泄漏
类型* p = new 类型名[个数];
分配连续多个同类型的堆内存,并且会调用多次构造函数
类似:malloc(sizeof(类型)*个数)
delete[] p;
专门用于释放通过new[]申请的内存,它会自动调用多次析构函数
注意:malloc/free、new/delete、new[]/delete[]不能混用
使用new[]为结构、联合、类申请到的内存的首地址的前4个字节中存储了构造函数的调用次数,这样编译器在通过delete[]时就可以知道需要调用多少次析构函数
但是普通类型没有构造、析构函数,所以不记录
delete与free一样不能重复释放,但都可以释放空指针
malloc分配失败返回NULL,程序继续
new分配失败会抛出一个异常,并立即结束程序
malloc 返回一个void*类型的指针,需要强转
new 返回一个具体类型的指针,不需要强转
区别 | malloc/free | new/delete |
身份 | 函数 | 运算符/关键字 |
参数 | 字节数(手动计算) | 类型名(自动计算) |
返回值 | void* | 带类型的指针 |
处理数组 | 手动计算总字节数 | new 类型[个数] |
扩容 | realloc | 无法直接处理 |
失败 | 返回NULL | 抛异常 |
构造\析构 | 不调用 | 自动调用 |
初始化 | 无法初始化 | 允许初始化 |
头文件 | 包含头文件 | 直接使用 |
函数重载 | 不允许重载 | 允许重载 |
分配内存的位置 | 堆区 | 自由存储区 |
注意:自由存储区只是一个抽象概念,与其他内存区不同,没有具体范围,默认调用new的时候,底层调用的是malloc所以此时绝大部分情况是分配在堆区,但是new可以当做运算符被程序员重载或者通过 new(地址) 类型名; 方式可以指定分配的内存首地址到其他区