auto_ptr解析

        auto_ptr是当前C++标准库中提供的一种智能指针,或许相对于boost库提供的一系列眼花缭乱的智能指针,这个不怎么智能的智能指针难免会黯然失色。诚然,auto_ptr有这样那样的不如人意,以至于程序员必须像使用”裸“指针那样非常小心地使用它才能保证不出错,以至于它甚至无法适用于同是标准库中的那么多的容器和一些算法,但即使如此,我们仍然不能否认这个小小的auto_ptr所蕴含的价值与理念。
  auto_ptr的出现,主要是为了解决“被异常抛出时发生资源泄漏”的问题,即如果我们让资源在局部对象构造时分配,在局部对象析构时释放。这样即使在函数执行过程时发生异常而退出,也会因为异常能保证局部对象被析构从而保证资源被释放。auto_ptr就是基于这个理念而设计,这最早出现在C++之父Bjarne Stroustrup的两本巨著TC++PL和D&E中,其主题为"resource acquisition is initialization"(raii,资源获取即初始化),然后又在Scott Meyer的《More Effective C++》中相关章节的推动下,被加入了C++标准库。
  下面我就列出auto_ptr的源代码,并详细讲解每一部分。因为标准库中的代码要考虑不同编译器支持标准的不同而插入了不少预编译判断,而且命名可读性不是很强(即使是侯捷老师推荐的SGI版本的stl,可读性也不尽如人意),这里我用了Nicolai M. Josuttis(《The C++ standard library》作者)写的一个auto_ptr的版本,并做了少许格式上的修改以易于分析阅读:

namespace std {

    // auxiliary type to enable copies and assignments (now global)

    template<class Y>

    struct auto_ptr_ref {

        Y* yp;

        auto_ptr_ref (Y* rhs):yp(rhs) {

        }

    };

 

    template<class T>

    class auto_ptr {

      private:

        T* ap;    // refers to the actual owned object (if any)

      public:

        typedef T element_type;

        //构造函数

        explicit auto_ptr (T* ptr = 0) throw()

         : ap(ptr) {

        }

        // 析构函数

        ~auto_ptr() throw() {

            delete ap;

        }

        // 拷贝构造函数

        auto_ptr (auto_ptr& rhs) throw()

         : ap(rhs.release()) {

        }

        template<class Y>

        auto_ptr (auto_ptr<Y>& rhs) throw()

         : ap(rhs.release()) {

        }        

        // 赋值操作符

        auto_ptr& operator= (auto_ptr& rhs) throw() {

            reset(rhs.release());

            return *this;

        }

        template<class Y>

        auto_ptr& operator= (auto_ptr<Y>& rhs) throw() {

            reset(rhs.release());

            return *this;

        }        

 

        // value access

        T* get() const throw() {

            return ap;

        }

        T& operator*() const throw() {

            return *ap;

        }

        T* operator->() const throw() {

            return ap;

        }

 

        // release ownership

        T* release() throw() {

            T* tmp(ap);

            ap = 0;

            return tmp;

        }

 

        // reset value

        void reset (T* ptr=0) throw() {

            if (ap != ptr) {

                delete ap;

                ap = ptr;

            }

        }

 

/* special conversions with auxiliary type to enable copies and assignments*/

        auto_ptr(auto_ptr_ref<T> rhs) throw()

         : ap(rhs.yp) {

        }

        auto_ptr& operator= (auto_ptr_ref<T> rhs) throw() {  // new

             reset(rhs.yp);

             return *this;

        }

        template<class Y>

        operator auto_ptr_ref<Y>() throw() {

            return auto_ptr_ref<Y>(release());

        }

        template<class Y>

        operator auto_ptr<Y>() throw() {

            return auto_ptr<Y>(release());
        }
    };
} 


1 构造函数与析构函数


