C++之智能指针20170920
/******************************************************************************************************************/
一、C++智能指针_自己实现智能指针
1.使用局部变量结合new的方式,防止new导致的内存泄漏
class sp
{
private:
Person *p;
public:
sp() : p(0) {}//表明sp的构造函数 继承person的无参构造函数
sp(Person *other)
{
cout<<"sp(const Person *other)"<<endl;
p = other;
}
~sp()
{
cout<<"~sp()"<<endl;
if (p)
delete p;
}
Person *operator->()//重载->
{
return p;
}
};
void test_func(void)
{
sp s = new Person();//分配的堆空间只是当作参数传递进去(传递给构造函数)了
s->printInfo();
}
分配的堆空间只是当作参数传递进去(传递给构造函数)了,s执行完后调用s的析构函数(局部变量,栈里),析构函数中又释放了内存就没有泄漏了
以后代码中需要person指针就可以使用sp代替,不用担心内存泄漏
/* 相当于:
* 1. Person *p = new Person();
* 2. sp tmp(p); ==> sp(Person *other)
* 3.
* 3.1 sp other(tmp); ==> sp(sp &other2)
问题在于: sp &other2 = tmp; // 错误语法,因为引用无法等于临时变量
const sp& other2 = tmp; //ok
* 或
* 3.2 sp other(tmp ==> Person*); ==>sp(Person *other)
*/
sp other = new Person();//编译器优化,直接把2,3部合并为一步
for (i = 0; i < 2; i++)
test_func(other);//这里执行两次会崩溃,原因在于第一次执行完后,把p指向的对象释放了,第二次再使用该指针自然会奔溃。
2.解决办法,引入引用计数,也就是再增加一个整数成员,当发现有人引用对象时 该值加1,如果使用完 减一,如果等于0再释放(解决过早释放问题)
class Person {
private:
int count;
public:
void incStrong(){ count++; }
void decStrong(){ count--; }
int getStrongCount(){ return count;}
Person() : count(0)//表示构造时count值默认值是0
{
cout <<"Pserson()"<<endl;
}
~Person()
{
cout << "~Person()"<<endl;
}
void printInfo(void)
{
cout<<"just a test function"<<endl;
}
};
class sp
{
private:
Person *p;
public:
sp() : p(0) {}
sp(Person *other)
{
cout<<"sp(Person *other)"<<endl;
p = other;
p->incStrong();
}
sp(const sp &other)
{
cout<<"sp(const sp &other)"<<endl;
p = other.p;
p->incStrong();
}
~sp()
{
cout<<"~sp()"<<endl;
if (p)
{
p->decStrong();
if (p->getStrongCount() == 0)
{
delete p;
p = NULL;
}
}
}
Person *operator->()
{
return p;
}
};
void test_func(sp &other)
{
sp s = other;
cout<<"In test_func: "<<s->getStrongCount()<<endl;
s->printInfo();
//Person *p = new Person();
//p->printInfo();
//delete p;
}
int main(int argc, char **argv)
{
int i;
sp other = new Person();
cout<<"Before call test_func: "<<other->getStrongCount()<<endl;
for (i = 0; i < 2; i++)
{
test_func(other);
cout<<"After call test_func: "<<other->getStrongCount()<<endl;
}
return 0;//main函数执行完后,再次减一,这时就把person对象销毁了
}
3. 重载*,供下面使用
Person& operator*()//重载*,供下面使用
{//不使用引用,返回的是值,就又会去构造对象(临时对象,见上述引用章节)
return *p;
}
/* 少用"Person *"; 用"sp"来代替"Person *"
* Person *per;
* 有2种操作: per->XXXx, (*per).XXX
* sp也应该有这2中操作:
* sp->XXX, (*sp).XXX*
*/
sp other = new Person();
(*other).printInfo();
4.优化:将count的操作独立出去,单独为一个类,其他类如person直接继承,就可以操作count的类。
5.优化:为了让智能指针类更通用,应当把类修改为类模版,这样就不局限于内部的指针仅指向person。
template<typename T>
class sp
{
private:
T *p;
}
最终就可以实现, 用"sp<Person>"来代替"Person *
而且不需要delete,由系统释放
/******************************************************************************************************************/
二、C++智能指针_轻量级指针
count++不是原子操作,所以在多线程的情况下是不安全的
所以引入轻量级指针(保证原子操作,保证操作count值线程安全):
1.使用安卓提供的轻量级智能指针的代码:
using namespace android::RSC;//使用安卓命名空间下的RSC命名空间
class Person : public LightRefBase<Person>
{//指定模版参数为person
public:
Person() {
cout <<"Pserson()"<<endl;
}
~Person()
{
cout << "~Person()"<<endl;
}
void printInfo(void)
{
cout<<"just a test function"<<endl;
}
};
2.被调用的安卓RefBase.h中的部分代码:
template <class T>
class LightRefBase
{
public:
inline LightRefBase() : mCount(0) { }
inline void incStrong(__attribute__((unused)) const void* id) const {
__sync_fetch_and_add(&mCount, 1);//__sync_fetch_and_add是原子操作
}
inline void decStrong(__attribute__((unused)) const void* id) const {
if (__sync_fetch_and_sub(&mCount, 1) == 1)
{//__sync_fetch_and_sub是原子操作
delete static_cast<const T*>(this);
}
}
3.注意轻量级指针只是对count的操作是线程安全的,但是对于操作的对象即智能指针还是不安全的,多线程情况下只能自己来保证智能指针的使用是线程安全的,即智能指针指向的对象的操作是线程安全的。
为啥叫轻量指针,是因为代码简单,使用方便。
/******************************************************************************************************************/
三、C++智能指针_弱指针的引入
轻量指针,代码简单,使用方便,但是
当有两个智能指针互相引用的时候,由于执行完引用计数不为零,就没办法释放,会造成内存泄漏:
/* 如果对象里含有其他对象成员:
* 构造时: 先构造其他对象成员, 再构造对象本身
* 析构时: 顺序刚好相反
*/
void test_func()
{
/* 1. 对于 new Person()
* 1.1 Person对象里的father先被构造
* 1.2 Person对象里的son被构造
* 1.3 Person对象本身
* 2. Person对象的指针传给"sp<Person> father"
* 导致: sp(T* other) 被调用
* 它增加了这个Person对象的引用计数(现在此值等于1)
*/
sp<Person> father = new Person();
/* 1. 对于 new Person()
* 1.1 Person对象里的father先被构造
* 1.2 Person对象里的son被构造
* 1.3 Person对象本身
* 2. Person对象的指针传给"sp<Person> son"
* 导致: sp(T* other) 被调用
* 它增加了这个Person对象的引用计数(现在此值等于1)
*/
sp<Person> son = new Person();
/* 它是一个"=" : this->son = son
* "="被重载, 它会再次增加该Person对象的引用计数
* 所以son对应的Person对象的引用计数增加为2
*/
father->setSon(son);
/* 它是一个"=" : this->father = father
* "="被重载, 它会再次增加该Person对象的引用计数
* 所以father对应的Person对象的引用计数增加为2
*/
son->setFather(father);
/* 当test_func执行完时, father和son被析构
* 1. 先看father:
* ~sp(): decStrong, 里面会将计数值减1 , father对应的Person的计数值等于1, 还没等于0, 所以没有delete
* 2. 对于son:
* ~sp(): decStrong, 里面会将计数值减1 , son对应的Person的计数值等于1, 还没等于0, 所以没有delete
*/
}
对于交叉引用导致内存泄漏的问题,如果两个指针是弱引用,也就是不增加对方的引用计数值,那么就不会造成内存泄漏,因此只能引用弱指针(弱引用)才能解决这个问题,
强指针(强引用):A指向B,A决定B的生死
弱指针(弱引用):A指向B,A不能决定B的生死
/******************************************************************************************************************/
四、C++智能指针_强弱指针的实现与使用
强弱指针的实现:
加入引用计数mWeak成员并提供加减操作,同时加入flag成员表示当前是强还是弱,具体实现见安卓源码.
1.源码说明:
I、在RefBase.h中的class RefBase中成员指针指向weakref_impl,也就是强弱指针的实现,weakref_impl继承于weakref_type(操作成员的抽象类),也就是说weakref_impl就是具体的实现类。
具体也可以说,
强弱指针的实现主要是加入引用计数mWeak成员并提供加减操作,同时加入flag成员表示当前是强还是弱
再将强弱指针的实现抽象出抽象类,具体实现类weakref_impl继承weakref_type抽象类,并在RefBase.h中的class RefBase中定义指向weakref_impl的指针。
II、这样实现的原因:
a.头文件中不想看到私有成员,只想看到接口,则把接口抽象出来为抽象类(放在头文件中),然后剩下的独立为实现类(放在cpp文件中),实现类继承抽象类,抽象类对外。
b.依赖对象的时候,最好依赖指针,因为直接依赖对象,当对象发生变化就会跟着发生变化,指针只占四个字节就不会跟着变化,对空间布局没有影响
2.强弱指针的使用:
I、使用安卓里现成的,
头文件:
system/core/include/utils
system/core/include/cutils
cpp:
system/core/libutils/RefBase.cpp
makefile:
%.o : %.cpp
g++ -c -o $@ $< -I include //-I include表示包含当前目录下的include目录
//查找头文件不会递归的往子目录查找,所以要么这里加要么代码中加上 目录名/XXX.h
例:
int main(int argc, char **argv)
{
wp<Person> s = new Person();//弱指针
//s->printInfo(); /* 出错, wp没有重载"->", "*" */
//(*s).printInfo(); /* 出错, wp没有重载"->", "*" */
sp<Person> s2 = s.promote();//提升为强指针,这样就可以调用了
if (s2 != 0) {
s2->printInfo();
}
return 0;
}
II、使用弱指针可以解决交叉引用导致内存泄漏的问题,但是想使用引用的对象里的函数,就必须先升级为强指针才可以调用
#include <iostream>
#include <string.h>
#include <unistd.h>
#include <utils/RefBase.h>
using namespace std;
using namespace android;
class Person : public RefBase
{//继承RefBase
private:
char *name;
wp<Person> father;
wp<Person> son;
public:
Person() {
cout <<"Pserson()"<<endl;
}
Person(char *name) {
cout <<"Pserson(char *name)"<<endl;
this->name = name;
}
~Person()
{
cout << "~Person()"<<endl;
}
void setFather(sp<Person> &father)
{
this->father = father;
}
void setSon(sp<Person> &son)
{
this->son = son;
}
char *getName(void)
{
return name;
}
void printInfo(void)
{
sp<Person> f = father.promote();
sp<Person> s = son.promote();
//cout<<"just a test function"<<endl;
cout<<"I am "<<name<<endl;
if (f != 0)
cout<<"My Father is "<<f->getName()<<endl;
if (s != 0)
cout<<"My Son is "<<s->getName()<<endl;
}
};
/* 如果对象里含有其他对象成员:
* 构造时: 先构造其他对象成员, 再构造对象本身
* 析构时: 顺序刚好相反
*/
void test_func()
{
/* 1. 对于 new Person()
* 1.1 Person对象里的father先被构造
* 1.2 Person对象里的son被构造
* 1.3 Person对象本身
* 2. Person对象的指针传给"sp<Person> father"
* 导致: sp(T* other) 被调用
* 它增加了这个Person对象的引用计数(现在此值等于1)
*/
sp<Person> father = new Person("LiYiShi");
/* 1. 对于 new Person()
* 1.1 Person对象里的father先被构造
* 1.2 Person对象里的son被构造
* 1.3 Person对象本身
* 2. Person对象的指针传给"sp<Person> son"
* 导致: sp(T* other) 被调用
* 它增加了这个Person对象的引用计数(现在此值等于1)
*/
sp<Person> son = new Person("LiErShi");
father->setSon(son);
son->setFather(father);
father->printInfo();
son->printInfo();
}
int main(int argc, char **argv)
{
test_func();
return 0;
}
C++之智能指针20170920的更多相关文章
- enote笔记法使用范例(2)——指针(1)智能指针
要知道什么是智能指针,首先了解什么称为 “资源分配即初始化” what RAII:RAII—Resource Acquisition Is Initialization,即“资源分配即初始化” 在&l ...
- C++11 shared_ptr 智能指针 的使用,避免内存泄露
多线程程序经常会遇到在某个线程A创建了一个对象,这个对象需要在线程B使用, 在没有shared_ptr时,因为线程A,B结束时间不确定,即在A或B线程先释放这个对象都有可能造成另一个线程崩溃, 所以为 ...
- C++智能指针
引用计数技术及智能指针的简单实现 基础对象类 class Point { public: Point(int xVal = 0, int yVal = 0) : x(xVal), y(yVal) { ...
- EC笔记:第三部分:17、使用独立的语句将newed对象放入智能指针
一般的智能指针都是通过一个普通指针来初始化,所以很容易写出以下的代码: #include <iostream> using namespace std; int func1(){ //返回 ...
- 智能指针shared_ptr的用法
为了解决C++内存泄漏的问题,C++11引入了智能指针(Smart Pointer). 智能指针的原理是,接受一个申请好的内存地址,构造一个保存在栈上的智能指针对象,当程序退出栈的作用域范围后,由于栈 ...
- 智能指针unique_ptr的用法
unique_ptr是独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr,如下面错误用法: std::unique_pt ...
- 基于C/S架构的3D对战网络游戏C++框架_05搭建系统开发环境与Boost智能指针、内存池初步了解
本系列博客主要是以对战游戏为背景介绍3D对战网络游戏常用的开发技术以及C++高级编程技巧,有了这些知识,就可以开发出中小型游戏项目或3D工业仿真项目. 笔者将分为以下三个部分向大家介绍(每日更新): ...
- C++ 引用计数技术及智能指针的简单实现
一直以来都对智能指针一知半解,看C++Primer中也讲的不够清晰明白(大概是我功力不够吧).最近花了点时间认真看了智能指针,特地来写这篇文章. 1.智能指针是什么 简单来说,智能指针是一个类,它对普 ...
- C++11智能指针读书笔记;
智能指针是一个类对象,而非一个指针对象. 原始指针:通过new建立的*指针 智能指针:通过智能指针关键字(unique_ptr, shared_ptr ,weak_ptr)建立的指针 它的一种通用实现 ...
随机推荐
- JAVA学习笔记--简介几个常见关键字static、final、this、super
一.static static(静态的),可以放在类.方法.字段之前. 通常,当创建类时,就是在描述那个类的外观与行为.除非用 new 创建那个类的对象,否则,实际上并未获得任何对象.执行 new 来 ...
- 直线石子合并(区间DP)
石子合并 时间限制:1000 ms | 内存限制:65535 KB 描述有N堆石子排成一排,每堆石子有一定的数量.现要将N堆石子并成为一堆.合并的过程只能每次将相邻的两堆石子堆成一堆,每次合并花费 ...
- [T-ARA][Lovey-Dovey]
歌词来源:http://music.163.com/#/song?id=22704426 作曲 : 新沙洞老虎/崔圭成 [作曲 : 新沙洞老虎/崔圭成] [作曲 : 新沙洞老虎/崔圭成] 作词 : 新 ...
- asp.net mvc 使用Ajax调用Action 返回数据【转】
使用asp.net mvc 调用Action方法很简单. 一.无参数方法. 1.首先,引入jquery-1.5.1.min.js 脚本,根据版本不同大家自行选择. <script src=& ...
- preg_replace 以及弃用的e
preg_replace (PHP 4, PHP 5) preg_replace — 执行一个正则表达式的搜索和替换 说明¶ mixed preg_replace ( mixed $pattern , ...
- 总结在Visual Studio Code创建Node.js+Express+handlebars项目
一.安装node.js环境. Node.js安装包及源码下载地址为:https://nodejs.org/en/download/ 32 位安装包下载地址 : https://nodejs.org/d ...
- 18软工实践-第八次作业(课堂实战)-项目UML设计(团队)
目录 团队信息 分工选择 课上分工 课下分工 ToDolist alpha版本要做的事情 燃尽图 UML 用例图 状态图 活动图 类图 部署图 实例图 对象图 时序图 包图 通信图 贡献分评定 课上贡 ...
- 【IdentityServer4文档】- 术语&演示服务器和测试
术语 你需要了解一下,规范.文档和对象模型使用的术语有哪些. IdentityServer IdentityServer 是一个 OpenID Connect 提供程序 - 它实现了 OpenID C ...
- UVALive - 6893 The Big Painting 字符串哈希
题目链接: http://acm.hust.edu.cn/vjudge/problem/129730 The Big Painting Time Limit: 5000MS 题意 给你一个模板串和待匹 ...
- lintcode-389-判断数独是否合法
389-判断数独是否合法 请判定一个数独是否有效. 该数独可能只填充了部分数字,其中缺少的数字用 . 表示. 注意事项 一个合法的数独(仅部分填充)并不一定是可解的.我们仅需使填充的空格有效即可. 说 ...