C++基础回顾(上)

C++基础回顾(上)

封面

前言

C++之前学过一点,但是很长时间都没用过,翻出了书从头看了一遍,简短地做了笔记,以便自己之后查看和学习。

关键字和标识符

C++标识符是由字母、数字、下画线组成的,而且必须以字母或下画线开头

程序的约定俗称:

  • 对象名一般用小写字母;

  • 单词之间可用下划线或者每个单词首字母大写

字符型char用来保存机器基本字符集中对应的整数值,即该字符的ASCII码值

访问一个对象所在内存空间中的数据,需要知道其内存空间所在的内存地址,这通过对象的名字来实现。也就是说对象名本质上是内存地址的一个映射。

创建对象时最好提供一个初始值,以完成初始化。使用未初始化的对象是很危险的,有可能引起严重错误。

const修饰符

  • 不希望对象的内容发生变化

  • const对象创建后,其值不能再改变,因此const对象必须初始化。

typedef

typedef double price //price 是double的一个类型别名

using 关键字也可以声明别名

using price = double; //price 是double的一个类型别名

auto 关键字被利用来根据初始值的类型自动推导出需要的数据类型

左值所在的内存空间的地址是可以获取的,而右值的地址是无法得到的。

运算符

<< 输出运算符

>> 输入运算符

自增和自减运算符

int i=0;
i++;
++i; //这两个的作用都等同于i=i+1
//但是他们放到表达式中就不同了
int j;
j=i++;//先赋值,再自增,即j=0,i=1了
j=++i; //先自增,再赋值,即j=1,i=1了
//自减运算符类似

符号优先级:逻辑非最高,其次是关系运算符,最后是逻辑与和逻辑或

条件运算符?: 三目运算符

cond?expr1:expr2判断cond表达式,真则返回expr1,假则返回expr2

{

} //用花括号括起来的语句是复合语句,构成一个语句块,在块中引入的名字只能在块内可见。

数据类型

复合类型: 指针、引用、数组、函数、联合体、枚举类型

引用:

  • 为已经创建的对象重新起一个名字,编译器只是将这个别名绑定到所引用的对象上,不会复制对象的内容给引用。

  • 相当于还是那一块内存,只不过这个内存的名字多了,比如小明还可以叫明明。

    int counter=0;
    int &refCnt =counter; //refCnt引用counter对象的内容
    
  • 右值引用:C++11新引入。可以操控右值对象了,尤其是编译器产生的临时对象

    int i=0;
    int &&rr1=i+1; //右值引用,绑定到一个临时对象,临时对象的生命期得到了延续,续命了。
    

指针:

  • 存放的是数据的内存地址,然后通过这个对象对数据进行访问。这种专门用来存放地址的对象称为指针对象。

    int i =100;
    int *ptr=&i; //&在这里是取址符 现在ptr对象里存放的就是i的地址。
    
  • 要访问i的内容,需要通过解引用操作符*来实现

    *ptr=10; //读取对象i的内容
    
  • 避免使用野指针,如果指针对象在定义时没有具体的指向对象,则需要用nullptr来初始化。

    {
    int *ptr1 =nullptr; //空指针
    int *ptr;  //野指针
    }
    
  • 多级指针

    把一个指针对象的地址存放到另一个指针对象中,则形成了指向指针的指针

    int i=1, *ptr =&i;
    int **pptr =&ptr; //指向指针的指针,pptr中存储的是ptr的地址,而ptr中存放的是i的地址
    
  • 引用可以看作是支持自动解引用操作的const指针

数组

  • 数组是有限个同类型元素组成的有序集合,所有元素顺序存放在一段连续的内存空间中。

    int arr[5] //存储5个整型元素的数组

  • []中的值必须是大于0的整型常量表达式

    unsigned cnt =10;
    int arr[cnt];  //不能这样定义,cnt并不是常量表达式
    constexpr int sz =10;
    int arr[sz]; //可以
    
  • 字符数组会在末尾自动添加字符串结束符’\0’

  • 举例

    inr arr[5] ={1,2,3,4,5};
    for (auto i:arr){  //auto自动推断数据元素的类型
    	cout<< i<<endl;
    } //这里只能进行读操作,因为i只是一个临时对象,他与数组arr并没有产生实际上的联系,我觉得
    
    //可以进行写操作的例子
    for (auto &i:arr){
    	i=0;
    } //此时i是引用了,相当于arr的别名,映射的地址都是相同的,因此对i赋值,能够修改arr的内容
    
  • 多维数组

    int a2d[3][5]; //二维数组 3*5
    
  • 指针指向数组

    一般情况下,编译器对数组的操作都会转换成对指针的操作,数组名通常被转换成数组第一个元素的地址

    int arr[] ={1,2,3,4,5};
    int *p =arr; //arr被转换成arr[0]的地址
    int *p =&arr[0]; //与上一句等价
    
    
  • 可以用指针访问数组,比如移动指针位置,从而访问数组中的其他数据。但要注意,指针运算过程中不能超出数组的范围了,不然就不知道指向的地址是哪里了。

