C/C++ 基础复习及补充

C/C++ 基础复习及补充

本文章仅为个人学习,如有错误之处请指正。

C标准库-注释-条件编译

C标准库在C++中可用,由x.h --> cx 如:string.h --> cstring stdio.h --> cstdio math.h --> cmath,有部分不变,如malloc.h

/*……*/ 快注释 // 行注释

条件编译,如

#if 1
……
#endif
#if 0 …… #else …… #endif
#if 1 …… #elif …… #elif ……#endif
#ifdef XXX …… #endif
#ifndef XXX …… #else …… #endif

C++标准输出输入流-名字空间

iostream库 i in o out

cout是标准名字空间std的一个名字,使用必须加上名字空间限定std::

名字空间避免引用多个库中方法(函数)重名的影响

也可以用:using std::cout 或 引入整个名字空间:using namespace std

<< 输出流运算符 a << x 将数据x输出到a

std::endl 相当于 \n

cin 标准输入流对象 输入运算符>>

cin >> a >> b >> c;
cout << a << b << c;

对文件同理,如

#include<iostream>
#include<fstream>
#include<string>
using namespace std;
int main(){
    ofstream oF("test.txt");
    oF << 22 << " " << "Are you ok ?\n";
    oF.close();
    ifstream oF("test.txt");
    double a;
    string str;
    iF >> a >> str;
    cout << a << " " << str << endl;
    return 0;
}

引用变量、引用形参

引用变量,相当于给一个变量去了一个别名

且引用变量“从一而终”,只能引用唯一已经定义的 其改变会改变所引用的变量

对引用变量的操作就是对其所引用的变量的操作

#include<iostream>
int main(){
    int a = 1;
    int r = &a;	//r与a是同一块地址的名称
    return 0;
}

引用变量和被引用的变量的变量类型必须匹配

引用形参,可以在函数中实现之间改变参数的值

C中有堆栈,存储局部数据

C中函数的形参是值参数,形参为函数的局部变量,有独立的内存块,调用时是将实参的值copy到(赋值)形参中,对此形参的改变不会影响实参 若要实现改变需要将形参设为指针变量(函数值的值形参:传递指针),传递(获取)实参的地址( & 获得实参地址),通过 * 获取地址的值对其修改

void fun_p(int *a,int *b){	//指针
    *a--;
    *b++;
}		//使用	fun_p(&m,&n);
void fun_g(int &x,int &y){	//函数引用形参	引用实参 对实参进行修改
    x--;
    y++;
}

函数的默认形参、函数重载

默认形参 (可选参数) 形参可以有默认值 默认形参必须在非默认形参右边

int fun_1(int n,int m = 1){
    ;;
}
double fun_2(double n,double m = 1.1){
    ;;
}

函数重载 C++允许作用域内有同名函数,但形参应不同

函数的签名 = 函数名 + 形参列表

使用时寻找对应实参类型的函数

注:函数重载无法根据返回值的类型区分函数

函数模板

通用算法(泛型算法):函数模板 可以通过更改typename实例化对应不同数据类型(如:string int double float …… )的函数

用template关键字增加一个模板头,将数据类型变成类型模板参数

#if 1
#include<iostream>

template <typename T>
T fun_m(T n,T m){
    return n + m;
}

int main(){
    std::cout << fun_m<int>(1,2) <<std::endl;	//int 为给模板传递需实例化的参数
    std::cout << fun_m(1.1,2.2) <<std::endl;	//编译器也会自动识别数据类型生成对应函数
	return 0}
#endif

用户自定义类型string和vector

string 用户自定义类型 表示符串

可以用成员访问运算符 . 访问string类的成员 或用运算符对string对象进行运算,如 +、[]下标运算符

#include<iostream>
using namespace std;
int main(){
    string sr_1 = "hello",sr_2("world");
    s.size(); //返回字符串长度
    string sr_3 s.substr(1,3); //相当于切片
    string sr_4 = sr_1 + " " + sr_2;
    sr_3[0] = 'A';
    int pos = sr_4.sr_1.find("llo"); //find函数 返回llo字符串所在的位置 下标
    sr_1.insert(1,"OK"); //在下标为1的位置插入
    return 0;
}

