Безопасная ссылка в C++ (указатель единого владельца с семантикой уведомления)

Мне нужно единоличное владение объектом, потому что я должен иметь возможность уничтожить его по требованию (иногда это имеет смысл; в этом случае объект представляет собой зарегистрированный сеанс, который по соображениям безопасности пользователь хочет закрыть). Давайте назовем этот объект session, Другие объекты клиента хранят ссылки на session, но, конечно, он может быть мертвым, когда клиенты обращаются к ссылке.

То, что мне нужно, - это "безопасная ссылка", которая уведомляется, когда исходный объект уничтожается, и сообщает об этом изящно (исключение, логическое значение) клиенту, а не segfault.

Существует ли что-нибудь подобное? Предпочтительно использовать то, что доступно в стандартном C++/Boost. Предпочтительно C++03. shared_ptr с weak_ptr почти то, что я после, если только shared_ptrс не продлил жизнь session, Я должен гарантировать, что session был разрушен и заблудился shared_ptr помешает этому.

4 ответа

Существует фундаментальная проблема с дизайном, который вы запрашиваете.

Предположим, у вас есть сеанс и пользователь сеанса. Пользователь проверяет, что сеанс действителен, а затем использует его. Между тем сеанс становится недействительным, между проверкой и использованием. weak_ptr имеет дело с этим, позволяя пользователю обновить до shared_ptr, затем проверьте и используйте это. Ваш дизайн исключает это.

Если вы готовы игнорировать эту проблему, у меня есть решение.

Объект хранит shared_ptr<void> (выделяется как char) в качестве члена. Это выставляет weak_ptr<void> который может использоваться для отслеживания его времени жизни, но не для его определения. Сам объект unique_ptr сохраняются, а пользователи сохраняют необработанный указатель и пару трекеров времени жизни.

Это не обеспечивает немедленного обновления завершения жизни: для этого используйте шаблон обратного вызова.

Может быть что-то вроде:

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 находится внутри 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;
}

Как насчет чего-то вроде этого:

#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;
}
Другие вопросы по тегам