C++基础学习
起源: 贝尔实验室贝尔实验室20世纪80年代。c语言是面向过程的一种开发语言 缺点是在开发大型项目时管理起来特别麻烦。所以在c的基础上经过改进和添加开发出了c++。 应用范围:文字处理程序以及电子表格(office)编辑器。操作系统(windows macOS linux android unixd ) 大型游戏
- 低级语言直接操纵硬件 针对不同的处理器 要有不同的低级语言去处理
- 高级语言不针对任何处理器。进行编程 之后由不同编译器 适配不同处理器
提高可移植性
- 面向过程注重实现功能的算法面向对象注重实现功能的逻辑
命名空间 using namespace std; 防止多工程的函数重名 采取的解决方案
a::mun() b::mun();
面向对象
- C如何实现面向对象思维:结构体里指针类型指向函数
- 类是一种规范 对象是根据这种新型规范构造的特定数据结构
类
class 类名{}; struct 结构体名{};
class声明的类型和struct声明的类型仅仅在形式上有所不同。
其唯一区别在于使用class声明的类型 默认成员是私有的(private),而struct声明的类型默认成员是共有的(public)
访问权限修饰符
- Public 公用访问权限。任意地方访问
- Private 私有访问权限。只能在本类或友元函数中访问
- Protected 继承访问权限。只能在本类和其子类以及友元函数中可以访问
可以利用public 权限的get/set函数来访问没有权限的成员 利用get/set封装后可以添加一些安全访问判断比如赋值的限制等,
c++相对c语言扩展
- 添加了面向对象的机制(函数重载,函数模版,带默认值的函数,继承,重写)
- 提供了标准的输入输出对象(cin cout)
- 引用类型
- string类型等
- vector.
文件转化过程 .c -> .exe
- 预处理 宏定义展开,头文件展开,删掉注释包括#if #end if 语句
gcc -E A.c -o A.i - 编译 检查语法,将预处理后的文件编译成汇编文件
gcc -S A.i -o A.s - 汇编 将汇编文件转换为二进制文件
gcc -C A.s -o A.o - 连接 连接 库 连接头文件
gcc A.s -o A.exe
内存分配
- 栈区:由编译器自动分配和释放。(函数参数 局部变量 等)自动回收的空间
- 堆区:由程序员自己分配释放程序结束时操作系统回收 (new 等) 需要手动回收的空间
- 全局区:存放全局变量和静态变量程序结束后由系统释放 (全局变量。静态变量)
- 文字常量区:存放常量字符串,程序结束后由系统自己释放
- 代码区:存放函数体二进制代码.
动态内存分配:(new / delete) 系统提供的变量类型。是在编译时开辟空间, new 是在运行时开辟空间。用完后 delete 释放内存空间 int *p = new int; p在栈区。new开辟的空间在堆区 (不要创建两个指针 指向同一块内存空间 这样释放内存空间时,本来只需要释放一次但是 由于指针名字不同可能误释放两次) int * intArray = new int[10]; delete [] intArray;
- 类名 std1; 程序结束时会自动释放 (存放在栈里)
- 类名 *std2 = new 类名(); 程序结束前需要手动释放 delete(存放在堆里)
向量
- 向量优点 可以在运行阶段设置长度, 具有和数组一样的快速索引方式, 可以插入和删除元素。
vector 的常用操作函数
- clear() 移除容器中所有元素
vecDouble.clear();
- empty() 判断容器是否为空
if (vecDouble.empty())
{
cout<<"empty"<<endl;
}
- size() 返回容器中元素个数
vector<double> vecDouble(5,1.1);
//开辟5个空间初始化为1.1
cout<<"size ="<<vecDouble.size()<<endl;
for (int i = 0; i < vecDouble.size(); i++)
{
cout<<vecDouble[i]<<endl;
}
- at(index) 返回索引为index的元素
cout<< vecDouble.at(i)<<‘\t’<<arr[i]<<endl
- erase(pos) 删除pos位置处的数据(pos 是迭代器)
- erase(beg, end) 删除beg和end区间的数据(参数是迭代器)
- front() 返回第一个元素
- insert(pos, elem) 在pos位置处插入一个元素 (pos 是迭代器)
- pop_back() 删除最后一个元素
- push_back(elem) 在容器末尾插入一个元素
vecDouble.push_back(2.2);//在向量末尾添加一个2.2 - resize(num) 重新设置容器大小
- begin() end() 返回容器首尾元素的迭代器
for (vector<double>::iterator it = vecDouble.begin(); it != vecDouble.end(); ++it)
{
cout<<*it<<endl;
}
- sort(it.begin(), it.end()) 顺序排序
sort(vecDouble.begin(),vecDouble.end());//sort排序 - reverse(it.begin(), it.end()) 逆序
reverse(vecDouble.begin(),vecDouble.end());//reverse逆序 - .reserve() 重新定义 空间大小
vecDouble.reserve(20);
迭代器 (iterator)
vector<double>::iterator it;创建vector类型的迭代器对象(本质是个指针)it = vecDouble.begin();迭代器变量it初始值等于 向量vecDouble首元素的迭代器it != vecDouble.end();迭代器等于vecDouble尾元素迭代器时证明是最后一个元素
for (vector<double>::iterator it = vecDouble.begin(); it != vecDouble.end(); ++it)
{
cout<<*it<<endl;
}
引用
- 引用为对象起别名,引用声明时必须初始化(就是指向一个变量)
- 如果指向常量,必须使用const修饰,引用的效率要比指针高,因为指针在赋值时要判断其地址是否有效,引用本质还是指针就是封装后的指针。
- 引用常用作为函数的型参类型和返回值。数组是不能用引用传递的
- 做返回值时千万不要返回局部变量的引用(这里和指针一样) 引用作返回值类型(参数也是引用类型)时可以不写返回值,默认返回最后一个被修改的参数。
- 引用作为函数返值类型时,应尽量是const类型的引用, 以免有人直接改变函数的返回值。
函数
-
函数重载 函数名相同,参数列表不同。 编译器会将参数列表的内容不同,给函数重命名,来实现重载
-
运算符重载(因为类本质就是一个“类型” 在这个类型内部作运算符重载后,再运用被重载后的运算符时返回的就是被编辑后的结果) 实现:
类型 类名::operator+(const 类型 & a) { cout << “重载运算符+”<< endl; return 类型(this->num + a.num); }
类型 类名::operator+(const 类型 & a)
{
cout << “重载运算符+”<< endl;
return 类型(this->num + a.num);
}
调用:和正常“+”完全一致。
-
函数模版 建立通用函数。函数重载要指明参数类型,但函数模版定义时不指明具体的数据类型(使用虚拟类型代替)函数被调用时编译器根据实参反推数据类型,类型的参话 template<typename 类型参数1 , typename 类型参数2, … > 返回值类型 函数名(形参列表){ //函数体中可以使用类型参数 }
// 模版的声明 template<typename T> void Swap(T&, T&); // 模版的定义 template<typename T> void Swap(T &a, T &b){ T temp = a; a = b; b = temp; }
所谓泛型编程就是由函数模版提供的支持
带默认值的函数
- 默认值可以在函数的原型或者定义中给出。但是不能在这两处同时出现
- 对于有参数列表的函数,必须从右到左添加默认值
常见的系统函数
- 平方 //num的二次方
cout<< pow(num, 2)<<endl;
double dou = 10000.00 / 3.0;
cout<< dou << endl;
- 强制以小数方式显示
cout<< fixed;
cout<< dou << endl;
- 控制显示的精度
cout<< setprecision(2);
//#include <iomanip>
cout<< dou << endl;
- 设定宽度(设定自己右边最近的成员的总宽度)
cout<<"|"<<setw(8)<<3.14<<"|"<<endl;
- 设置填充
cout<<setfill(‘_’)
cout<<"|"<<setw(8)<<3.14<<"|"<<endl;
自定义函数
- 函数定义时可以不写参数名只写参数类型
- c++函数是不能返回数组类型的(不过可以返回结构体类型。结构体成员可以有数组)
函数指针
//函数原型
double Sum(double, double);
//函数指针声明
double (*PtrSum)(double, double);
//指向函数
PtrSum = Sun;
内联函数 inline
- 是c++为提高程序运行速度所做出的一项改进,与常规函数的区别不在于编写方式在于被调用时的运行机制。
- 普通函数:函数是有指针的 调用时是去找这块地址上的函数 调用它
- 内联函数:是类似宏一样替换这段
//定义内联函数
Inline void fun1()
{
cout << 1;
}
构造函数和析构函数 构造函数实现时可以通过:的方式直接对函数成员变量赋值 ,这里也可以直接调用其他函数
- 构造函数 无返回值类型 无返回值,与类名相同的函数,new类时会直接调用它,一般最常用于初始化成员
- 析构函数 无返回值类型 无返回值,~+类名的函数,对象被销毁时调用它,一般最常用于回收空间
友元函数 friend
- 参数是对象 函数内部可以访问对象的私有成员。
- 流运算符一般采用友元函数进行重载。
复制构造函数(拷贝构造函数)
深复制/浅复制 如果复制双方存在指针 ,要采用深复制 ,否则会出现 ,复制前后两个变量指向一块内存空间的情况 ,导致改变其中一个值另一个值也会跟着变。
这里类是一个类型
类名::构造函数名(const 类型 & 参数名);
String::String(const String &str)
{
m_length = strlen(str.m_value);
m_value = new char[m_length + 1];
strcpy(m_value, str.m_value);
}
继承 公有继承/私有继承/保护继承 class 子类 :父类{ } 重写
多态 父类指针new子类 重写的函数后调用父类的函数 虚函数
this指针
- 每个成员函数(包括构造函数 析构函数)都有一个this指针
- 可以通过this关键字访问当前对象的成员 this -> 变量名; this -> 函数名();
- 函数返回值类型是 对象的引用时。采用 return *this; 的方式返回当前对象。
const
-
修饰成员变量 只能在声明时进行一次赋值,不能进行修改。
-
修饰指针 const 在号左侧(表示指针所指向的数据是常量,指针指向的内容是不可以修改的但是指向的位置是可以修改的) const 在号右侧 (表示指针本身是常量,不能指向其他内存空间,指针指向的内存空间可以修改) *号两侧都有 const 那么则都不可以修改。
Int num1 = 1024;
const int num2 = 2048;
const int* ptr_num1 = &num1;
ptr_num1 = &num2;//合法
-
修饰函数参数 函数体内不可被修改
-
修饰引用类型的参数 这里假设传递的是一个对象的引用那么只能调用 这个对象本身的 被const修饰的成员
-
修饰引用类型返回值 避免直接修改返回值。/ 返回值是对像的话 不要返回局部对象,直接返回引用(这里原理和指针一样)
-
修饰函数 (写在函数括号后面) 内部不允许调用非const修饰的成员
St& Getst(st&) const;