1 背景
以阅读ART源码为目的进行学习。
2 数据类型
2.1 基本数据类型
由于C++和硬件平台特性关联比较大,因此不像java那样严格规定每种类型所需的字节数。而是规定了每种类型最少需要多少个字节。对于浮点型则是规定了最少的有效数字的位数。
可以通过sizeof(类型)操作符来得到每种类型的字节数。
- short:短整型,至少2个字节
- char:字符型,至少1个字节
- int :整型,至少2个字节,一般为4个字节
- long:长整型,至少4个字节
- long long: 长整型,至少8个字节
- float:单精度浮点型,至少支持6个有效数字
- double:双精度浮点型,至少支持10个有效数字
- bool:布尔型, 规范没有定义
2.2 指针、引用、void类型
表示指针类型 T *,其中T表示某种数据类型
表示引用类型 T &,其中T表示某种数据类型
void表示空类型 void * ,只能用来修饰指针变量。表示不关心指针所指向内存中的数据类型
2.2.1 指针
- 指针的类型
- 指针的赋值
- 指针的解引用
1,指针的类型
指针本质上代表虚拟内存的地址,即:指针就是内存地址。 如32位操作系统上,一个进程的虚拟地址空间为4G,虚拟地址就从0x0~0xFFFFFFFF(8个F),这其中任何一个值都是地址,也就是指针。
一个运行的程序上,它的虚拟内存中有什么呢? 肯定是数据或代码。
如果内存中存储的是数据,因为数据是有类型的,所以指向该内存的指针也是有类型的。 int* p: p是一个指针,指向某一块内存的地址,而该内存中存储了一个int类型的数据。
如果内存存储的是代码,一般是某个函数的入口,那么指向该地址的指针称为函数指针。
int mymain(int argc,char* argv[]){
}
//int表示返回类型
//pmain是函数指针
//后面的输入参数列表
int (*pmain)(int argc,char* argv[])
//两种赋值方式都一样
pmain=mymain;
pmain=&mymain;
//进行调用
pmain(0,nullptr);
2,指针的赋值
- 直接写一个地址赋值给指针
- new操作符在堆上分配一块内存,该内存地址存储在对应的指针变量中
- 通过取地址符号&,获取某个变量或者某个函数的地址
int* p=(int*)0xff43kk99; 这种操作很危险!!
Obj* obj=new Obj();
int a=100;
int* pa=&a;
int (*pmain)(int a,char* b[]);
pmain=mymain;
pmain=&mymain;
3,指针解引用
指针即内存地址,那么如何获取内存中内容呢? 通过 * 。
对于数据类型指针,* 意味着获取内存中的数据 对于函数指针,* 意味着调用某个函数
int* a=100;
//取值
*a=100;
//调用函数
(*pmain)(100,nullptr);
2.2.2 引用
引用就是变量的别名。引用和原变量是一对一的强关系。 引用和原变量是同一个东西。是周树人和鲁迅的关系。
2.3 字符和字符串
2.4 数组
指针和数组有着天然的关系。
int *parray=new int[4];
parray+i: 表示下标i的地址。
*(prray+i):表示下标为i的内存中的值
3 源码构成和编译
C++中承载源码的文件分为头文件和源文件。头文件的后缀名以.h、.hpp、hxx结尾。源文件的后缀名以.cc、.cpp、cxx结尾。
头文件一般用来公开声明一些变量、函数、类。
源文件则是对头文件的一个实现,实现这些变量、函数或者类。
命名空间:类似于java的包,表示一个范围scope。声明在命名空间的变量、函数或者类,就属于这个命名空间。
头文件的命名空间里定义了test1函数:
//type.h
namespace my_type{
void test1();
}
在源文件的命名空间里定义了test1和test2函数:
type.cpp
//引入
#include "type.h"
namespace my_type{
void test1(){
//具体逻辑
}
void test2(){
//具体逻辑
}
}
使用者(一般只会include头文件)是不知道test2函数的。
test.cpp
//引入
#include "type.h"
int main(void){
my_type::test1();
}
C++ 标准库std就是命名空间。
4 Class相关
对class类的理解整体从三个方面:
- class是面向对象世界的核心单元,里面封装了成员变量和成员方法。开发者更多考虑的是设计合理的类并处理对象与对象之间的关系。关注的是对象与对象之间交互。
- class支持抽象、继承和多态。关注的是类和类之间的关系。
- 从数据类型的角度来看,类也是一种数据类型,只不过是开发者自定义的数据类型。
一个完整的class例子: typeClass.h
#ifndef _TYPE_CLASS_H_
#define _TYPE_CLASS_H_
namespace type_class{
void test();
class Base{
public:
Base(); //默认构造
Base(int a);//普通构造
Base(Base & other); //拷贝构造
Base& operate =(Base& other); //拷贝赋值函数
Base(Base && other);// 移动构造
Base& operate =(Base&& other) //移动赋值函数
~Base(); //析构函数
protected:
//声明且实现
int getMemberB(){
return memberB;
}
int deleteC(int a, int b=100,test=true); //只声明,不实现
private:
int memberA;
int memberB;
int* membeC;
}
}
4.1 构造、赋值和析构函数
构造函数用来完成对象的初始化,包括成员变量的初始化。 构造函数分类:
- 默认构造 无参数
- 普通构造 :带参数的初始化
- 拷贝构造: 这里是深拷贝,也就是遇到引用类型的数据,会重新申请内存拷贝原内存中的值。
- 移动构造:直接把A对象的值移动到B对象,A对象会被析构。
赋值: 拷贝赋值:会执行拷贝过程 移动赋值:只是移动,提升了效率
析构函数: 当对象被回收的时候会回调析构函数。 在栈中创建对象,在函数返回前该对象会执行析构函数。 通过动态new创建对象,且delete该对象时会回调析构函数。
4.2 类的派生和继承
4.2.1 虚函数、纯虚函数和虚析构函数
通过virtual关键字修饰。
virtual void test();//虚函数
virtual void test2()=0; //纯虚函数
虚函数:派生类可以重写该方法。重写后会调用到子类。 纯虚函数:派生类不能重写。类似于接口 虚析构函数:想要多态,基类中析构函数必须虚化。
调用顺序?
构造函数在实例创建时候调用。基类先于派生类。而析构函数则相反。
如: class Drive:Base,VirtualBase{}
构造函数顺序:先Base在VirtualBase最后在Drive。
析构函数顺序:先Drive在VirtualBase在Base。
4.3 类的友元和前向声明
友元的概念: 提供某种方式,使得类外的某些函数或者某些类能够访问一个类的私有成员变量或者成员函数。对于被访问的类而言,这些类就是被访问类的友元。
通过friend来修饰,友元不能被继承。
friend void accessObj();
friend void B::testa();
类可以先声明,后定义。
explicit是用来避免类型隐式转换的。
struct和类几乎一样,但是struct的成员都是public的。
5 操作符重载
操作符重载的本质就是把操作符当成函数来对待。当调用操作符时候相当于调用了某个函数。 定义:
operate 操作符符号
->和* 操作符重载。
可实现智能指针。通过重载时间类似于指针的效果。
但是对于真正的指针来说,->相当于访问成员变量或者函数。 *就是解引用。
6 函数模板和类模板
从数据类型的角度进行抽象,那么久抽象出来一个T。 它既可以表int也可以表示long。
可以利用具体数据类型来实例化某个模板。
- 数据类型的抽象
- 实例化
6.1 函数模板
一般在头文件定义: 原因: 因为编译器去实例化一个模板的时候,需要知道全部的类型。
函数模板本质上不是函数,只有当编译其用具体类型套用到模板函数上之后才会生成实际的函数。
//基本类型
template<typename T1,typenae T2,typename T3=long>
T3 add(T1 a,T2 b){
return a+b;
}
// 引用类型
template<typename T,(*compare)(T& a, T& b)>
void add22(T& a,T& b){
return a+b;
}
函数模板的特例化:
特例化就是实际的函数。而非模板函数。
格式: template<> 修饰,直接用具体类型替换调模函数中的类型。
6.2 类模板
格式:
template<typenmae T>
Class TObj{
template<typename T1,typenae T2,typename T3=long>
T3 add(T1 a,T2 b){
return a+b;
}
}
类模板中即可以有普通函数,也可以有模板函数(又称为成员模板)。
7 lambda表达式
C++中lambda表达式本质就是匿名的函数对象。 格式:
auto f =[捕获列表](函数名)->返回值类型{函数体}
auto func(int a)->int {...}
创建lambda表达式后就得到一个闭包,闭包就是匿名函数对象。
8 STL介绍
标准模板库。命名空间为std。
8.1 string类
8.2 容器类
动态数组vector : 类比java的arraylist
哈希表 unordered_map ----hashMap
链表 list---- linkedList