vector

C/C++静态数组 内在数组 存储在程序内的堆栈中 定义后,大小固定无法

vector 向量(动态数组) 类似以数组 可动态增长 需使用 vector 库

是一个类模版 通过实例化产生一个类 如:vector<dounble> 产生一个数据元素为 double 的 vector<int> 类(向量)

可通过 vector<int> 类对象去访问其成员,如成员函数 也可用运算符进行一些运算

#include<iostream>
#include<vector>
using namespace std;
int main(){
    int arr[] = {22,43,66}; //静态数组一旦定义后无法改变
    std::vector<int> v_1 = {23,56,76}; //初始化
    vector<int> v_2; //空的
    v_1.push_back(88); //push_back(),最后添加元素
    V_1.pop_back(); //与push_back(),删除最后一个元素
    //在前面或中间插入数据	任意处插入删除 ?
    //.size()	获取符串长度	[]下标索引
    v_1.resize(); //重新定义大小
}

指针和动态内存分配

指针 指针即地址 通过 &(取地址运算符) 获得变量的地址 &a

指针变量 存储指针的变量(只能存储对应类型的变量) 通过 *(取内容运算符) 可以得到一个指针变量所指向的变量

T *p; //p是存储"T类型变量的地址"的变量
*p:   //获取p所指向的内容
int *p = &a;
int *q = p;
{	//指针访问数组元素 
    int arr[] = {12,34,45}; //局部变量	数据存放在堆栈区	程序内的
    int *p = arr; //数组名就是数组的第一个元素的地址即 &(arr[0])	*p[1]即*(p+i)
    cout << *(p+1) << " " << p[1] << arr[1] << endl;
    for(int *q = p + 3;p < q;p++)	cout << *P <<'\t'; 
} //访问必须在已经定义的元素

动态内存分配

堆存储区 所有程序共享的内存

new 用来申请内存块 delete 用于释放内存

C中 malloc free realloc

{
    int *p = new int; //分配int大小的内存	指针存储在int类型指针变量p中
    //C中使用 malloc 库实现动态内存分配
    //C++此处使用 new 不仅能(申请)分配内存,还能给对象进行一定的初始化
	*p = 3; //通过指针变量访问 new 申请的内存
    delete p; //释放内存	如果不处理	会内存泄漏
    p = new int;
    delete p;
}
{
    int n  = 3;
    int *p = new int[n]; //指针指向多个int型内存	p指向第一个 int[0]
    //通过 p[i] *(p+i) 访问
    for(int i = 0;i < n;i++)		p[i] = i;
    for(int *q = p + n;p < q;p++)	cout << *P <<'\t';
    char *s = (char *)p; //强制类型转化	int型 4字节	char型一个字节
    //对与指针变量s认为指向的是char
    int char_n = n * sizeof(int) / sizeof(char); //sizeof 返回字节长度
    for(int i = 0;i < char_n;i++)			s[i] = 'S' + i;
    for(char *r = s + char_n;s < r;s++)		cout << *s;
    //delete p; 仅能释放第一个
    delete[] p; //全部释放
}
{
    T *p = new T;
    delete p;
    T *p = new T[n]; //组	n具体值	可以提前定义	如:int n = 3;
    delete[] p;
}

类和对象

变量 对象 存储数据的内存块

过程 函数 对数据进行处理

面向对象编程

过程式编程:问题分为子问题,编写对应函数 C中一个程序由函数构成,函数由一系列指令语句构成(程序块) 数据和对数据处理的函数 分离

数据可被任何 函数访问 数据不安全 维护麻烦

面向对象编程:数据是隐藏的 程序由不同种类的对象相互协作完成 对象间通过发送/接受消息来协作完成各种任务 由面对对象编写的程序成为"对象式系统"

