【问题标题】:Safe reference in C++ (single-ownership pointer with notify semantics)C++ 中的安全引用(具有通知语义的单一所有权指针)
【发布时间】:2013-12-04 11:21:47
【问题描述】:

我需要一个对象的单一所有权,因为我需要能够按需销毁它(这有时是有道理的;在这种情况下,对象代表一个登录会话,出于安全原因,用户想要关闭该会话) .我们称这个对象为session。其他客户端对象保留对session 的引用,但当然,当客户端访问该引用时,它可能已失效。

我所追求的是一个“安全引用”,它在原始对象被销毁时得到通知,并优雅地向客户端报告(异常,布尔值),而不是段错误。

这样的事情存在吗?最好使用标准 C++/Boost 中可用的内容。最好是 C++03。 shared_ptrweak_ptr 几乎是我所追求的,只要 shared_ptrs 没有延长 session 的生命周期。我需要保证 session 已被销毁,而流浪的 shared_ptr 会阻止这种情况发生。

【问题讨论】:

  • 根据所有权标准,您似乎更想要std::unique_ptr。不幸的是,它不可能在 C++03 中实现,所以没有 Boost 等价物。
  • 这个东西有一个设计模式
  • @JoachimPileborg 这可能行得通。删除器可以将 shared_ptr 指向“session_health”对象,该对象在删除时设置为“dead”。可惜我在 C++03 中没有 unique_ptr
  • @Nik 真的吗?如何强制销毁 session 对象。

标签: c++ reference smart-pointers


【解决方案1】:

您要求的设计存在根本问题。

假设您有一个会话和该会话的用户。用户检查会话是否有效,然后使用它。同时会话变为无效,在检查和使用之间。 weak_ptr 通过允许用户升级到 shared_ptr 来解决这个问题,然后检查并使用它。您的设计排除了这种情况。

如果你愿意忽略这个问题,我有一个解决方案。

该对象保留一个shared_ptr<void>(分配为char)作为成员。它公开了weak_ptr<void>,可用于跟踪其生命周期,但不能确定它。对象本身是unique_ptr 存储的,用户保留一个原始指针和生命周期跟踪器对。

这不提供生命周期终止的立即更新:为此使用回调模式。

【讨论】:

  • 你是对的,但你描述的情况只会发生在多线程代码中。我们已经需要在不同的地方进行锁定以防止出现竞争情况。这只是另一个。
  • 您是否建议使用shared_ptr<void> 指向的char 作为布尔“我还活着”标志?
  • @thehouse 没有。它只是您可以创建为shared_ptr 的最小的东西。存在是“我还活着”标志:您要跟踪其生命周期的对象拥有shared_ptr,因此它们的生命周期是一致的。虽然多线程是上述问题发生的一种方式,但它并不是唯一的:每次你离开本地流控制(比如调用一个函数)你必须证明它没有删除会话,这很痛苦。
【解决方案2】:

可能是这样的:

class Session
{
    private:
    class Implementation {};

    public:
    Session()
    :   m_self(0) // You might do better, here
    {}

    ~Session() { delete m_self; }

    private:
    Session(const Session&); // No copy
    Session& operator = (const Session&); // No copy

    public:
    bool valid() const { return bool(m_self); }
    void terminate() { delete m_self; m_self = 0; }

    private:
    Implementation* m_self;
};

上面的类与 std::shared_ptr 或 std::unique_ptr 没有相似之处。每个会话对象只能通过引用传递。

如果您需要发出会话终止(或其他事件)的信号,我建议将 boost::signal2 放入实现中。

【讨论】:

  • 这实质上将Session 转换为智能引用,原始会话变为Implementation。我想我希望有一些更通用、更少干扰的东西。我认为shared_ptr<optional<Implementation>> 会做同样的事情,不是吗?
  • optional_session->swap(optional<Implementation>()) 相当于你的session.terminate(),我想?然后所有客户端都可以使用if (optional_session) 表示与if (session.valid()) 相同。
【解决方案3】:

Session 类封装在 SharedSession 中,它有一个对 Session 的引用。 SharedSession 类向客户端提供 Session 类的行为。当尝试访问会话时,客户端可以关闭会话和所有其他引用得到异常。 此代码示例中未涵盖线程情况下的锁定。

#include<iostream>
#include<utility>

//- single object ownership
//- ensure shared object is not deleted by one of the reference deletion;
//- client should be notified when pointed memory is deleted explicitly on request.

//Proxy class for Session
class SharedSession{

  //Session class
  class Session{