vector

  • 容器类型,能够存放类型相同的元素,但是支持变长操作,对容量大小可根据需要进行动态调整。

  • 定义和初始化

    vector<int> v1; //存放整数的空vector
    vector<int> v2={0,1,2}; //存放三个int元素的vector
    
  • vector<int> vi;
    vi.push_back();  //尾部插入一个元素
    vi.pop_back(); //尾部删除一个元素
    vi.clear(); //移除所有元素
    vi.at(1) //访问vi的第二个元素
    
  • 迭代器

    为了支持随机访问, vector中有一个叫迭代器的东西,通常借助容器的成员函数begin和end获取

    vector<int> vi={0,1,2,3};
    auto itb=vi.begin(); //itb指向vi的第一个元素
    auto ite=ci.end(); //ite指向vi的尾后元素
    //itb和ite都是vector<int>::iterator的迭代器类型
    

    迭代器与指针类似,可以通过解引用来获取指向对象的内容。

    for (auto it=vi.begin();it!=vi.end();++it){
    	*it *=2; //每个元素乘2
    	cout<<*it<<endl;
    }
    

枚举类型

  • 不限定作用域和限定作用域方式的枚举类型

  • 不限定作用域的枚举类型和限定作用域的枚举类型

    enum color{red,green,blue}; //enum关键字来定义不限定作用域的枚举类型 
    enum class  stoplight{red,green,yellow}; //enum class 来定义限定作用域的枚举类型
    color c =red; //访问color的枚举成员,限定作用域的不能这样访问了
    stoplight b =stoplight::red; //访问限定作用域的枚举类型
    stoplight a =red; //不行
    
    

函数

一个函数的定义由四部分组成:返回值类型、函数名、形参列表和函数体。错误分为语法错误和逻辑错误。

int main(){
	return 0;
}

函数调用过程中,相对应的实参类型和形参类型必须匹配。

和对象的名字一样,函数的名字必须在使用之前声明。和函数定义不同,函数声明只需要描述函数的返回类型、名字和形参类型即可。

int maximum(int a ,int b); //因为声明没有函数体,因此形参名称也是可以省略的
int maximum(int, int);

一般来说,函数或对象声明放在头文件,相应的定义放在源文件。

局部对象和全局对象

​ 存储周期:对象在内存中存储的时间

存储周期类型作用周期存储位置
自动存储周期定义位置在内存中创建,离开作用域时释放
静态存储周期static关键字声明的对象,程序运行期间,始终存在全局数据区
动态存储周期new运算符生成的对象,delete释放内存空间
线程存储周期thread_local关键字创建的对象,在其所在的线程创建时开始,线程结束时释放

全局对象具有外部链接性,可以在其他的文件中访问

int g_val =10; //g_val在fun.cpp中定义的全局对象,具有外部链接性
extern int g_val; //extern声明可以使得main.cpp也能访问到g_val
static int gs_val=20; //静态对象,在外部访问不了
int main(){
	cout <<g_val+gs_val;
} //输出30

参数传递

函数的实参和形参的交互方式:单向的值传递,双向的引用传递

单向的值传递中,函数的形参发生变化,不会影响到实参

void Swap(int x,int y){
	int z(x);
	x=y;
	y=z;
}//形参x y交换

int i(4),j(5);
Swap(i,j);
cout<<i<<j<<endl; //实参 i j的内容并没有被改变

地址传递,可以通过形参改变实参的值。

void Swap(int *x,int *y){
	int z(*x);
	*x=*y;
	*y=z;
}//形参x y所指向的地址的内容交换

int i(4),j(5);
Swap(&i,&j); //取址符
cout<<i<<j<<endl; //实参 i j的内容交换了

引用传递,类似地址传递

void Swap(int &x,int &y){ //引用,即别名
	int z(x);
	x=y;
	y=z;
}//形参x y内容交换

int i(4),j(5);
Swap(i,j); 
cout<<i<<j<<endl; //实参 i j的内容交换了

const形参

const形参好像在程序应用很广泛

void fun(const int i); //只可以读i,不可写
void fun(const int *i); //可以更改i的指向,但是改不了指向的地址的内容
void fun(const int &i); //只读,不可写

上述值传递对于实参是安全的,但是对于数据大的对象复制操作低效;引用可以避免复制,但是实参不安全;const 引用形参安全高效。

向main函数传递实参

//main函数有两个可选的形参
int main(int argc,char*argv[]){

}
//第一个形参argc的值是argv的元素的个数,第二个形参是一个数组,每个元素是指向C风格字符串的指针 
//argv[0]保存的是可执行程序的名字,可选实参从argv[1]开始

