C++学习笔记之指针引用
指针
指针定义
指针定义的基本形式:指针本身就是一个变量,其符合变量定义的基本形式,它存储的是值的地址。对类型T,T是“到T的指针”类型,一个类型为T的变量能保存一个类型T的对象的地址。
如:
int a=112;
float c=3.14;
int* d=&a;
float* e=&c;
cout << d << endl; cout << e << endl;
cout << (*d) << endl; cout << (*e) << endl;
通过一个指针访问它所指向地址的过程称为间接访问(indirection)或者引用指针(dereferencing the point);这个用于执行间接访问的操作符是单目操作符*:
cout<<(*d)<<endl;
cout<<(*e)<< endl;
左值与右值
概念:
- 编译器为其单独分配了一块存储空间,可以取其地址的,左值可以放在赋值运算符左边;
- 右值指的是数据本身,不能取到其自身地址,右值只能赋值运算右边;
具体分析:
- 左值最常见的情况如函数和数据成员的名字;
- 右值是没有标识符、不可以取地址的表达式,一般也称之为“临时对象"。
比如:a=b+c; &a是允许的操作,而&(b+c)不能通过编译,因此a是一个左值,而(b+c)是一个右值;
指针数组与数组指针
指针的数组(array of pointers)与数组的指针(a pointer to an array):
- 指针的数组 T t []*:运算符和T结合,定义数组的元素为T指针类型。
- 数组的指针 T (*t) []:*运算符和t结合,定义变量t为指针变量。
int* a[4]; // array of pointers 指针的数组
int (*b) [4]; // a pointer to an array 数组的指针
// 输出
cout << *(a[0]) << endl;
cout << (*b)[0] << endl;
const与指针
const pointer与pointer to const例子:
char strHelloworld[]={"helloworld"};
char const * pStr1="helloworld"; //和const char* pStr1等效
char * const pStr2="helloworld";
char const * const pStr3="helloworld";
pStr1=strHelloworld;
//pStr2=strHelloworld; //pStr2不可改
//pStr3=strHelloworld; //pStr3不可改
关于const修饰的部分:
- 看左侧最近的部分;
- 如果左侧没有,则看右侧;
指针的指针
指向指针的指针例子:
int a=123;
int* b=&a;
int** c=&b;
*操作符具有从右向左的结合性,上面取值规则如下:
- **这个表达式相当于*(*c),必须从里向外逐层求值;
- *c得到的是c指向的位置,即b;
- **c相当于*b,得到变量a的值;
NULL指针
NULL指针:一个特殊的指针变量,表示不指同任何东西。
int* a=NULL;
NULL指针给了一种方法,来表示特定的指钍目前未指向任何东西,使用的注意事项:
- 对于一个指针,如果已经知道将被初始化为什么地址,那么请赋给它这个地址值,否则适把它设置为NULL。
- 在对一个指针进行间接引用前,请先判断这个指针的值为否为NULL。
- 没有初始化的,不用的或者超出范围的指针请把值置为NULL。
内存泄漏(Memory Leak)问题
内存泄漏问题:指程序中己动态分配的维内存由于某种原因程序未释放或无法释,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
内存泄漏发生原因和排查方式:
- 内存泄漏主要发生在堆内存分配方式中,即“配置了内存后,所有指向该内存的指针都遗失了”。
- 因为内存泄漏属于程序运行中的问题,无法通过编译识别,所以只能在程序运行过程中来判别和诊断。
while (true)
{
int* wp1 = new int(10); //内存泄漏
}
智能指针
C++中推出了四种常用的智能指针:
- unique ptr:专属所有权,管理的内存只能被一个对象持有,不支持复制和赋值,可以使用std:move()进行控制所有权的转移。
auto w = std::make_unique<int>(10);
cout << *(w.get()) << endl; // 10
//auto w2 = w; // 编译错误如果想要把 w 复制给 w2, 是不可以的。
auto w2 = std::move(w); // w2 获得内存所有权,w 此时等于 nullptr
cout << ((w.get() != nullptr) ? (*w.get()) : -1) << endl; // -1
cout << ((w2.get() != nullptr) ? (*w2.get()) : -1) << endl; // 10
- shared ptre:通过一个引用计数共享一个对象,当引用计数为0时,该对象没有被使用,可以进行析构。
auto wA = shared_ptr<int>(new int(20));
{
auto wA2 = wA;
cout << ((wA2.get() != nullptr) ? (*wA2.get()) : -1) << endl; // 20
cout << ((wA.get() != nullptr) ? (*wA.get()) : -1) << endl; // 20
cout << wA2.use_count() << endl; // 2
cout << wA.use_count() << endl; // 2
}
//cout << wA2.use_count() << endl;
cout << wA.use_count() << endl; // 1
cout << ((wA.get() != nullptr) ? (*wA.get()) : -1) << endl; // 20
// move 语法,意味着放弃了对内存的所有权和管理
auto wAA = std::make_shared<int>(30);
auto wAA2 = std::move(wAA); // 此时 wAA 等于 nullptr,wAA2.use_count() 等于 1
cout << ((wAA.get() != nullptr) ? (*wAA.get()) : -1) << endl; // -1
ccout << wAA.use_count() << endl; // 0
cout << wAA2.use_count() << endl; // 1
- weak ptr:被设计为与shared_ptr共同工作(避免循环引用shared_ptr导致的内存泄露),用一种观察者模式工作,只对shared ptr进行引用而不改变其引用计数,当被观察的 shared ptr失效后相应的weak ptr也相应失效。
struct AW
{
shared_ptr<BW> pb;
~AW() { cout << "~AW()" << endl; }
};
struct BW
{
weak_ptr<AW> pa; //此处改为shared_ptr则会发生内存泄漏(循环引用)
~BW() { cout << "~BW()" << endl; }
};
void Test()
{
cout << "Test shared_ptr and shared_ptr: " << endl;
shared_ptr<A> tA(new A());
shared_ptr<B> tB(new B());
cout << tA.use_count() << endl; // 1
cout << tB.use_count() << endl; // 1
tA->pb = tB;
tB->pa = tA;
cout << tA.use_count() << endl; // 2
cout << tB.use_count() << endl; // 2
}
void Test()
{
cout << "Test weak_ptr and shared_ptr: " << endl;
shared_ptr<AW> tA(new AW());
shared_ptr<BW> tB(new BW());
cout << tA.use_count() << endl; // 1
cout << tB.use_count() << endl; // 1
tA->pb = tB;
tB->pa = tA;
cout << tA.use_count() << endl; // 1
cout << tB.use_count() << endl; // 2
}
int main()
{
Test();
return 0;
}
- auto_ptr:在拷贝/赋值过程中,会直接剥夺指针对原对象对内存的控制权(C++11中已经废弃的,在C++17中被正式删除)。
引用
C++的引用是一种特殊的指针,不允许修改的指针,参考 C++:引用的简单理解 。
注:C++中类的引用也需要显式定义,C#中除了值类型则全是引用类型,注意区别。
使用指针有空指针、野指针、不知不觉改变了指针的值却继续使用的问题,使用引用则:
- 不存在空引用;
- 必须初始化;
- 一个引用永远指向它初始化的那个对象;
int x = 1, x2 = 3;
int& rx = x;
rx = 2;
cout << x << endl; //2
cout << rx << endl; //2
rx = x2;
cout << x << endl; //3
cout << rx << endl; //3
有了指针为什么还需要引用?
Bjarne Stroustrup的解释:为了支持函数运算符重载;
有了引用为什么还需要指针?
Bjarne Stroustrup的解释:为了兼容C语言;
关于函数传递参数类型的说明:
- 对内置基础类型(如int,double等)而言,在函数中传递时pass by value更高效;
- 对OO面向对象中自定义类型而言,在函数中传递时pass by reference to const更高效;
C++学习笔记之指针引用的更多相关文章
- C++学习笔记1_ 指针.引用
1.引用的本质struct typeA{ int &a;}struct typeB{ int *a;}int main(void){ cout<<sizeof(struct typ ...
- Dubbo -- 系统学习 笔记 -- 示例 -- 泛化引用
Dubbo -- 系统学习 笔记 -- 目录 示例 想完整的运行起来,请参见:快速启动,这里只列出各种场景的配置方式 泛化引用 泛接口调用方式主要用于客户端没有API接口及模型类元的情况,参数及返回值 ...
- 吴裕雄--天生自然C++语言学习笔记:C++ 引用
引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字.一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量 C++ 引用 vs 指针 引用很容易与指针混淆,它们之间有三个主要的 ...
- 《C语言学习笔记》指针数组及其应用
C语言中,最灵活但又容易出错的莫过于指针了.而指针数组,是在C中很常见的一个应用.指针数组的意思是说,这个数组存储的所有对象都为指针.除了存储对象为指针,即一个地址外,其它操作和普通数组完全一样. # ...
- c++学习笔记——智能指针
智能指针是为了便于管理动态内存,能够自动管理释放所指向的对象. 智能指针共有三种:1.shared_ptr允许多个指针指向同一个对象:2.unique_ptr独占所指向的对象:3.weak_ptr是一 ...
- C++学习笔记29,引用变量(1)
引用变量在创建的时候就必须初始化.无法创建一个未被初始化的引用. #include <iostream> using namespace std; int main() { int x=1 ...
- go学习笔记-语言指针
语言指针 定义及使用 变量是一种使用方便的占位符,用于引用计算机内存地址.取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址. 一个指针变量指向了一个值的内存地址.类似于变量和常量, ...
- IOS开发学习笔记006 - 指针
C语言 指针 简单使用如下: int *p;//定义 int a = 10; p = &a;//赋值 int * b = &a;//定义并赋值 定义格式:类型 *指针变量名: 注意事项 ...
- 【C语言学习笔记】指针
用来存放一个变量地址的变量就叫指针变量.指针变量也是有类型约束的,一般什么类型的指针指向什么类型的变量. 指针之所以叫变量,是因为它里面所存放的变量的地址也是不断变化的,指针是可以移动的. 定义格式: ...
- Struts2学习笔记-jsp中引用struts2框架
如果在jsp中需要引用struts2 框架,需在前面加上以下内容 <%@taglib prefix="s" uri="/struts-tags" %> ...
随机推荐
- Asp .Net Core 系列:Asp .Net Core 配置 System.Text.Json
目录 简介 Asp .Net Core 如何配置 System.Text.Json 所有配置 全局配置 对比 Newtonsoft.Json 无实体类型下操作 Json 自定义转换器 处理 Dynam ...
- auto_ptr|unique_ptr|shared_ptr|weak_ptr|你都搞明白了吗?
前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助. 高质量干货博客汇总https://blog. ...
- 在k8s中,有哪些存储?
在 Kubernetes(简称 K8s)中,有多种内置和外部的存储解决方案,它们可以满足不同场景下的持久化存储需求.以下是一些常见的存储类型: PersistentVolume (PV): Persi ...
- mybatis批量插入支持默认值和自定义id生成策略的免写sql插件
最近做项目时用了免写sql的插件但是发现批量操作不满足现有需求.所以,在原有基础之上扩展了批量的操作支持[支持插入默认值和自定义id生成策略].使用方法如下: 一:在pom文件中引入jar配置 < ...
- .NET Core开发实战(第26课:工程结构概览:定义应用分层及依赖关系)--学习笔记
26 | 工程结构概览:定义应用分层及依赖关系 从这一节开始进入微服务实战部分 这一节主要讲解工程的结构和应用的分层 在应用的分层这里定义了四个层次: 1.领域模型层 2.基础设施层 3.应用层 4. ...
- .NET Core开发实战(第13课:配置绑定:使用强类型对象承载配置数据)--学习笔记
13 | 配置绑定:使用强类型对象承载配置数据 要点: 1.支持将配置值绑定到已有对象 2.支持将配置值绑定到私有属性上 继续使用上一节代码 首先定义一个类作为接收配置的实例 class Config ...
- CF739A Alyona and mex 题解
题目传送门 前置知识 贪心 | 构造 解法 从贪心的角度分析,最小的 \(\operatorname{mex}\) 仅会与长度最小的区间有关:另外为使得 \(\operatorname{mex}\) ...
- Typora 快捷方式给字体设置颜色
1.下载并安装 AutoHotkey (具体步骤可自行百度) 访问 AutoHotkey 主页: https://autohotkey.com/ 点击下载: https://autohotkey.co ...
- 老王电子的拆机 ESP32-SOLO-1 填坑报告
ESP32-SOLO-1 拆装 都是带板的, 长这个样子 需要用热风枪从背面吹, 因为中间有焊点, esp32朝下, 用280度大概2到3分钟, 四周需要均匀着风, 用镊子试探天线部分是否松动, 将外 ...
- Java Socket编程系列(一)开发一次性会话的Server和Client
关于什么是Socket: A socket is one end-point of a two-way communication link between two programs running ...