    public:

      Session()
      {
        std::cout<<"Session ctr"<<std::endl;
      }

      bool isValidSession()
      {
        std::cout<<"Is valid session"<<std::endl;
      }

      bool login(std::string user,std::string password,std::string& sessionId)
      {
        std::cout<<"Login session"<<std::endl;
        //authenticate user - password and generate session id.
        sessionId = "abd123";
        return true;
        //return false //in case of failure
      }

      ~Session()
      {
        std::cout<<"Session dtr"<<std::endl;
      }


    private:

      std::string _userName;
      std::string _password;
      std::string _sessionId;

      Session(const Session& rhs){}
      Session& operator=(const Session& rhs){}
  };

  Session* _sessionInstance;
  //number of SharedSession objects created
  static int _sessionCount;
  //single ownership of sesion maintained in pair
  static std::pair<int,Session*> _sessionPair;
  //maintain state of session 
  static std::pair<bool,int> _sessionValid;

  public:

  SharedSession()
  {
    ++_sessionCount;
    std::cout<<"Shared Session "<<_sessionCount<<std::endl;
    if(_sessionCount == 1)
    {
      _sessionInstance = new Session();
      _sessionPair = std::make_pair(1,_sessionInstance);
      _sessionValid = std::make_pair(true,_sessionCount);
    }
    if(!_sessionValid.first)
      throw -1;//session is deleted
    else
    {
      _sessionValid.second = _sessionCount;
      _sessionInstance = NULL;
    }
  }

  ~SharedSession()
  { 
    std::cout<<"Shared session dtr  "<<_sessionValid.second<<std::endl;
    if(_sessionValid.second == 1 && _sessionValid.first)
    {
      delete (_sessionPair.second);
      std::cout<<"session deleted"<<std::endl;
      _sessionPair.second = NULL;
      _sessionValid.first = false;
    }
    _sessionValid.second -= 1;
    --_sessionCount;
  }

  bool closeSession()
  {
    //invalidate session
    _sessionValid.first = false;
    std::cout<<"close session"<<std::endl;
    delete _sessionPair.second;
  }

  bool isValidSession()
  {
    std::cout<<"session state "<<_sessionValid.first<<std::endl;
    if(_sessionValid.first)
      _sessionPair.second->isValidSession();
    else
      //notify client about deleted session
      throw -1;
  }

  bool login(std::string user,std::string password,std::string& sessionId)
  {
    if(_sessionValid.first)
      return _sessionPair.second->login(user,password,sessionId);
      //notify client about deleted session
    throw -1;
  }

  //any other operations which Session class exposes
};

int SharedSession::_sessionCount = 0;
std::pair<int,SharedSession::Session*> SharedSession::_sessionPair;
std::pair<bool,int> SharedSession::_sessionValid;

int main()
{
  SharedSession session1;
  SharedSession session2;
  try
  {
    session1.closeSession();
    session2.isValidSession();
  }
  catch(int& error)
  {
    std::cout<<"Exception occured  "<<error<<std::endl;
    std::cout<<"Session already deleted."<<std::endl;
  }
  return 0;
}

【讨论】:

    【解决方案4】:

    这样的事情怎么样:

    #include <assert.h>
    
    template <class T>
    class notify_ptr
    {
    public:
        notify_ptr(T* ptr):m_ptr(ptr){};
        void kill()
        {
            if (m_ptr)
            {
                delete m_ptr;
                m_ptr = 0;
            }
        }
        bool isAlive()
        {
            return m_ptr!=0;
        }   
        T* operator->()
        {
            if (!m_ptr)
            {
                assert("Using a dead reference !");
                return 0; // segfault
            }
            return m_ptr;
        }
    private:
        T* m_ptr;
    };
    
    class session
    {
    public:
        session():i(0){}
        void AddOne(){++i;}
    private:
        int i;
    };
    
    void bar(notify_ptr<session>& mySession)
    {
        mySession->AddOne();
        mySession.kill();
    }
    
    int main()
    {
        notify_ptr<session> ptr(new session);
        bar(ptr);
        if (ptr.isAlive())
        {
            ptr->AddOne();
        }
        return 0;
    }
    

    【讨论】:

    • 这不起作用,因为指针的副本不共享它们的活跃度。您通过notify_ptr 的一个副本杀死该对象,而另一个副本告诉您它还活着。
    猜你喜欢
    • 2013-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-21
    • 2014-07-25
    • 1970-01-01
    • 2017-04-29
    相关资源
    最近更新 更多