标准C++
C的补充和扩充 C就是C++的一个子集
C语言写大型程序 复杂 编程效率低
C语言最大优势在于性能效率
C++兼顾执行效率 与 编程效率
C/C++
版本越稳定越好
1.应用
科学计算 完善的科学计算的库
操作系统(驱动程序) 可移植性
网络软件(网络编程) ACE库
网络游戏
算法
军事软件
图形界面软件 vc(visual stdio c++ win32/MFC)
windows下平台 C++ windows c++ MFC(Microsoft Foundation Classes)
QT(c++ 图形界面库 跨平台 支持IOS/Android)
从C语言过渡到C++:
1.第一个c++程序
C++基本上完全支持C语言语法
2.C++的程序后缀名可以是 .cpp .c++ .cc .cxx .C
3.编译C++程序
g++
gcc -lstdc++
#include <iostream>
using namespace std;
int main(){
cout << "Hello world!" << endl;
return 0;
}
名字空间 命令空间 namespace
C++的大型项目 都会采用名字空间
名字空间 C++对于变量、类型、函数在逻辑上的一个划分 一个名字空间相当于一个作用域
在代码中不能直接访问其它名字空间中的标识符
定义名字空间:
namespace xxxx{
}
C++中对于同名的名字空间进行合并 (名字一样就是同一个名字空间)
名字空间可以嵌套
使用名字空间:
1.using namespace name;
声明name名字空间对于当前作用域可见
2.using name::标识符
把name::标识符 引入到当前作用域
3.在使用时直接指明名字空间作用域 name::xxx
明确访问某名字空间中的标识符
匿名名字空间
C++中对于没有名字空间限制的标识符默认归入到匿名名字空间
如果在程序中需要指明访问,由可以用 ::标识符 来指定访问匿名名字空间中的标识符
:: (名字空间)作用域限定符
名字空间::标识符
C++的类型
char short int long float double
1.结构体
对于空结构体:
C: sizeof(空结构体) = 0
C++: sizeof(空结构体) = 1
C++中的当struct类型定义之后 声明或者定义变量时 struct关键字可以省略
2.联合
C++支持匿名联合
所谓联合 是变量在内存中的一种布局方式
union{
int x;
char v[4];
};
cout << x << endl;
cout << v[0] << endl;
3.枚举
C++中枚举是一种独立的数据类型
不能把整数变量或者整数值 赋值给 枚举变量(虽然含义上枚举变量 实际上是存储整数值)
反之 枚举值 枚举变量 本质上是整数值 所以可以赋值给整数变量
enum DIRE{L,R,U,D};
DIRE d = U;
d = 2;//Error
int x = d;
x = D;
4.布尔
C++有bool类型
C语言中 stdbool.h bool
cout << boolalpha << true << endl; boolalpha 设置打印bool值不是0和1
非"零"即真
0
0.0
'\0'
NULL
""
5.运算符别名
&& and
|| or
! not
[ <:
] :>
{ <%
} %>
&= and_eq
& bitand
| bitor
~ compl
!= not_eq
!= 0r_eq
^ xor
^= xor_eq
**在c++中如果要使用c中的标准库文件,则在c++中用#include
C++的函数
C++中函数没有隐式声明,在调用之前必须先声明
C++中函数没有形参,和void一样 不能传递实参
C语言struct中不能声明函数 但是C++的struct可以声明函数 C++中在struct声明的函数为成员函数 直接访问成员变量
1.重载
C++中,同一个作用域下面,函数名相同,参数列表不同即构成重载
前提条件:同一个作用域,函数名相同
参数列表不同:
1.参数个数不同
2.参数同位置类型不同
重载的函数在调用时 会根据实参的个数和类型来决定调用哪一个函数 静态绑定(编译时决定)
gcc编译器在编译函数时 生成的标识符 就是函数名
g++编译器在编译函数时 会对函数进行改名操作 修改之后的标识符会根据函数名和形参的类型和个数来生成新的标识符
void func(int a) _Zfunci
void func(double d) _Zfuncd
void func(int a,int b) _Zfuncii
func(1);
动态绑定(运行时才能决定调用哪个函数)
如果某些函数不需要进行改名操作(以C语言的方式来编译C++的函数)
extern "C"{
void func(){}
void bar(){}
}
*调用重载函数时注意产生歧义
2.缺省值
在声明函数时参数可以设置默认值 在调用函数时可传也可以不传
缺省参数靠右原则: 如果一个形参有默认值 其右边所有的参数也必须要有默认值
*注意:缺省值函数在重载时要注意
当声明与实现分离时 缺省参数只能放在声明中 实现中不能带缺省参数
3.哑元
只有类型,没有形参名谓之哑元
只关心参数的类型 不关心参数的值
在运算符重载中用于区分 前++-- 和 后++--
4.内联 inline
内联是对函数调用的一种优化,直接用函数的二进制指令替换调用指令
内联函数运行效率更高 一般来说生成的程序更大
一般来说简单的函数可以用内联 复杂的函数没有必要使用内联函数
递归函数不能是内联函数
inlinue只是一种建议,函数能不能作为内联函数由编译器自已决定
c语言中动态内存由:
malloc/calloc/realloc/free 是<stdlib.h>中函数
c++中由 new/delete/new []/delete[] 是c++操作符
new /new []实现是调用c标准库中的malloc/calloc实现的
delete/delete []实现是调用c标准库中的free实现的
new/delete 用于申请和释放单个动态内存
new[]/delete[] 用于申请和释放数组的动态内存
new/new[] 意味着分配内存 调用类型的构造函数(方法)
delete/delete[] 会调用析构函数
(1)申请和释放单个变量动态内存
类型 * 变量 = new 类型; 申请一个sizeof(类型)字节大小的内存,并且初始化为"零"
类型 * 变量 = new 类型(初始值);
delete 指针变量;
(2)申请数组形式的动态内存
类型 * 变量 = new 类型[数组长度]; sizeof(类型)*数组长度 个字节大小的内存
类型 * 变量 = new 类型[数组长度]{初始值,…};
delete [] 指针变量;
malloc与new有什么区别?
1.malloc是c标准库函数 new是c++中的操作符
2.malloc不会调用类的构造函数 new会自动调用类的构造函数
3.malloc可以申请数组的内存空间 new只能申请单个变量的内存空间 new[] 才能申请数组
4.malloc需要给定申请内存大小的字节数 new只给类型自己会计算
5.malloc返回的是void* new返回的是指定类型地址
6.malloc出错返回NULL new出错抛出异常
5.C++的引用(引用的本质其实是指针)
C++中的引用 即变量别名
声明引用变量:
类型& 变量 = x; //引用变量
引用变量必须初始化
引用变量一旦定义之后 引用变量和目标变量 本质上是同一个变量
指针值可以为NULL 但是引用不能为空
引用变量一旦定义,引用目标已经确定,引用变量不能再去引用其它的目标
int a = 10;
int& r = a;
r = 1000; //本质上操作的就是a
引用的意义:
(1)用于函数传递参数
如果要在函数内部修改实参的值 C必须传递指针 但在C++中可以使用引用
(2)返回一个结果作为左值 返回值类型可以声明为引用类型
如果返回的是引用类型的目标 目标不可以是函数的局部变量
(3)引用提高传参效率 节省内存 形参可以用const来修饰 表示 函数内部并不希望对实参进行修改
常量引用 const 类型& xx = val;
形参列表中的引用 是在 函数 调用时 初始化的
*重要:引用与指针的区别?
引用是C++中 c和c++都可以使用指针 引用的底层实现就是指针
指针是实体变量 引用不是实体变量
sizeof(指针) == 固定值
sizeof(引用) 随着引用目标不同 大小不同
(1)引用必须初始化 指针可以不初始化(野指针)
int& r = x; int *p;
(2)引用目标不能为空 指针指向可以为空
int*p = NULL;
p = NULL;
(3)引用一旦初始化 引用目标不能改变 指针任何时候都可以修改目标
int *p = &a;
p = &b;
(4)可以声明指针的引用 但不可以声明引用的指针
int *p;
int*& pr = p;
int a = 10;
int& r = a;
int& *pa = &r;//Error
(5)可以声明指针的指针 但不可以声明引用的引用
指针的指针 其实就是二级指针
int a = 10;
int& r = a;
int&& rr = r;//Error
int& rr = r;
(6)可以声明指针数组 但不可以声明引用数组
int *arr[3] = {};
int &brr[3] = {};
(7)可以声明数组的引用 也可以声明数组的指针
int arr[3] = {1,2,3};
int (&ra)[3] = arr;
int (*par)[3] = &arr;
C++ 能加const则加const 能用引用由用引用
C++ 中丢失const肯定是报错
6.类型转换
(1)隐式类型转换 可以自动转换 不会有警告和错误
(2)强制类型转换
目标变量 = (目标类型)(源变量)
int x = 1;
char *p = (char *)&x;
所有需要强制类型转换都是程序设计不合理导致的 C++不建议使用强制类型转换
(3)显示类型转换
a. 静态类型转换
static_cast<目标类型>(源对象)
使用场合:
源对象和目标对象 如果任何一个方向上可以进行隐式类型转换
那么两个方向上都可以进行静态类型转换
b. 去常类型转换
const_cast<目标类型>(源对象)
使用场合:
用于去除引用变量和指针变量的常属性
c. 重解释类型转换
reinterpret_cast<目标类型>(源对象)
使用场合:
适用于整数和指针之间的转换
或者指针与指针之间的转换
d. 动态类型转换
dynamic_cast<目标类型>(源对象)
用于存在多态关系的父子类型的指针和引用之间的转换
7.C++的字符串
C++中支持C风格的字符串
C++中提供了string类型
8.C++面向对象编程
类
对象
抽象: 把一类事物共同的特征抽象为成员属性 把一类事物的共同行为抽象为成员方法
封装: 对一类事物的描述 并加以访问控制属性的限制
面向对象的三大特征: 封装、继承、多态
class 类名{
成员属性
成员方法
};
访问控制属性:
public: 公开的 表示在任何地方都可以访问
protected: 保护的 只能在类或者子类的才能访问
private: 私有的 只能在本类的内部才能访问
struct和class的区别:
struct中默认访问控制属性是public
class中默认访问控制属性是private
(1)构造方法(函数)
用于构造对象时调用的方法 每实例化一个对象都需要调用构造方法
a.构造方法没有返回值类型,也不能是void
b.构造方法名必须和类名一模一样
c.当一个类没有实现构造方法时,编译器会自动添加一个无参的构造方法
当程序员自己有实现构造方法时,编译器将不会再提供无参的构造方法
d.构造方法可以重载
可以按照不同的方式来构造对象
(2)this
在C++中,每一个类的成员方法中都隐含一个this指针
this指针指向正在调用该方法的对象
可以用this-> 来区分成员变量 和 局部变量
成员方法中:
局部变量可以和成员变量同名 根据局部优先原则 访问到的是局部变量
class 类名{
public:
类名(形参列表){
}
};
(3)实例化对象
类名 变量名(形参列表); --有参构造方法 构造对象
类名 变量名; --无参构造方法 实例化对象
类名 标识符(); //不是调用无参构造方法来实例化对象 而是一个函数声明语句
(4)初始化列表
只允许在构造方法中
常属性成员 及 引用成员 必须在初始化列表中初始化 不能在构造函数体中初始化
其实成员变量 可以用初始化列表 也可以在构造函数体中
构造函数名(形参列表):成员变量(初始值),...{
}
初始化列表的执行顺序与只与成员变量的声明顺序有关,与初始化列表的语句的先后无关
(5)常对象和常函数
具有常属性(const修饰)的对象 称为 常对象
常对象只能调用常函数 不能调用普通的成员函数(普通的成员函数 this所指向的对象没有const)
常函数 成员函数有const属性
//const修饰的是this指向所指向的那个对象
返回值类型 函数名(形参列表)const{
}
常函数和非常函数 构成 重载
常对象是不能调用非常版本的函数
非常对象 如果在 没有非常版本的函数时 可以调用 常版本的函数
普通的全局函数不能是常函数 不能有const修饰 因为没有隐含的this指针
对于所有没有const修饰的对象 都可以默认提升为 const
重载: 列参列表不同(指针和引用类型的常属性不同也构成重载)
对于普通的变量(非引用和指针) 重定义 错误
void func(int x){}
void func(const int x){}
对于指针或者引用 重载
void func(int& x){}
void func(const int& x){}
void func(int *x){}
void func(const int* x){}
const int* func(const int& x)const{
}
(6) mutable
mutable 用于修饰成员变量
在常函数中不能对成员属性进行修改 但是可以对mutable修饰的成员属性进行修改
(7) static修饰的属性 称为类属性 静态属性
静态属性 需要类外声明(初始化)
类型 类名::静态属性 = x;
静态属性不能在初始化列表中初始化
静态属性 也称为 类属性
静态属性属于类 可以起用用 类名::静态属性 来访问
也可以通过 对象.静态属性 的方式访问
静态属性属于类 所有的对象共享一份
static修饰方法 称为 静态方法 类方法
静态方法中没有隐含的this指针 所以不能直接访问成员属性 不能调用成员方法
在静态方法中 可以 直接访问静态属性
(8) explicit
class A{
A(B b){}
};
在任何一个类中,如果实现了单参构造函数 A(B b){}
那么,B类型的对象都可以借助单参构造函数成为A类型的对象
构造函数可以用explicit修饰 防止单参构造函数的隐式调用
C++显式类型转换:
static_cast<>
const_cast<>
dynamic_cast<>
reinterpret_cast<>