【原文】指针的引用与正常指针 释放内存的另一例比较
一、先看一段代码:
#include
<iostream>
using
namespace
std;
void
freePtr1(int
* p1)
{
delete p1;
p1 = NULL;
}
void
freePtr2(int
*& p2)
{
delete p2;
p2 = NULL;
}
void
main()
{
int
*p1 = new
int
;
*p1 = 1;
freePtr1(p1);
int
*p2 = new
int
;
*p2 = 2;
freePtr2(p2);
system("pause");
}
思考:在
freePtr1
和
freePtr2
的比较中,你能发现它们的不同点吗?
二、对代码进行解释:
#include
<iostream>
using
namespace
std;
void
freePtr1(int
* p1)
{
//
未释放内存前
->
p1 Address : 0012FDDC
p1 value : 003429B8
,在这里,
p1
它也是一个变量,既然是一个变量,那么它将会以值的传递,把外部变量
p1
传到栈内,在栈内产生一个地址:
0012FDDC
,当然,它的值不会变仍然是指向堆地址:
003429B8
。
delete p1; //
系统回收
p1
值的地址
003429B8
处的内存。
p1 = NULL;//
对
p1
赋以
NULL
值即:
00000000
,注意:
p1
本身的地址并没有变,变的是
p1
的值。
//
释放内存后
->
p1 Address : 0012FDDC
p1 value : 00000000
,出栈后,
p1
由于是一个临时对象,出栈后它会自动被视为无效。
}
void
freePtr2(int
*& p2)
{
//
未释放内存前
->
p2 Address : 0012FEC8
p2 value : 003429B8
,
p2
是一个指针的引用,即引用指向指针,记住引用的特点:对引用的对象直接操作。所以它的地址和值与栈外的
main()
函数中,
p2
的值是同一个。
delete p2; //
对
p2
所引用的指针进行释放内存,即:系统回收
main()
函数中
p2
的值
003429B8
地址处的内存。
p2 = NULL;//
对
main()
函数中
p2
的指针赋以
NULL
值。
//
释放内存后
->
p2 Address : 0012FEC8
p2 value : 00000000
,由于操作的对象都是
main()
函数中的
p2,
所以它将应用到原变量中。
}
void
main()
{
int
*p1 = new
int
;
//
释放内存前
->
p1 Address : 0012FED4
p1 value : 003429B8
freePtr1(p1);
//
释放内存后
->
p1 Address : 0012FED4
p1 value : 003429B8
int
*p2 = new
int
;
//
释放内存前
->
p2 Address : 0012FEC8
p2 value : 003429B8
freePtr2(p2);
//
释放内存后
->
p2 Address : 0012FEC8
p2 value : 00000000
system("pause");
}
这是偶三个月后,又一次拿起了
C++
开始初学,上面这个例子它可以让初学者(当然也包括我在内),可以对引用和指针又有一个新的或是另外一面的了解,人家都说了,好记性不如偶的烂键盘(虽然是一个比较简单的问题)……
^_^
,如果有什么不同看法,或是个人见解,请留言,谢谢。
--------------------------------------------------------------------------------------------------------------------------------
【原文】指针与引用的区别
指针与引用看上去完全不同(指针用操作符“*”和“->”,引用使用操作符“. ”),但是它们似乎有相同的功能。指针与引用都是让你间接引用其他对象。你如何决定在什么时候使用指针,在什么时候使用引用呢?
首先,要认识到在任何情况下都不能使用指向空值的引用。一个引用必须总是指向某些对象。因此如果你使用一个变量并让它指向一个对象,但
是该变量在某些时候也可能不指向任何对象,这时你应该把变量声明为指针,因为这样你可以赋空值给该变量。相反,如果变量肯定指向一个对象,例如你的设计不
允许变量为空,这时你就可以把变量声明为引用。
“但是,请等一下”,你怀疑地问,“这样的代码会产生什么样的后果?”
char *pc = 0; // 设置指针为空值
char& rc = *pc; // 让引用指向空值
这是非常有害的,毫无疑问。结果将是不确定的(编译器能产生一些输出,导致任何事情都有可能发生)。应该躲开写出这样代码的人,除非他
们同意改正错误。如果你担心这样的代码会出现在你的软件里,那么你最好完全避免使用引用,要不然就去让更优秀的程序员去做。我们以后将忽略一个引用指向空
值的可能性。
因为引用肯定会指向一个对象,在C++里,引用应被初始化。
string& rs; // 错误,引用必须被初始化
string s("xyzzy");
string& rs = s; // 正确,rs指向s
指针没有这样的限制。
string *ps; // 未初始化的指针
// 合法但危险
不存在指向空值的引用这个事实意味着使用引用的代码效率比使用指针的要高。因为在使用引用之前不需要测试它的合法性。
void printDouble(const double& rd)
{
cout << rd; // 不需要测试rd,它
} // 肯定指向一个double值
相反,指针则应该总是被测试,防止其为空:
void printDouble(const double *pd)
{
if (pd) { // 检查是否为NULL
cout << *pd;
}
}
指针与引用的另一个重要的不同是指针可以被重新赋值以指向另一个不同的对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变。
string s1("Nancy");
string s2("Clancy");
string& rs = s1; // rs 引用 s1
string *ps = &s1; // ps 指向 s1
rs = s2; // rs 仍旧引用s1,
// 但是 s1的值现在是
// "Clancy"
ps = &s2; // ps 现在指向 s2;
// s1 没有改变
总的来说,在以下情况下你应该使用指针,一是你考虑到存在不指向任何对象的可能(在这种情况下,你能够设置指针为空),二是你需要能够
在不同的时刻指向不同的对象(在这种情况下,你能改变指针的指向)。如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,那么你应该使用引用。
还有一种情况,就是当你重载某个操作符时,你应该使用引用。最普通的例子是操作符[]。这个操作符典型的用法是返回一个目标对象,其能被赋值。
vector<int> v(10); // 建立整形向量(vector),大小为10;
// 向量是一个在标准C库中的一个模板(见条款M35)
v[5] = 10; // 这个被赋值的目标对象就是操作符[]返回的值
如果操作符[]返回一个指针,那么后一个语句就得这样写:
*v[5] = 10;
但是这样会使得v看上去象是一个向量指针。因此你会选择让操作符返回一个引用。(这有一个有趣的例外,参见条款M30)
当你知道你必须指向一个对象并且不想改变其指向时,或者在重载操作符并为防止不必要的语义误解时,你不应该使用指针。而在除此之外的其他情况下,则应使用指针。
摘自 林锐《高质量C++编程》
分享到:
相关推荐
在C语言中,0可以在某些上下文中自动转换为空指针,如初始化、赋值和比较。但在函数调用中,特别是变参函数调用中,直接使用0可能不会被视为指针,因此需要显式类型转换,如: ```c execl("/bin/sh", "sh", "-c", ...
- 引用和解引用操作,例如使用&获取变量地址,使用*进行指针解引用。 - 多重赋值和复合赋值操作,如a += b等同于a = a + b。 - 运算符的优先级和结合性,如逻辑运算符&&和||的优先级低于比较运算符。 - 字符和...
6. **内存管理**:C++提供了动态内存分配和释放,理解指针和引用,以及何时何地使用new和delete,是防止内存泄漏的关键。 7. **C++11及后续标准**:第二版可能会涵盖C++11、C++14和C++17引入的新特性,如右值引用、...
链表是一种由节点组成的线性结构,每个节点包含数据域和指针域(在Java中用引用来代替)。链表可以是单向的、双向的或循环的。文章以单向链表为例,介绍了如何使用Java代码创建链表节点类、如何进行链表的建立、遍历...
8.8. 指针和引用表达式 8.9. 布尔表达式 8.10. 函数返回值 8.11. 变量及数组初始化 8.12. 预处理指令 8.13. 类格式 8.14. 初始化列表 8.15. 名字空间格式化 8.16. 水平留白 8.17. 垂直留白 译者 (YuleFox)...
VC++学习笔记摘录是孙鑫老师讲授的VC++课堂笔记,涵盖了VC++的基础知识和高级技术。本笔记摘录共分为两部分,第一部分介绍了VC++的基础知识,如MFC生成的C++源文件、StdAfx.h文件、afxwin.h文件等;第二部分介绍了...
根据提供的文件内容,我们可以提取出...从文件中摘录的代码片段展示了各种语法结构的使用场景和代码的书写规范。虽然有些代码因为OCR扫描的不准确而出现了错误或者不完整,但整体上仍能体现出C语言考试中常用的考察点。
此外,IUnknown接口是所有COM接口的基础,它包含了三个基本方法:QueryInterface、AddRef和Release,分别用于获取其他接口、增加引用计数和释放对象。 进程间通信(IPC)是COM的另一大特性,使得COM组件可以在同一...
6. **内存管理**:理解指针、动态内存分配(new和delete)、智能指针(shared_ptr、unique_ptr、weak_ptr)是C++程序员必备的技能,面试中会关注对内存泄漏和野指针的理解与防范。 7. **命名空间**:C++11引入的...
1. C++语言的基本原则:包括如何在代码中正确使用指针、引用、作用域、内存管理等基础概念。 2. 类和对象的设计与实现:详细讨论如何设计类来避免常见陷阱,优化数据封装以及接口的合理使用。 3. 避免C++中的常见...
2. 编码实践:涉及变量声明、函数和类的设计、作用域管理、参数传递的最佳方式(值传递、引用传递、指针传递)以及错误处理机制。 3. 性能优化:可能包括内存管理技巧、循环优化、算法选择等针对提高代码性能的规则...
在提供的文件信息中,标题《Sams Teach Yourself C++ in One Hour a Day.pdf》和描述指明了该文档与C++编程语言相关,具体内容的摘录中则提到与编程相关的博客、联系方式以及版权声明。基于这些信息,以下是关于C++...
1. **COM基础**:首先,书中会介绍COM的基本概念,包括对象、接口、iid(接口ID)、clsid(类ID)以及如何通过IUnknown接口进行引用计数和接口查询。 2. **COM对象生命周期**:COM对象的创建和销毁过程,包括...
`)、指针和内存分配(`char *c = NULL; c = (char*)malloc(100 * sizeof(char));`)。对C语言的理解和编程能力是计算机网络应用开发中的基础,尤其是在嵌入式开发和系统编程中。 3. Shell命令:文档中提到了`...
- 指针与内存操作,例如指针赋值与解引用(如*P0;)。 2. 数据类型与结构 - 通过注释(iii),可以看到对不同数据类型的强制类型转换:将float转换为int和double,将int转换为float和double。 - IEEE754浮点数表示...
- **引用**: 详细阐述了引用的用法以及与指针的区别,帮助读者理解何时使用引用更为合适。 - **运算符重载**: 探讨了如何正确地重载运算符,以及在哪些场景下使用运算符重载可以提高代码的可读性和维护性。 - **继承...
由于没有具体的章节或内容摘录,以下知识点是基于常见的C++编程书籍内容所构建: 1. C++语言概述:介绍C++的历史、特点以及它与其他编程语言(如C语言)的关系。 2. 基本语法:包括变量声明、数据类型、运算符、...
- **命令函数指针(Command Function Pointers)**: 讲解了如何设置和使用Vulkan中的函数指针。 - **实例(Instances)**: 解释了Vulkan实例的创建和使用,以及其在应用程序中的角色。 #### 4. 设备与队列(Devices and ...
- **解释**: 对于指针和迭代器的操作,需要特别注意避免悬空指针或无效迭代器等问题,以防止程序崩溃或未定义行为。 ##### No.30: 异常安全原则 - **重点**: 实现异常安全的原则。 - **解释**: 异常安全是指程序在...