函数重载

  • 统一作用域下具有相同名字,但是不同形参列表的一组函数称为重载函数

    const int & getMax(const int &a,const int &b){} //这里的值返回的是一个int常量的引用
    const int & getMax(const int &a,const int &b, const int &c){} //重载
    const string & getMax(const string &a,const string &b){} //重载
    

默认函数

void turnoff(int time =21); //声明的时候给出了默认形参,调用时不提供实参,则采用默认值21

内联函数

inline void Swap(int &x,int &y){}; //inline关键字定义内联函数,直接在调用处嵌入内联函数代码,不发生函数调用,不产生函数调用开销,但对编译器只是建议

lambda表达式

  • lambda表达式可以理解为一个临时的匿名函数,表示一个可以调用的代码单元

    [captures](parameters)-> return type{statements}
    //parameters 、return type 、statements分别代表形参列表、返回值类型和函数体
    //[]代表lambda引导,captures子句指定在同一作用域下lambda主体访问哪些对象以及如何捕获这些对象,可以为空
    
    int divisor=5;
    vector <int > numbers{ 1,2,3,4,5,10,15,20,25,35,45,50);
    for each(numbers,begin(),numbers.end(),[divisor](int y){
    	if(y%divisor==0) //divisor 为外围divisor 的副本
    		cout <<y <<endl;//输出被 divisor整除的元素
    });
    

宏定义

  • 功能是定义一个标识符来代替一串字符,该标识符为宏名

  • #define 宏名 字符串常量
    #define PI 3.1415926
    

  • 类是用户自定义类型数据,基本思想是抽象和封装

  • 类是对一个事物的属性和操作的描述。例如对于一个分数类来说,基本属性有分子和分母,操作包括约分、计算分数值等操作。

    class Fraction{ //Fraction是类名 有两个数据成员和5个成员函数
    	//数据成员
    	int m_fenzi=0; //分子,默认值为0
    	int m_fenmu=1; //分母,默认值为1
    public: //public关键字后的成员时对外公开的,在程序的任何地方都可以访问
    	//成员函数
    	int fenzi() const{return m_fenzi;} //这里在圆括号后面引入const关键字是说明对this指针指向的const对象的数据成员进行写操作是非法的。
    	int fenmu() const{return m_fenmu;}
    	double value() const; //计算分数值
    	void reduce(); //约分
    private: // private 私有成员函数,只能在类的内部使用 
        //值得注意的是,不显式指明成员的访问属性,访问属性默认private
    	int gcd(int x, int y); //计算x和y的最大公约数
    
    }; //类中只有成员函数的声明,定义最好放在类外面
    
    double Fraction::value() const{
    	return static_cast<double> (m_Fenzi)/m_fenmu;
    } //成员函数value 的定义
    
    Fraction a;
    a.value(); 
    a.value(&a); //这两句是等价的,意思是当通过对象调用成员函数时,有一个隐式的指针类型形参this接受了调用对象的地址
    double value(Fraction *const this)const; //value 成员函数的声明自动转换为该形式,一个隐式的指针类型形参this
    

    struct关键字也可以用来定义一个类,使用struct定义中的类成员的访问属性默认为public

  • 友元函数

    不是类的成员却可以访问类的非公有成员。

    class Fraction{
    	friend ostream &print(ostream&os, const Fraction &a); //friend关键字声明友元函数
    };
    
  • 友元类

     class cricle; //类的前向声明
    class rectangle{
    	friend class cricle; //友元类
    }; 
    //友元都是单向传递的,你是我的朋友,但我不是你的朋友
    
  • 构造函数与析构函数

    类类型对象的初始化过程是由一类特殊的成员函数完成的,称为构造函数

    构造函数帮助对象创建时为数据成员执行初始化操作,只要创建类类型对象就会执行构造函数

    构造函数:1. 函数名和类名一致;2. 无返回值;3. 不能声明为const成员函数

    class Fraction{
    	public:
    		Fraction()=default; //默认构造函数
    		Fraction(int above, int below):m_fenzi(above),m_fenmu(below){} //显示定义构造函数,但是也是默认构造函数
    		Fraction(int above, int below){
    			m_fenzi=above;
    			m_fenmu=below;
    		}//这一个等同于上一个构造函数,但是上一个在m_fenzi和m_fenmu创建时就初始化了,这个还要再赋值一下,上一个执行效率更高。
    };
    

    析构函数

    class Fraction{
    	public:
    		~Fraction(){} //析构函数
    };
    
  • 静态成员:可以通过static关键字声明静态成员,但该成员是共享的,类对象不包含静态成员

    class Fraction{
    	pulic:
    		static int num;
    };
    Fraction a ;//a成员中不包含num这个成员
    

    如果您觉得我写的不错,麻烦给我一个免费的赞!如果内容中有错误,也欢迎向我反馈。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值