专栏首页基础知识文章C++入门知识(二)

C++入门知识(二)

6.引用

1.什么是引用?

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
**类型& 引用变量名(对象名) = 引用实体;**
void TestRef()
{
	int a = 10;
	int& ra = a;//<====定义引用类型
	printf("%p\n", &a);
	printf("%p\n", &ra);
	cout << a << endl;
	cout << ra << endl;
	ra -= 1;
	cout << a << endl;
	cout << ra << endl;
}
int main()
{
	TestRef();
	system("pause");
	return 0;
}

执行结果:

2.引用特性:

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体
void TestRef2()
{
	int a = 10;
	//int& ra;   // 该条语句编译时会出错,定义时没有初始化
	int& ra = a;
	int& rra = a;
	printf("%p %p %p\n", &a, &ra, &rra);
}

3.常引用

常引用声明方式:const 类型标识符&引用名=目标变量名;

void TestConstRef()
{
const int a = 10;
//int& ra = a;   // 该语句编译时会出错,a为常量
const int& ra = a;
// int& b = 10; // 该语句编译时会出错,b为常量
const int& b = 10;
double d = 12.34;
//int& rd = d; // 该语句编译时会出错,类型不同
const int& rd = d; }

用这种方式声明的引用,不能通过引用对目标变量的值进行修改,从而使引用的目标成为const,达到了引用的安全性。

4.引用场景

1、传递可变参数 2.做返回值

void Swap(int& left, int& right) {
	int temp = left;
	left = right;
	right = temp;
}
int& TestRefReturn(int& a) {
	a += 10;
	return a;
}
int main()
{
	int a = 8, b = 9; Swap(a, b);
	cout << a << b << endl;
	TestRefReturn(a);
	cout << a << endl;
	return 0;
}

注意:如果函数返回时,离开函数作用域后,其栈上空间已经还给系统,因此不能用栈上的空间作为引用类型 返回。如果以引用类型返回,返回值的生命周期必须不受函数的限制(即比函数生命周期长)。

5.引用和指针的区别

★ 相同点: 都是地址的概念; 指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。 ★ 区别: 1. 指针是一个实体,而引用仅是个别名; 2. 引用使用时无需解引用(*),指针需要解引用; 3. 引用只能在定义时被初始化一次,之后不可变;指针可变; 4. 引用没有 const,指针有 const; 5. 引用不能为空,指针可以为空; 6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小; 7. 指针和引用的自增(++)运算意义不一样; 8.从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。

7内联函数

1什么是内联函数?

内联函数(有时称作在线函数或编译时期展开函数)是一种编程语言结构,用来建议编译器对一些特殊函数进行内联扩展(有时称作在线扩展);也就是说建议编译器将指定的函数体插入并取代每一处调用该函数的地方(上下文),从而节省了每次调用函数带来的额外时间开支。但在选择使用内联函数时,必须在程序占用空间和程序执行效率之间进行权衡,因为过多的比较复杂的函数进行内联扩展将带来很大的存储资源开支。另外还需要特别注意的是对递归函数的内联扩展可能引起部分编译器的无穷编译。

2内联函数的特性

  1. 内联函数是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数。
  2. 内联函数对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。
  3. 内联函数不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。
// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i) {
cout << i << endl; }
// main.cpp
#include "F.h"
比特科技
int main()
{ f(10);
return 0; }
// 链接错误:main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl f(int)" (?
f@@YAXH@Z),该符号在函数 _main 中被引用

8auto关键字

auto是一个C/C++语言存储类型,仅在语句块内部使用,初始化可为任何表达式,其特点是当执行流程进入该语句块的时候初始化可为任何表达式。

#include<iostream>
using namespace std;
int TestAuto()
{
	return 10;
}
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	auto d = TestAuto();
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;
	//auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
	system("pause");
	return 0;
}

运行结果:

【注意】使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。

auto如何使用?

1. auto与指针和引用结合起来使用 用auto声明指针类型时,用auto和auto*没有区别,但用auto声明引用类型时则必须加& 2. 在同一行定义多个变量 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

#include<iostream>
using namespace std;
void TestAuto()
{
	auto a = 1, b = 2;
	//auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'a';
	auto* e = &a;
	auto& f = a;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(e).name() << endl;
	cout << typeid(f).name() << endl;
	system("pause");
	return 0;
}

