C++总结——语法篇

2023-05-16

    • Static关键字
        • 1、静态全局变量
        • 2、静态局部变量
        • 3、静态函数
        • 4、静态数据成员
        • 5、静态成员函数
    • 引用
        • 引用与指针的区别
      • 左值、右值、左值引用、右值引用
    • new delete与malloc free

Static关键字

1、静态全局变量

static修饰全局变量使变量成为静态全局变量。该变量存储在静态存储区,只能在本文件中使用,因此其他文件还可以定义名字相同的变量,不会发生冲突。

2、静态局部变量

是指static修饰局部变量。作用域仍然是局部作用域,存放在内存的全局数据区,直至程序运行结束前都不会消失,但只在定义它的函数中可见,只初始化一次。未赋初值时会自动为0。

3、静态函数

指static修饰函数时,该函数变为静态函数。只能在声明他的文件中可见,不能被其他文件使用。其它文件中可以定义相同名字的函数,不会发生冲突。

4、静态数据成员

static修饰类的数据成员。特点:

  1. 对于普通数据成员,每个类的实例都会有一份自己的拷贝。而对于静态数据成员来说,它不属于任何类的对象,无论类产生了多少对象,静态数据成员在程序中也只有一份,由该类的所有对象共享访问,即所有对象操作同一个静态数据成员。
  2. 静态数据成员定义时要分配空间,所以不能在类声明中定义。静态数据成员必须在类内声明,类外初始化。由于静态数据成员不属于任何一个对象,只属于类,所以对静态数据成员的所有操作(除声明外)都要使用类名作为作用域进行操作。并且静态数据成员和普通数据成员一样遵从public,protected,private访问规则。
  3. 静态数据成员存储在全局数据区,但静态成员变量不占用类的大小,在编译时创建并进行初始化

5、静态成员函数

  • 指static修饰类的成员函数。

  • 同静态数据成员一样,静态成员函数不属于任何一个对象,它为整个类服务,所有该类对象共享同一个函数。因此与普通的成员函数不同。由于普通的成员函数是属于类的某一个具体的对象,因此普通成员函数会有一个隐藏的this指针来操作该对象拥有的资源。但静态成员函数不属于任何一个对象,因此,静态成员函数没有this指针,也就不能访问属于类对象的任何的非静态成员函数和非静态数据成员。即:静态成员函数只能访问静态成员函数和静态数据成员。使用方法如下:
    1、使用对象访问
    2、使用类名访问。静态成员函数与静态数据成员一样,必须在类内声明,类外定义,并且定义时不能加static关键字。

  • 使用静态成员函数的一个原因就是可以用它在建立任何对象之前处理静态数据成员这是普通成员函数不能实现的。

  • 静态成员函数不能被申明为const,因为static成员不是任何对象的组成部分

  • 静态成员函数不能被申明为虚函数

  • 注意点:当使用类的成员函数作为仿函数时,如sort的第三个参数(排序时选择的算法),必须使用属于类而不是属于对象的静态成员函数。

class A {
public:
    static bool cmp(int& a, int& b) {
        return a > b;
    }
    void mySort(vector<int>& nums) {
        sort(nums.begin(), nums.end(), cmp);    //cmp函数的static不能省略
    }
};

引用

  • 引用必须初始化,并且引用一经定义之后就不能改变指向,引用必须引用合法的内存空间。
    但const int& ref = 10 可以被编译通过,因为编译器做了解释: int temp = 10; const int &ref = temp;
    指针与引用的区别
  • 引用的本质:指针常量(指向不可修改,内容可以修改)

引用与指针的区别

  • 指针是实体,引用是某块内存的别名。
  • 自增运算符意义不同:指针++是指地址自增,引用++是指值自增。
  • 指针可以改变指向,而引用只能在定义时被初始化一次,之后不可变
  • 引用不能为空,指针可以为空
  • sizeof(引用)得到的是所指向的变量的大小,而sizeof(指针)得到的是指针本身的大小,一般占4字节。
  • 引用不能与const连用(即没有&const),但指针可以(即有*const)
  • 指针可以有多级指针,而引用只有一级
  • 尽量不要返回函数内部new分配的内存的引用,容易造成内存泄露:如下面例子:
string& foo() {
    string* ptr = new string("123");
    return *ptr;
}
string temp = foo();  //new生成的这块内存无法释放 
//上面这句话可以如下处理,但会麻烦
string& tmp = foo();
string str = tmp;
delete &tmp;

左值、右值、左值引用、右值引用

左值:既能够出现在等号左边,也能出现在等号右边的变量。即左值是可寻址的变量,有持久性,能被修改的变量
右值:只能出现在等号右边的变量。即右值一般是不可寻址的常量,或在表达式求值过程中创建的无名临时对象,短暂性的。右值无法被修改
左值引用:引用一个对象;
右值引用:就是必须绑定到右值的引用,C++11中右值引用可以实现“移动语义”,通过 && 获得右值引用。
右值引用的作用之一:移动语义(std::move),用来减少临时资源的开辟

class Person {
public:
	//默认构造函数
	Person() {
		m_Age = 0;
		m_Height = nullptr;
	}

	//拷贝构造函数
	Person(const Person& p) {
		m_Age = p.m_Age;
		m_Height = new int(*p.m_Height);  //深拷贝,防止浅拷贝
		//cout << "拷贝构造函数申请的地址为:" << m_Height << endl;
		cout << "调用了拷贝构造函数" << endl;
	}

	//移动构造函数
	Person(Person&& p) noexcept {
		m_Age = p.m_Age;
		m_Height = p.m_Height;
		p.m_Height = nullptr;
		cout << "调用了移动构造函数" << endl;
	}

	//赋值运算符
	Person& operator=(Person& p) {
		m_Age = p.m_Age;
		m_Height = new int(*p.m_Height);
		//cout << "赋值运算符申请的地址为:" << m_Height << endl;
		return *this;
	}
	//移动赋值运算符
	Person& operator=(Person&& p) noexcept {
		//先自我检测,释放自身资源
		if (this != &p) {
			delete m_Height;
		}
		
		m_Age = p.m_Age;			//接管p的资源
		m_Height = p.m_Height;

		p.m_Height = nullptr;      //将p的资源置空
	}

	//有参构造函数
	Person(int age, int height) {
		m_Age = age;
		m_Height = new int(height);
		//cout << "有参构造函数申请的地址为:" << m_Height << endl;
		cout << "调用了有参构造函数" << endl;
	}

	//析构函数
	~Person() {
		if (m_Height != nullptr) {
			delete m_Height;
			m_Height = nullptr;
		}
	}

	int m_Age;
	int* m_Height;
};


int main(){
	//传统的左值引用
    int a = 10;
    int& b = a;  // 定义一个左值引用变量
    b = 20;      // 通过左值引用修改引用内存的值

    //下面一行代码无法通过编译,因为等号右边的数无法取地址
    int &var = 10;  

    //上面一行代码可以改成如下的常引用,理由上面已经说过
    const int& var = 10; 
    
    //但改成常引用就无法修改var的值了,因此需要使用右值引用来解决问题
    //下面这行代码就能编译通过
    int&& var = 10;

    //并且右值引用也能改变值
    var = 1; 

	vector<Person> vec;
	Person p1(20, 160);

	vec.push_back(p1);			//p1会在传入参数时调用赋值构造函数被拷贝一次,之后马上销毁
	vec.push_back(std::move(p1));	//p1会被转换成右值,于是会调用移动构造函数,不会调用拷贝构造,
									//提升效率	
 }

右值引用的作用之二:完美转发
完美转发,它指的是函数模板可以将自己的参数“完美”地转发给内部调用的其它函数。所谓完美,即不仅能准确地转发参数的值,还能保证被转发参数的左、右值属性不变。
看下面这段代码

template<typename T>
void f(T&& x){ 
    cout << ++x; 
}
f(2); // 3

函数f接受一个右值x,但是f(2)这条语句将2传入时,可以对2进行自增。这说明右值引用本身时左值,即x时左值。那么在传参时就会出现问题。看下面这段代码

template<typename T>
void g(T&& x) {
    cout << "右值" << endl;
}

template<typename T>
void g(T& x) {
    cout << "左值" << endl;
}

template<typename T>
void f(T&& x) {
    cout << "f右值引用" << endl;
    g(x);
}
template<typename T>
void f(T& x) {
    cout << "f左值引用" << endl;
    g(x);
}

结果如下:
在这里插入图片描述