备注:C++支持两种初始化变量的方式:复制初始化(copy-initialization)和直接初始化(dierct-initialization);复制初始化语法用等号,直接初始化则是把初始化式放在括号中(《c++ primer 4th》p42):
int ival(1024);     //直接初始化
int ival = 1024;    //复制初始化

auto_ptr<>不允许使用一般指针惯用的初始化方式,你必须直接初始化(《stl标准程序库》p40):
std::auto_ptr<ClassA> ptr1(new ClassA);  //OK
std::auto_ptr<ClassA> ptr2 = new ClassA; //ERROR

只有auto_ptr可以拿来当做另外一个auto_ptr的初始值,普通指针是不行的(《stl标准程序库》p41):
std::auto_ptr<ClassA> ptr;
ptr = new ClassA;                              //ERROR
ptr = std::auto_ptr<ClassA>(new ClassA);  //ok delete old object and own new

在使用auto_ptr时,有几点需要注意的地方:
1) auto_ptr是这样一种指针,它是“它所指向的对象”的拥有者,当身为对象拥有者的auto_ptr被摧毁时,该对象也将遭到摧毁。auto_ptr要求一个对象只能有一个拥有者,绝对不应该出现多个auto_ptr同时拥有一个对象的情况,像这样:
int* p = new int(0);
auto_ptr<int> ap1(p);
auto_ptr<int> ap2(p);
因为ap1与ap2都认为指针p是归它管的,在析构时都试图删除p,两次删除同一个对象的行为在C++标准中是未定义的。所以我们必须防止这样使用auto_ptr。

2) 并不存在针对array而设计的auto_ptr,考虑下面这种用法:
int* pa = new int[10];
auto_ptr<int> ap(pa);
因为auto_ptr的析构函数中删除指针用的是delete,而不是delete [],所以我们不应该用auto_ptr来管理一个数组指针。

3) 构造函数的explicit关键词有效阻止从一个“裸”指针隐式转换成auto_ptr类型。

4) 因为C++保证删除一个空指针是安全的,所以我们没有必要把析构函数写成:
~auto_ptr() throw()
{
    if(ap) delete ap;
}

2 拷贝构造与赋值
与引用计数型智能指针不同的,auto_ptr要求其对“裸”指针的完全占有性。也就是说一个”裸“指针不能同时被两个以上的auto_ptr所拥有。那么,在拷贝构造或赋值操作时,我们必须作特殊的处理来保证这个特性。auto_ptr的做法是“所有权转移”,即拷贝或赋值的源对象将失去对“裸”指针的所有权,所以,与一般拷贝构造函数、赋值函数不同,auto_ptr的拷贝构造函数、赋值函数的参数为引用而不是常引用(const reference).当然,一个auto_ptr也不能同时拥有两个以上的“裸”指针,所以,拷贝或赋值的目标对象将先释放其原来所拥有的对象。下面就auto_ptr拥有权转移的情况举例进行说明:
拷贝构造函数:
std::auto_ptr<ClassA> ptr1(new ClassA); //initialize an auto_ptr with a new object
std::auto_ptr<ClassA> ptr2(ptr1);        //copy th auto_ptr
                                                //transfers ownership from ptr1 to ptr2
在第一个语句中,ptr1拥有那个new出来的对象。在第二个语句中,拥有权从ptr1转移到ptr2。此后ptr2就拥有了那个new出来的对象,而ptr1不再拥有它。这样,对象就只会被delet一次--在ptr2销毁的时候。
赋值操作符(赋值函数):
std::auto_ptr<ClassA> ptr1(new ClassA); //initialize an auto_ptr with a new object
std::auto_ptr<ClassA> ptr2(new ClassA); //create another auto_ptr
ptr2 = ptr1;          //assign the auto_ptr
                        //delete object owned by ptr2
                        //transfers ownership from ptr1 to ptr2
在这里,赋值动作将拥有权从ptr1转移到ptr2,于是,ptr2拥有了先前被ptr1所拥有的那个对象。如果ptr2被赋值前正拥有另外一个对象,赋值动作发生时会调用delete,将该对象删除。