对象 由自己的属性和对应的(行为)功能 messages 识别有什么对象

  • 对象设计
    • 从具有共同特征的许多对象抽象出(类)某种概念 从此抽象实例化为具体对象
      • 某些(类)概念之间可能存在某种关系关系 概念之间具有组合(包含)关系
      • 具有派生关系、继承 – (类)概念之间具有继承(派生)关系
  • 面向对象编程与过程式编程
    • 过程式编程 用内在类型(概念)如:int float,用机器类型的概念去解决复杂问题,不易于思考问题
    • 面向对象编程 使用符合人们思维的概念来思考问题,更方便理解、查错、组装(更进一步抽象)
  • 用户自定义类型
    • 自己定义自己"用户自定义类型",如 类型,来表示各种应用问题的各种概念
    • C++标准库已经提供了很多实用的"用户自定义类型"
      • cout 是 ostream 类的对象(变量) cin 是istream 类的对象(变量) 可向其发送消息
      • string是一个表示字符串的类,通过 . 访问 str.size() 茶寻该对象包含的字符数目
    • 一个用户定义类型包括
      • 属性
      • 操作 运算
      • 访问权限 私有 共有
    • 靠虑多个用户定义类型的关系
    • 具体对象如何交互协作 发送消息 使用成员函数

C++中定义类

用 class struct 关键字 定义的一个类就是一个数据类型

  • 包含关系 从一个类中实例化的一个对象,包含不同类型的成员

  • 类类型的变量通常称为对象

    • 通过 . 访问对象成员
  • 与内在类型一样,可定义类类型的数组 存储一组类类型变量

  • 可以定义类类型 指针类型变量 如:T * 就是T指针类型 T *p p 为T指针类型变量

    • 指针变量可以指向一个类对象
    • 间接访问运算符 -> 取内容运算符 *
      • 如:(*p).xxxx p -> xxxx
    • 指向动态分配的指针变量
      • 使用 new 与 delete 关键字
  • 类成员函数

    • 类内定义函数

      • struct So_M{
            string name;
            void fun_prt(){cout << name << endl;};
        };
        
    • 类外定义函数

      • struct So_M{
            string name;
            void fun_prt(); //函数声明
        };
        void So_M::fun_prt(){ //建立So_M内的函数	So_M名字空间内的函数
            //名字空间So_M 类作用域 如果没有则 为普通函数
            cout << name << endl;
        };
        

实例:

struct Student{
	string name;
	int age;
};
//类类型的变量通常称为对象
Student stu_1; //实例化Student类	stu_1是Student类的一个实例
//通过 . 访问对象成员
stu_1.name = "jj";
stu_1.age = 20;
//与内在类型一样,可定义类类型的数组	存储一组类类型变量
Student stu_s[3];
//类类型指针类型变量
Student *p; //定义Student指针类型变量
p = stu_s + 2; //指向第三个stu_s	将其地址放到 p 指针变量中
(*p).name = "OK";
p -> age  =  18 ;
//指向动态分配的对象
Student *q = new student; //存放在堆存储区	所有程序都可以访问
q -> age = 22;
delete q; //不用时释放内存防止	内存泄漏
q = new student[3]; //指向3个student类型的内存空间的起始地址	student[0] 的地址
p[1].age   = 15;
*(q+2).age = 16; //可以通过 (*(q+1)) 以保证顺利访问
q -> age   = 78;
delete[] q;

成员访问运算符 . 间接访问运算符 -> 取地址运算符 & 取内容运算符 *


学生成绩分析案例

MY_DO : 使用静态组 堆栈区