以下情况auto不能使用

  1. auto不能作为函数的参数 // 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导void TestAuto(auto a) {}
  2. auto不能直接用来声明数组
void TestAuto()
{
    int a[] = {1,2,3};
    auto b[] = {4,5,6};
}

9基于范围的for循环

用法:

for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。 语法形式:

for(declaration:expression)
{
	statement
}

其中: expression部分表示一个对象,用于表示一个序列。 declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素。 每次迭代,declaration部分的变量会被初始化为expression部分的下一个元素值。

基于范围的for循环特点

(1)和普通循环一样,也可以采用continue跳出循环的本次迭代。 (2)用break来终止整个循环

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	int x[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	for (int y : x) { 
		cout << y << " ";
	}

	for (auto y : x) {
		cout << y << " ";
	}
	cout << endl;

	for (auto &y : x) { 
		cout << y << " ";
	}
	cout << endl;

	for (const auto &y : x) { 
		cout << y << " ";
	}
	cout << endl;
	cout << "end of integer array test" << endl;
	cout << endl;

	vector<double> v;
	for (int i = 0; i < 10; ++i) {
		v.push_back(i + 0.14159);
	}

	for (const auto &j : v) {
		cout << j << " ";
	}
	cout << endl;
	cout << "end of vector test" << endl;
	system("pause");
}

10指针空值nullptr

nullptr:是C++空指针类型的关键字,nullptr是在C++11中引入的。 传统意义上来说,c++把NULL、0视为同一种东西,有些编译器将NULL定义为 ((void*)0),有些将其定义为0.

c++不允许直接将void隐式的转化为其他类型,但是如果NULL被定义为 ((void)0), 当编译char p = NULL;NULL只好被定义为0。 还有: void func(int); void func(char); 如果NULL被定义为0,func(NULL)会去调用void func(int),这是不合理的 所以引入nullptr,专门用来区分0、NULL。 nullptr的类型为nullptr_t,能够隐式的转换为任何指针。

nullptr 与 nullptr_t:

typedef decltype(nullptr) nullptr_t; 

注意:

  1. 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
  2. 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
  3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。

总结,刚接触C++,整理的知识不是很有条理,有问题的地饭否还望指出!!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C++入门知识(一)

    一、#命名空间 在C/C++中,变量、函数和类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符...

    海盗船长
  • 二叉树由浅至深(上)

    树有很多种表示方式,如:双亲表示法,孩子表示法、孩子兄弟表示法等等。这里简单了解其中最常用的孩子兄弟表示法。

    海盗船长
  • 排序算法——一篇文章搞懂常用的排序算法

    排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。 稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记...

    海盗船长
  • 学以致用C++设计模式 “建造者模式”

    今天,他突发奇想,为什么做汉堡就得是那个顺序呢?为什么不能先把肉烤好,然后凉一会?为什么不能先挤奶油然后把带着奶油的面包拿去烤?为什么要用生菜不用熟菜?

    看、未来
  • 简单对比下 Moshi 和 Kotlinx.serialization

    上一篇我们对比介绍了 Gson 和 Kotlinx.serialization,很多小伙伴在后台留言说,moshi 呢?

    bennyhuo
  • CTF | 基础题0x01

    非常简单的一道CTF,现在暑假了,利用点滴的时间刷点题,由于还是个小白,就先弄点容易的

    TeamsSix
  • 基于SSM框架的JavaWeb通用权限管理系统

    这是一个通用权限管理系统项目,基于SSM(Spring + Spring-MVC +Mybatis)框架开发,其SQL语句持久在Hibernate 中,对原生S...

    C you again 的博客
  • SSM框架搭建

    ssm框架的学习小结,主要是初期环境的搭建配置以及信息的交互处理过程,非技术人员请绕道~ SpringMVC其实就是Spring框架的一个模块,是一个基于MVC...

    企鹅号小编
  • flask_socket_io中报错RuntimeError: You need to use the eventlet server. See the Deployment section of t

    经测试发现,如果是用uwsgi启动,采用eventlet模式,process只能设置1个,且会造成线程堵塞,如果设置超过1个,也会报上面的错误

    用户1214487
  • 比较两个概率分布的方法——Kullback-Leibler散度

    在这篇文章中,我们将探讨一种比较两个概率分布的方法,称为Kullback-Leibler散度(通常简称为KL散度)。通常在概率和统计中,我们会用更简单的近似分布...

    磐创AI

扫码关注云+社区

领取腾讯云代金券