智能指针的那些事

C++不像Java,C#语言,它没有垃圾回收机制,但是它提供了强大而灵活的管理机制,使得开发人员自己避免内存泄露。可以通过new 获得内存或创建对象,一定使用delete来释放,这样就避免内存泄露。同时也可以将分配和使用用类封装,从而保证没有内存泄露。

#include <iostream>

using namespace std;

 

#include <stdio.h>

#include <string.h>

 

class simpleClass

{

private:

char *m_buf;

size_t m_size;

public:

simpleClass(size_t n = 1)

{

m_buf = new char[n];

m_size = n;

}

~simpleClass()

{

printf("%d is deleted at %xd \n", m_size, m_buf);

delete[] m_buf;

}

char * GetBuf()

{

return m_buf;

}

};

 

void foo()

{

simpleClass  a(10);

char *p = a.GetBuf();

 

strcpy(p, "Hello");

printf("%s\n", p);

}

 

int main()

{

foo();

printf("exit main()...\n");

return 0;

}

这个程序中,对char类型的内存分配封装在类simpleClass中,通过声明对象,并给所需内存的大小,调用GetBuf获取响应内存,这段内存在simpleClass的对象退出时会调用析构函数自动释放。但是还是存在不完美的地方,如果在foo()函数中增加一条赋值语句

void foo()

{

simpleClass  a(10);

simpleClass b = a;

char *p = a.GetBuf();
// 增加的语句

 

strcpy(p, "Hello");

printf("%s\n", p);

}

 

 

在实现simpleClass时,并没有实现拷贝构造函数,因此编译器会构造一个默认的拷贝构造函数,执行位拷贝(bit copy)操作,即对象a的内容一个个字节的拷贝到对象b中,因此对象a中的m_buf和拷贝后对象b中的m_buf指向的是同一个地址的内存。当a和b都销毁时,m_buf中的内容被销毁两次。解决方案:

(1)禁止拷贝构造,将拷贝构造函数声明为私有的。

(2)使用引用计数,对使用内存维护一个计数器。当有指针指向这块内存,计数器加1,当指向这块内存的指针销毁时,计数器减1,只有当计数器减为0时,表示没有指针指向这块内存,这块内存才可以被释放。

#include <iostream>

using namespace std;

 

#include <stdio.h>

#include <string.h>

 

class simpleClass

{

private:

char *m_buf;

size_t m_size;

int *m_count;

public:

simpleClass(size_t n = 1)

{

m_buf = new char[n];

m_size = n;

 

m_count = new int; // 为m_count分配空间 

*m_count = 1;

printf("m_count is:%d\n", *m_count);

}

simpleClass(const simpleClass& s)

{

m_size = s.m_size;

m_buf = s.m_buf;

m_count = s.m_count;

(*m_count)++;

printf("m_count is:%d\n", *m_count);

}

~simpleClass()

{

(*m_count)--;

printf("m_count is:%d\n", *m_count);

if (0 == *m_count)

{

printf("%d is deleted at %d\n", m_size, m_buf);

delete[] m_buf;

delete m_count;

}

}

char * GetBuf()

{

return m_buf;

}

};

 

void foo()

{

simpleClass a(10);

char *p = a.GetBuf();

 

strcpy(p, "Hello");

printf("%s\n", p);

 

simpleClass b = a;

printf("b=%s \n", b.GetBuf());

}

 

int main()

{

foo();

printf("exit main()...\n");

return 0;

}

 

分析:当a被构造时,m_count初始化为1,当执行b=a时,count增加为2。a 和 b指向同一块内存,存储的内容都是“hello”。当退出foo()时,b首先被销毁,m_count减1,但是m_count的值仍是大于0的,所以内存没有释放。当a销毁时,m_count的值减为0,才释放对应的内存。

如何在foo()函数里面添加两句代码

void foo()

{

simpleClass a(10);

char *p = a.GetBuf();

 

strcpy(p, "Hello");

printf("%s\n", p);

simpleClass b = a;

 

// 添加代码

simpleClass c(20);

c = a;

printf("b= %s,c= %s\n", b.GetBuf(), c.GetBuf());

}

 

 

在通过拷贝构造创建b之后,声明了一个c对象,申请的内存大小是20字节。然后a赋值给c,此时c指向了a的内存。而c原来指向的内存则无指针指向,因此被释放。但是程序没有处理,造成内存泄露。解决方案,使用运算符重载。

simpleClass& operator=(const simpleClass& s)

{

// 判断当前对象的 m_buf 是否和 s.m_buf 是否指向相同的地址 

if (m_buf == s.m_buf)

return *this;

 

// 如果不是,当前对象引用计数减1 

(*m_count)--;

 

// 如果引用计数为0,释放该对象指向的空间 

if (*m_count == 0)

{

printf("%d is deleted at %xd\n", m_size, m_buf);

delete[] m_buf;

delete m_count;

}

// 当前对象指针 指向新的地址空间 

m_buf = s.m_buf;

m_size = s.m_size;

m_count = s.m_count;

(*m_count)++;// 这也是为什么引用计数用指针的原因??

}

 

总算是没有错误了,但是这个智能指针还不完整,并没有实现真正指针那样的操作,如运算符*,->都没有重载。更多详细内容参考《C++ Primer》 400页。

推荐书籍:

《C++ Primer》(第5版)

*****************************分割线************************************

如果喜欢,帮忙点赞,推荐给好友

添加方式,在微信公众号

搜索:c语言每日一问

或者:Love_gcc123

或者长按二维码图片,就可以添加

时间: 2024-07-31 17:33:22