一共有三个函数,f函数调用了g函数,g函数的作用是如果传入的参数是左值就输出左值,如果传入的参数是右值就输出右值。但是由于g函数的参数是通过f函数传入的(即x先通过外部的f函数传入,再由f函数传给g),经过第一次传参时x已经变为左值了(可以和第一个例子比对着看),所以g(x)永远只会输出左值(即永远只会和第二个函数模板匹配),这明显与我们想要的结果不匹配。那么我们可以这么写:

template<typename T>
void f(T&& x) {
    cout << "f右值引用" << endl;
    g(std::forward<T>(x));
}
template<typename T>
void f(T& x) {
    cout << "f左值引用" << endl;
    g(std::forward<T>(x));
}

此时,输出的结果如下:
在这里插入图片描述
符合我们的预期。std::forward函数保持了 x 的引用类型。
那么再结合移动语义想象一下这样的一个例子:你需要通过函数传入的参数进行一个赋值(或者拷贝操作),但是如果不保持参数的性质,虽然你为了减小开销,外面传入的是一个右值,但是一进入函数就会变成左值。举个例子:
回到之前的移动语义的例子中来,之前的代码不变,我们增加一个函数

void fun(Person&& p) {
	Person p2(p);
}
int main(int argc, char* argv[])
{
	Person p1(20, 160);
	fun(std::move(p1));
}

结果如下:
在这里插入图片描述
我们在主函数中构造了P1对象,fun()函数本义是利用移动构造函数来减小复制次数,但是可以看到尽管我们在main函数中使用了右值,但是fun函数里任然使用了拷贝构造函数。我们做如下修改:

Person p2(std::forward<Person>(p));

结果如下:
在这里插入图片描述
可以看到符合我们的预期

new delete与malloc free

相同点:都能从堆上申请、释放空间
区别:

  1. new与delete属于运算符,而malloc是函数
  2. malloc只负责开辟空间,new不仅仅有malloc的功能,可以进行数据的初始化
  3. malloc开辟内存失败返回nullptr指针;new抛出的是bad_alloc类型的异常
  4. free只会释放空间,而delete会先调用类的析构函数,在释放空间。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++总结——语法篇 的相关文章