#if 1
#include<iostream>
#include<string>
using namespace std;
struct student{
    string name;
    int scroe;
    void print();
};
void student::print(){
    cout << name << " " << scroe <<endl;
}
int main(){
    int i,j = 0;
    int m,n = 5;
    cout << "Inter number of students" << endl;
    student stu_s[n];
    for(i = 0;i < n;i++)	stu_s[i].name  = 'A' + i;
    for(i = 0;i < n;i++)	stu_s[i].scroe = 111 + i;
    for(i = 0;i < n;i++)	j += stu_s[i].scroe;
	cout << "Average scroe is " << j/n << endl;
    j = stu_s[0].scroe;
    for(i = 1;i < n;i++)
        if(j < stu_s[i].scroe){
            j = stu_s[i].scroe;
            m = i;
        }
    cout << "Max is ";
    stu_s[m].print();
    for(i = 1;i < n;i++)
        if(j > stu_s[i].scroe){
            j = stu_s[i].scroe;
            m = i;
        }
    cout << "Min is ";
    stu_s[m].print();
    for(i = 0;i < n;i++)	stu_s[i].print();
    return 0;
}
#endif

使用动态组 vector自定义类型

#include<iostream>
#include<string>
#include<vector>
struct student{
    string name;
    int scroe;
    void print(){
        cout << name << " " << scroe <<endl;
    }
};
int main(){
    vector<student> students;
}

this 指针、访问控制、构造函数

this 指针:成员函数实际上隐含的一个this指针 这玩意相当于 python 中的 self 指向储存对象的地址 不过定义函数时不用传参

通过对象调用成员函数 C++中类的内部函数调用时编译器会将其转换为普通的外部函数

struct student{
    string name;
    int scroe;
    void print(){	//此函数会被编译器转化为 参数为指针的外部函数
        cout << this -> name << " " << this -> scroe <<endl;
    }	//故可以在成员函数中加上 this 关键字
};
//假定的转化
void print(student *this){	//转换后的函数	this 指针变量 指向调用此函数的对象
    //this 存放 调用此函数的对象 的地址
    cout << this -> name << " " << this -> scroe <<endl;
    //通过this指针访问 对象的成员变量
    }
student stu_1;
stu_1.print(); //相当于 print(&stu_1)	&stu 指针

访问控制 使用 public private 关键字

  • class 内的成员默认是 private(私有) 外部函数无法直接访问对象的私有成员
    • public 任何函数都可以修改对象成员的数据
    • private
  • struct 内的成员默认是 public(共有、公开)
class student{
private:	//设置 private成员	声明私有	有保护作用
    string name;
    int scroe;
public:		//设置 public成员	声明共有	对外的接口
    string name;
    int scroe;
    void print(){
        cout << name << " " << scroe <<endl;
    }
    //同设置成员函数	访问修改私有成员变量	保证自己修改自己数据
    string get name()		{ return name; }
    string get age()		{ return age; }
    void set_name(string n)	{ name = n; }
    void set_age(double g)	{ age  = g; }
};

构造函数

函数名与类名相同且无返回值类型的成员函数

  • 如果没有在类中创建构造函数 C++默认生成 student(){} 的构造函数
  • 定义了构造函数C++则不会生成默认的构造函数
class student{
private:
    "string name;
    "int scroe;
public:
    string name;
    int scroe;
    student(){}	//默认构造函数
    student(string n,int g){	//不是默认构造函数
        name = n;	age = s;
    }
    void print(){
        cout << name << " " << scroe <<endl;
    }
};
int main(){
    student stu_1; //在创建一个类对象时会自动调用称为"构造函数"的成员函数
    //构造函数完成对对象的创建工作	如果未在类中定义构造函数C++会生成默认的构造函数
    student stu_1("G",13);
    student stu_s[3];	//创建定义类类型的数组	类必须有默认构造函数	否则无法成功	数组有多个对象	没法传参
    return 0;
}

运算符重载

运算符重载:针对用户自定义类型(类定义)重新定义运算符函数