这里的注意点是:
1) 因为一个auto_ptr被拷贝或被赋值后,其已经失去对原对象的所有权,这个时候,对这个auto_ptr的提领(dereference)操作是不安全的。如下:
int* p = new int(0);
auto_ptr<int> ap1(p);
auto_ptr<int> ap2 = ap1;
cout<<*ap1; //错误,此时ap1只剩一个null指针在手了
这种情况较为隐蔽的情形出现在将auto_ptr作为函数参数按值传递,因为在函数调用过程中在函数的作用域中会产生一个局部对象来接收传入的auto_ptr(拷贝构造),这样,传入的实参auto_ptr就失去了其对原对象的所有权,而该对象会在函数退出时被局部auto_ptr删除。如下:
void f(auto_ptr<int> ap){cout<<*ap;}
auto_ptr<int> ap1(new int(0));
f(ap1);
cout<<*ap1; //错误,经过f(ap1)函数调用,ap1已经不再拥有任何对象了。
因为这种情况太隐蔽,太容易出错了,所以auto_ptr作为函数参数按值传递是一定要避免的。或许大家会想到用auto_ptr的指针或引用作为函数参数或许可以,但是仔细想想,我们并不知道在函数中对传入的auto_ptr做了什么,如果当中某些操作使其失去了对对象的所有权,那么这还是可能会导致致命的执行期错误。也许,用const reference的形式来传递auto_ptr会是一个不错的选择。

2)我们可以看到拷贝构造函数提供了一个成员模板,使得可通过型别自动转换,构造出合适的auto_ptr。例如,根据一个派生类的对象,构造出一个基类对象的auto_ptr。同样,赋值操作符(赋值函数)也提供了一个成员模板,使得可通过型别自动转换,赋值给合适的auto_ptr。例如,将一个派生类的对象,赋值给一个基类对象的auto_ptr。
class base{};
class derived: public base{};
那么下列代码就可以通过,实现从auto_ptr<derived>到auto_ptr<base>的隐式转换,因为derived*可以转换成base*类型
auto_ptr<base> apbase = auto_ptr<derived>(new derived);

3) auto_ptr不满足stl容器对元素的要求。
auto_ptr并不满足stl标准容器对元素的最基本要求。因为在拷贝和赋值动作之后,原本的auto_ptr和新产生的auto_ptr并不相等。在拷贝和赋值过后,原来的auto_ptr会交出拥有权,而不是拷贝给新的auto_ptr。因此绝对不要将auto_ptr作为标准容器的元素。

3 提领操作(dereference)
提领操作有两个操作,一个是返回其所拥有的对象的引用,另一个是则实现了通过auto_ptr调用其所拥有的对象的成员。如:
struct A{ … }
auto_ptr<A> apa(new A);
(*apa).f();
apa->f();
当然, 我们首先要确保这个智能指针确实拥有某个对象,否则,这个操作的行为即对空指针的提领是未定义的。

4 辅助函数
1) get用来显式的返回auto_ptr所拥有的对象指针。我们可以发现,标准库提供的auto_ptr既不提供从“裸”指针到auto_ptr的隐式转换(构造函数为explicit),也不提供从auto_ptr到“裸”指针的隐式转换,从使用上来讲可能不那么的灵活,考虑到其所带来的安全性还是值得的。
2) release,用来转移所有权。
3) reset,用来接收所有权,如果接收所有权的auto_ptr如果已经拥有某对象,必须先释放该对象。

5 特殊转换
auto_ptr中剩余的部分(辅助型别auto_ptr_ref及其相关函数)涉及非常精致的技巧,使我们得以拷贝和赋值non-const auto_ptrs,却不能拷贝和赋值const auto_ptr(更加详细的说明,参考《stl标准模板库》P55)。

6 auto_ptr运用实例(《STL标准模板库》p47):

下面的一个例子展示了auto_ptr转移拥有权的行为:

#include <iostream>

