C++基础学习

191 阅读6分钟

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)

访问权限修饰符

  1. Public  公用访问权限。任意地方访问
  2. Private 私有访问权限。只能在本类或友元函数中访问
  3. Protected 继承访问权限。只能在本类和其子类以及友元函数中可以访问

可以利用public 权限的get/set函数来访问没有权限的成员 利用get/set封装后可以添加一些安全访问判断比如赋值的限制等,

c++相对c语言扩展

  1. 添加了面向对象的机制(函数重载,函数模版,带默认值的函数,继承,重写)
  2. 提供了标准的输入输出对象(cin cout)
  3. 引用类型
  4. string类型等
  5. vector.

文件转化过程 .c -> .exe

  1. 预处理 宏定义展开,头文件展开,删掉注释包括#if #end if 语句 gcc -E A.c -o A.i
  2. 编译 检查语法,将预处理后的文件编译成汇编文件 gcc -S A.i -o A.s
  3. 汇编 将汇编文件转换为二进制文件gcc -C A.s -o A.o
  4. 连接 连接 库 连接头文件 gcc A.s -o A.exe

内存分配

  1. 栈区:由编译器自动分配和释放。(函数参数 局部变量 等)自动回收的空间
  2. 堆区:由程序员自己分配释放程序结束时操作系统回收 (new 等) 需要手动回收的空间
  3. 全局区:存放全局变量和静态变量程序结束后由系统释放 (全局变量。静态变量)
  4. 文字常量区:存放常量字符串,程序结束后由系统自己释放
  5. 代码区:存放函数体二进制代码.

动态内存分配:(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;
       }
    
    

所谓泛型编程就是由函数模版提供的支持

带默认值的函数

  1. 默认值可以在函数的原型或者定义中给出。但是不能在这两处同时出现
  2. 对于有参数列表的函数,必须从右到左添加默认值

常见的系统函数

  • 平方 //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;

自定义函数

  1. 函数定义时可以不写参数名只写参数类型
  2. 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;