//定义输入输出流运算符	只能作为外部函数
class student{
private:
    string name;
    int scroe;
public:
    student(string n,int g){	//不是默认构造函数
        name = n;	age = s;
    }
    //将外部函数声明为友元函数 使得其可以访问中的私有属性
    friend ostream& operator<<(ostream &o,student s);
    friend istream& operator>>(istream &in,student &s);
};
ostream& operator<<(ostream &o,student s){ //<< 本身是一个函数 返回值是其本身 返回的是引用
    cout << s.name << "<-->" << s.score <<endl;
    return o;
} //对 输入流运算符 输出流运算符 进行重载
istream& operator>>(istream &in,student &s){ //>> 本身是一个函数 返回值是其本身 返回的是引用
    in >> s.name >> s,.score;
    return in;
};
student stu_1;
cin >> stu_1; //operator(cin,stu_1) 调用后值是 cpoy 故使用引用地址
cout << stu_1;	//偌未重新定义cout 函数(对cout增加功能) 输出流运算符无法识别	实际过程 此函数相当于 operator(cout,stu_1)

运算符重载 C++有很多运算符可以重载

#if 1
#include<iostream>
class Point{
    double x,y; //默认是私有的
public:
    Point(double __x,double __x){
        x = __x;	y = __y;
    }
    double operator[](int i) const{ //下标运算符定义	取值	下标运算符必须定义再类内 为成员函数	cout函数	说明不会对私有属性 x,y 修改	从而使函数签名不同
    	if (i == 0)			return x;
        else if (i == 1)	return y;
        else thorw"下标失误"; //抛出异常
    }
    double& operator[](int i){ //返回的是引用变量
    	if (i == 0)			return x;
        else if (i == 1)	return y;
        else thorw"下标失误"; //抛出异常
    }
    Point operator+(const Point p){ //由于是内部(成员)函数	故调用时 会自动传递一个掉用的对象的参数 this
        return Point(*x + q[0],y + q[1]);
    } //则 调用时 p + q 实际为 p.operator+(q)
    friend ostream& operator<<(ostream &o,Point p);
    friend istream& operator>>(istream &in,Point &p);
};
ostream& operator<<(ostream &o,Point p){
    cout << "(" << p.x << "," << p.y << ")" <<endl
    return o;
}
istream& operator>>(istream &i,Point &p){
    i >> p.x >> p.y
    return i;
}
#if 0
Point operator+(const Point p,const Point p){
    return Point(p[0]+q[0],p[1]+q[1]);
} //可以放到类内 作为成员函数 此调用 q + p 实际上为 operator(p,q)
#endif
int main(){
    Point p(2.2,3.3);
    cout << p[0] << " " << p[1]; //p[i] 实际掉用 成员函数 p.operator[](int i)
    //p[1] 返回的不是可修改的值 是从x复制来的值	若改为引用则可以
    p[1] = 1.2
    return 0;
}
#endif

String类、拷贝函数、析构函数

拷贝函数 自定义类型 复制时调用

析构函数 自定义类型 释放内存时调用 即使释放内存

class String{
private:
    char *data; //C风格的字符串
    int n;
public :
    ~String() { //析构函数 释放内存	后到先销毁
        if(data)	delete[] data;
    }
    #if 0
    String (const String &s){ //硬拷贝
        data = s.data; //两者一样 指针一样	指向同一块内存	故修改一个另一个跟着改变	一个内存被释放另一个也会如此
        n = s.n;
    }
    #endif
    String (const String &s){ //构造函数先来后到
        data = new char[s.n + 1];	//指向一块新的动态内存	使两者互不干扰
        n = s.n;
        for(int i = 0;i < n;i++) //i <= n 将结束字符copy进入 无则自行加入
            data[i] = s.data[i];
        data[n] = '\0';
    }
    String(const char *s = 0){
        if(s = 0)	{data = 0;n = 0;}
        else	{while (*p)	p++;}	//结束字符就是0
        n = p - s;
        data = new char[n+1]; //动态内存分配
        for(int i = 0;i <= n;i++) //i <= n 将结束字符copy进入 无则自行加入
            data[i] = s[i];
        "data[n] = '\0';
    }
    int size()	{return n;}
    char operator[](int i) const{
        if(i < 0 || i >= n)	throw"下标错误"; 
        else	return date[i];
    }
    char& operator[](int i){
        if(i < 0 || i >= n)	throw"下标错误"; 
        else	return date[i];
    }
    friend ostream& operator<<(ostream &o,String s);
};
ostream& operator<<(ostream &o,String s){
    for(int i = 0;i < s.size();i++)
        cout << s[i];
    return o;
}
String sa = sb;	//= 实际上调用了copy构造函数	未定义定义是编译器自定生产硬copy函数
//一个函数结束时会释放 程序内的 堆栈区内存