随机推荐

  • 机器学习:Linear Discriminant Analysis(过程详解+实例代码MATLAB实现

    目录 LDA概念线性判别分析 xff08 LDA xff09 二分类LDA二分类过程举个例子 线性判别分析 多分类LDA多分类过程 Experiment 3 Linear Discriminant AnalysisLDA二分类讲解LDA二分
  • python编程:从入门到实践 笔记

    文章目录 第二章 变量和简单数据类型2 1 运行 hello world py 时发生的情况2 2 变量2 2 1 变量的命名和使用2 2 2 使用变量时避免命名错误 2 3 字符串2 3 1 使用方法修改字符串的大小写name title
  • 网络层——IP协议

    协议格式网段划分特殊的IP地址IP地址的数量限制私有IP地址和公网IP地址 协议格式 4位版本号 xff1a 指定IP协议的版本 xff0c 对于IPv4来说 xff0c 就是44位首部长度 xff1a 标识IP报头的长度 xff0c 最小
  • 若依 CAS 6.1 java.io.FileNotFoundException: \etc\cas\thekeystore (系统找不到指定的文件。)

    参考文章 实际上语文水平不过关 xff0c 把cas resources内部的文件全部拷贝到cas overlay template src main resources下
  • 解决通过vnc登陆linux server,在terminal最小化后找不到的问题

    在左边的菜单栏右击 xff0c add new panel xff0c 再右击新添加的add to panel xff0c 然后会让你选择往这个新panel上添加新的菜单 xff0c 把window list添加就好了
  • CSS第二章:2.颜色单位(RGB值、RGBA值)

    总览 一 RGB 1 CSS中能够使用颜色名来使用颜色 2 不使用颜色名 xff0c 我们使用RGB值来描述颜色 3 R red G green B blue 4 每一种颜色的范围在0 255 xff08 0 100 xff09 之间 5
  • 信息化与数字化的区别

    数字化与信息化的区别 今日看到一篇文章 信息化与数字化的核心差异 xff0c 让我重新思考了这两个即熟悉又陌生的名词 xff0c 通过这篇文章和在B站上搜相对应的视频 xff0c 使我大致懂了一些 1 信息化是什么 xff1f 信息化是数据
  • 开启快乐之旅—【C#Winform&认识篇】

    入门学习 简单了解熟悉界简单操作1 xff1a 修改窗体标题2 xff1a 修改窗体图标3 xff1a 窗体出现位置4 xff1a 最大化 xff0c 最小化操作5 xff1a 是否任务栏显示form窗体6 xff1a 窗口大小 简单了解
  • ubuntu磁盘清理

    打开ubuntu software 搜索Bleachbit 安装即可 系统清理工具BleachBit使用 ubuntu清理磁盘空间的多种方法 Ubuntu上释放磁盘空间的几种简单方法 Ubuntu 安装 BleachBit 也可 1 查看电
  • 一些网站资源分享

    链接地址 网站地址 1 博客就是这个博客 2 音乐是我网易云歌单 3 图床是我保存的图库 4 西瓜导航里面有各种网站 5 钟馗之眼是一个爬虫全网某些特征的网站 6 罗马盘是资源网站 7 AI引擎 xff0c 体验智能的感觉 8 优店 xff
  • 软件工程的完整生命周期

    生命周期大体上分为11个步骤 xff0c 如下图 xff1a 第一步 xff1a 概念 创意 需求 产生 xff0c 这个环节一般是项目发起人完成 xff0c 也有可能由产品经理或收到反馈后发起 xff0c 很多时候是会带有这个项目的期望或
  • Qt-设置背景色的几种方式

    最近设置背景色 xff0c 被背景色搞得晕头转向 xff0c 然后总结了一下用过的方法与踩过的坑 xff0c 希望大家有所帮助 QLabel label 61 new QLabel CSS样式 xff0c 直接设置样式 xff0c 该方法对
  • C++ 如何获取数组/容器的长度?

    文章目录 一 获取数组的长度1 sizeof a sizeof a 2 end a begin a 二 获取标准库容器的长度三 数组作为函数参数时 xff0c 在函数中无法获取其长度 一 获取数组的长度 1 sizeof a sizeof
  • 计算机网络期末复习题

    1 请简述在划分子网的情况下 xff0c 路由器转发IP数据报的基本过程 答 xff1a 在划分子网的情况下 xff0c 路由器转发分组的算法如下 xff1a 1 从收到的数据报首部提取目的IP地址D xff08 1分 xff09 2 先判
  • Jetson TX1 TX2 IO 引脚设置

    有朋友问我引脚问题 xff0c 我就把一段文档摘抄在这里了 希望对大家有用 配置40引脚扩展接头 主屏幕 兼容的硬件屏幕 40引脚扩展标题屏幕 命令行界面 设备树覆盖 创建简单的设备树覆盖 为40针接头连接器创建自定义设备树覆盖 启动Jet
  • Android手机怎样投屏到win10(无需联网)

    一 前言 适用场景 xff1a 无可用wifi 想获得大屏观影体验 x1f601 二 具体操作 PC端 在Cortana处搜索 投影设置 投影到此电脑 处的设置如下如图 xff08 也可根据自己习惯调整 xff09 安卓端 华为手机为例 打
  • Windows远程控制ubuntu16.04的vnc教程(附灰屏和无鼠标只显示叉号的解决方案)

    VNC可远程Linux的桌面 xff0c 不仅仅只可以远程终端窗口 xff0c 操作起来要比ssh好用 xff0c 效果如图 VNC介绍 xff1a VNC Virtual Network Console xff0c 即虚拟网络控制台 xf
  • 联邦学习安全与隐私保护

    一 FL隐私保护方法 1 1 加密方法 通过将明文编码为密文的方式 xff0c 只允许特定人员解码 xff0c 为数据隐私保护提供了有效手段 xff0c 但往往需要较大的计算开销 xff0c 较难应用于实际场景中 安全多方计算 SMC研究的
  • vue - vue项目对axios请求的封装

    axios介绍 axios是基于promise的网络请求库 xff0c 可以在nodejs和浏览器中运行 在服务端axios使用原生的nodejs的http模块 xff0c 在客户端浏览器中则而是用xmlhttprequests xff0c
  • C++总结——语法篇

    Static关键字1 静态全局变量2 静态局部变量3 静态函数4 静态数据成员5 静态成员函数 引用引用与指针的区别 左值 右值 左值引用 右值引用 new delete与malloc free Static关键字 1 静态全局变量 sta