#include <memory>

using namespace std;

 

/* define output operator for auto_ptr

 * - print object value or NULL

 */

template <class T>

ostream& operator<< (ostream& strm, const auto_ptr<T>& p)//参数p是常量引用,所以不发生拥有权转移

{

    // does p own an object ?

    if (p.get() == NULL) {

        strm << "NULL";         // NO: print NULL

    }

    else {

        strm << *p;             // YES: print the object

    }

    return strm;

}

 

int main()

{

    auto_ptr<int> p(new int(42));

    auto_ptr<int> q;

 

    cout << "after initialization:" << endl;

    cout << " p: " << p << endl;

    cout << " q: " << q << endl;

 

    q = p;

    cout << "after assigning auto pointers:" << endl;

    cout << " p: " << p << endl;

    cout << " q: " << q << endl;

 

    *q += 13;                   // change value of the object q owns

    p = q;

    cout << "after change and reassignment:" << endl;

    cout << " p: " << p << endl;

    cout << " q: " << q << endl;
} 

输出结果为:

after initialization:

p: 42

q: NULL

after assigning auto pointers:

p: NULL

q: 42

after change and reassignment:

p: 55

q: NULL

 


参考资料:

http://www.cppblog.com/SmartPtr/archive/2008/01/22/27549.html

《STL标准程序库》P38-58

 

http://blog.csdn.net/wuliming_sc/article/details/3820119

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了python应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
std::shared_ptr<FiniteStateMachine> WidgetScheduleNodeImpl::MakeFiniteStateMachine() { auto builder = FiniteStateMachine::Builder::New(GetDescription(), S_WIDGET_INIT); if (builder == nullptr) { return nullptr; } builder->MakeTransition(S_WIDGET_INIT, E_START_WIDGET, S_WIDGET_WAITING, [this](FiniteStateMachine &machine, uint32_t event) { OnStartSchedule(machine, event); }); builder->MakeTransition(S_WIDGET_WAITING, E_START_AUTH, S_WIDGET_AUTH_RUNNING, [this](FiniteStateMachine &machine, uint32_t event) { OnStartAuth(machine, event); }); builder->MakeTransition(S_WIDGET_WAITING, E_CANCEL_AUTH, S_WIDGET_AUTH_FINISHED, [this](FiniteStateMachine &machine, uint32_t event) { OnStopSchedule(machine, event); }); builder->MakeTransition(S_WIDGET_WAITING, E_NAVI_PIN_AUTH, S_WIDGET_AUTH_FINISHED, [this](FiniteStateMachine &machine, uint32_t event) { OnNaviPinAuth(machine, event); }); builder->MakeTransition(S_WIDGET_AUTH_RUNNING, E_COMPLETE_AUTH, S_WIDGET_AUTH_FINISHED, [this](FiniteStateMachine &machine, uint32_t event) { OnSuccessAuth(machine, event); }); builder->MakeTransition(S_WIDGET_AUTH_RUNNING, E_CANCEL_AUTH, S_WIDGET_AUTH_FINISHED, [this](FiniteStateMachine &machine, uint32_t event) { OnStopSchedule(machine, event); }); builder->MakeTransition(S_WIDGET_AUTH_RUNNING, E_NAVI_PIN_AUTH, S_WIDGET_AUTH_FINISHED, [this](FiniteStateMachine &machine, uint32_t event) { OnNaviPinAuth(machine, event); }); builder->MakeTransition(S_WIDGET_AUTH_RUNNING, E_START_AUTH, S_WIDGET_AUTH_RUNNING, [this](FiniteStateMachine &machine, uint32_t event) { OnStartAuth(machine, event); }); builder->MakeTransition(S_WIDGET_AUTH_RUNNING, E_UPDATE_AUTH, S_WIDGET_AUTH_RUNNING, [this](FiniteStateMachine &machine, uint32_t event) { OnStopAuthList(machine, event); }); return builder->Build(); }代码解析
07-08

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值