C/C++ 一般函数 传值为 copy 方式

类模板

vector 是C++中的向量 类模板

自定义Vector 把类型泛化 相应类型 改为模板类型参数 使得统用

struct Point{
    int x;
    int y;
    Point(int tx,int ty)	{x = tx;y = ty;}
    friend ostream& operator<<(ostream &o,Point p)
};
ostream& operator<<(ostream &o,Point p){
    cout << "(" << p.x << "," << p.y << ")" <<endl
    return o;
//类模板 用来生成类的
template<typename T> //把存储	需要改动的数据类型改为 T
class Vector{
    T *data;		//指向动态内存
    int capacity; //空间容量
    int n;
public:
    Vector(int cap = 5){
        data = new T[cap];
        if(data == 0){
            cap = 0;n = 0;break
        }
        capcity = cap;
        n = 0;
    }
    void push_back(T e){
        if(n = capacity){ //空间已满
            T *p = new T[2*capacity];
            if (p){
                for(int i = 0;i < n;i++)
                    p[i] = data[i]; //将数据复制到 p 所指的新空间中
                delete[] data;
                data = p;
                capacity = 2*capacity;
            }
        }
        data[n] = e;
        n++;
    }
    T operator[](int i) const{
        
    }
    T& operator[](int i){
        if(Ti < 0||i >= n) throw"错误下标";
        return data[i];
    }
    int size(){
        return n;
    }
};
Vecter<double> v; //类模板 实例化生成一个对应类型的类
Vexter<student> v

C++补充

命名空间 namespace

使用

  • 权限式 (限定) std::xxx
  • using std::xxx 指定单个导入
  • using namespace std 全部导入

全剧命名空间

  • ::xxx or Global::xxx

风格 ?导入 ?

命名空间改 namespace stco = std::count

命名空间 不同文件可以相同 可以进行 函数重载

  • 优先选择近的
  • using Base::fun 使内部空间(子类可以试用 fun)
  • 可以使用匿名名字空间

与 C 兼容 extern 'C'

常量部分

时间维度

  • 与编译期
  • 编译期
  • 运行期

目标为度

  • 对象(变量)
  • 函数
/***
 * : 如何优雅的使用常量
 * : 由 #define	const	constexpr
 */

// 宏 #define 为预编译期的 真常量
// 名字空间 + 使用 using const	using 防止写库时入侵	会通过 指针 使用 修改
// const readonly
// constexper 常量表达式	编译期

//头文件使用

模板元编程 ===> constexper

编译器 在编译期 进行 编译器处理

template <int i> struct A {}
template <> struct A<0> {}

template <int>
struct A {
    enum {v = A<n-1>::v};
};

template <int base, int exp>
struct Pow{
    enum {v = base * Pow<base, exp-1>::v};
};	// 递归调用
template <int base>
struct Pow<base, 1>{
    enum {v = base};
};	// 模板偏特化 递归终止条件	模板特化

constexper 使模板元编程比较方便 使编译器实现取得数值

constexper 是 inline

constexpr auto fact(int n) {
    if (n == 1)	return n;
    return n * fact(n-1);
}	// constexper 修饰进行 使编译器计算

// 使用
constexpr auto a = fact(8);	// 强制编译器执行
// 也可以正常使用
  • 模板元编程 性能更高
  • constexpr if 语句 尽量不用递归 C++14 以上
  • 两者的底层实现不同
  • constexpr 使 模板元编程更方便 constexpr if 语句 更方便 C17

编译期 不应有运行时的东西

  • 18
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值