智能指针的那些事的相关文章

智能指针简介

智能指针用于解决常规指针所带来的内存泄露.重复释放.野指针等内存问题.智能指针基于这样的事实得以发挥作用:定义在栈中的智能指针,当超出其作用域时,会自动调用它的析构函数,从而可以释放其关联的内存资源. 之前C++标准库中定义的智能指针std::auto_ptr<T>,因其设计存在缺陷,所以已不再推荐使用.C++11引入了新的智能指针:unique_ptr.shared_ptr和weak_ptr. 一:unique_ptr unique_ptr类似于auto_ptr.两个unique_ptr实例

第十一章:使用智能指针管理对象资源

前言 在前面的文章中,细致地分析了构造函数,拷贝构造函数,赋值运算符,析构函数这几个类中最重要函数的用法. 如果严格地遵循这些做法,可以消除绝大部分资源管理的问题. 然而,要想更灵活的使用对象中的资源,仅仅这些还不够.譬如,若你想自己控制对象资源的生命周期(不要在作用域结束的时候自动被析构掉),那就应当好好考虑下智能指针了. 有人说,智能指针是属于设计模式范畴的产物,这么说有点偏激,但也确实有点道理. 问题分析 我们假定有一个投资类Investment: 1 class Investment 2

More Effective C++ 条款28 Smart Pointers(智能指针)

1. 智能指针(如标准库的auto_ptr,shared_ptr,weak_ptr,boost的scoped_ptr等)主要用于动态内存的管理,同时提供给用户与内置指针一样的使用方法,本条款主要涉及智能指针在构造与析构,复制和赋值,解引等方面的注意点,而非智能指针的实现细节. 2. 智能指针的构造,赋值,析构 智能指针的copy constructor,assignment operator,destructor对应于不同的观念而有不同的实现,主要有三种选择: 1).不允许对象的共享,在调用co

Effective c++--智能指针 &amp; 函数模板

Shared_ptr允许当智能指针被建立起来是指定一个资源释放函数绑定于智能指针身上,也就是说在shared_ptr被初始化的同时也可以指定其管理的资源的释放函数.Shared_ptr提供的某个构造函数接受两个实参:一个是被管理的指针,另一个是引用次数变为0时将被调用的"删除器".Shared_ptr构造函数坚持其第一个参数必须是个指针.它支持定制型删除器. 缺省情况下C++以by value方式传递对象至函数,这种情况下函数参数都是以实际参数的副本为初值,而调用端获得的也是函数返回值

Binder学习笔记(十一)—— 智能指针

轻量级指针 Binder的学习历程爬到驱动的半山腰明显感觉越来越陡峭,停下业务层的学习,补补基础层知识吧,这首当其冲的就是智能指针了,智能指针的影子在Android源码中随处可见.打开frameworkds/rs/cpp/util,RefBase.h和StrongPointer.h两个文件,代码多读几遍都能读懂,可是串起来总感觉摸不到骨架,把不住主线.闭上眼零零星星的点串不成一条线.究其原因应该是此处使用了模式,最好先剔除掉业务层的皮肉,把模式的骨架摸个门清,再回来看代码就会势如破竹了. 不是多

智能指针学习笔记

1. 介绍 本文介绍智能指针的使用.智能指针是c++ 中管理资源的一种方式,用智能指针管理资源,不必担心资源泄露,将c++ 程序员 从指针和内存管理中解脱出来,再者,这也是c++发展的趋势(这话不是我说的,见<Effective c++>和<c++实践编程>),应该认真学习一下. 智能指针中,最有名的应该数auto_ptr,该智能指针已经被纳入标准库,只需要包含<memory>头文件即可以使用,另外,TR1文档定义的shared_ptr和weak_ptr也已经实现(我用

boost智能指针之shared_ptr和weak_ptr

std::auto_ptr很多的时候并不能满足我们的要求,比如auto_ptr不能用作STL容器的元素.boost的smart_ptr中提供了4种智能指针和2种智能指针数组来作为std::auto_ptr的补充. shared_ptr<boost/shared_ptr.hpp>:使用shared_ptr进行对象的生存期自动管理,使得分享资源所有权变得有效且安全. weak_ptr<boost/weak_ptr.hpp>:weak_ptr 是 shared_ptr 的观察员.它不会干

话说智能指针发展之路

从RAII说起 教科书里关于"动态创建内存"经常会提醒你,new一定要搭配delete来使用,并且delete掉一个指针之后,最好马上将其赋值为NULL(避免使用悬垂指针). 这么麻烦,于是乎,这个世界变成11派人: 一派人勤勤恳恳按照教科书的说法做,时刻小心翼翼,苦逼连连: 一派人忘记教科书的教导,随便乱来,搞得代码处处bug,后期维护骂声连连: 最后一派人想了更轻松的办法来管理动态申请的内存,然后悠闲唱着小曲喝着茶- (注:应该没人看不懂11是怎么来的--就是十进制的3的二进制形式

智能指针auto_ptr

前奏: Garbage Collection 技术一直颇受注目,并且在 Java 中已经发展成熟,成为内存管理的一大利器,但它在 C++ 语言中的发展却不顺利,C++ 为了追求运行速度,20 年来态度坚决地将其排除在标准之外. 为了稍许平复因为没有 Garbage Collection 而引发的 C++ 程序员的怨气,C++对 Smart Pointer 技术采取了不同的态度. 首先,了解一下智能指针, 该方法使用一个指针类来代表对资源的管理逻辑,并将指向资源的句柄(指针或引用)通过构造函数传递