共享指向静态const对象的指针?

时间:2018-05-29 12:28:15

标签: c++ c++11 shared-ptr

对于上下文,我正在实现一个用户访问系统,人们可以登录我的应用程序。当前登录用户由指向CUser

的指针访问
std::shared_ptr<CUser> m_pCurrentUser;

当用户注销时,我希望此指针指向我声明为静态const的“默认”用户:

static const CUser xDefaultUser(L"Default", L"password", CUser::EAccessLevels::eAnon);

我的第一个问题是,是否将静态const应用于默认用户对象是正确的。我的理由是它永远不应该改变(const),并且我希望它在应用程序的生命周期中可用(静态)。

第二个问题是我应该如何将默认用户指定为m_pCurrentUser。我应该声明const shared_ptr<CUser>而不是直接对象吗?

4 个答案:

答案 0 :(得分:3)

将指向静态对象的指针分配给shared_ptr不是一个好主意。你会得到内存损坏,因为ptr没有这个内存:

shared_ptr<CUser> ptr = &xDefaultUser;
ptr = nullptr; // crash

您可以使用默认对象创建static const shared_ptr。在这种情况下,内存不会被破坏。请看下面的例子:

#include <string>
#include <memory>
#include <iostream>

class User {
public:
    User(const std::string& name)
        : _name(name)
    {}
    ~User() { std::cout << "Bye, " << _name << std::endl; }
    void print() { std::cout << _name << std::endl; }
private:
    std::string _name;
};

static const std::shared_ptr<User> s_defaultUser = std::make_shared<User>("<default>");

class UserMgr {
public:
    UserMgr()
        : m_current(s_defaultUser)
    {}

    void MakeCurrent(std::shared_ptr<User> u) { 
        if (u) {
            m_current = u;
        } else {
            m_current = s_defaultUser;
        }
    }

    std::shared_ptr<User> GetCurrent() { return m_current; }
private:
    std::shared_ptr<User> m_current;   
};

int main() {
    UserMgr mgr;

    mgr.MakeCurrent(std::make_shared<User>("User 1"));
    mgr.GetCurrent()->print();
    mgr.MakeCurrent(std::make_shared<User>("User 2"));
    mgr.GetCurrent()->print();
    mgr.MakeCurrent(std::make_shared<User>("User 3"));
    mgr.GetCurrent()->print();
    mgr.MakeCurrent(nullptr);
    mgr.GetCurrent()->print();

    return 0;
}

Demo on coliru

答案 1 :(得分:2)

智能指针shared_ptrunique_ptr必须拥有他们指向的对象,并且在不再需要时将删除它。

当然,你总能做到:

m_pCurrentUser = std::make_shared(xDefaultUser);

没有什么会破坏,指针将保存静态const对象的副本。只有原始指针或weak_ptr不关心所有权。

因此,如果你可以接受复制静态const对象的开销,那就这样:你保持干净的智能指针处理。

如果您不能接受,那么您将需要构建一个特殊类型的智能指针,通常表现为shared_ptr,但在特殊情况下也不能销毁其指向的对象。 简单方式是拥有一个bool owner成员,应该在删除指针对象之前进行测试。我不确定它的所有含义,所以我的建议是克制你是否可以

答案 2 :(得分:2)

存取方法怎么样?

const CUser* GetCurrentUser() 
{
  return (m_pCurrentUser == nullptr) ? &xDefaultUser : m_pCurrentUser.get();
}

它允许您控制对用户的访问(例如,在非const版本中您可以返回副本或空指针)并且它可以防止您必须解决忘记设置{的错误{1}}注销时的默认用户,最终为空。

答案 3 :(得分:0)

你需要一个额外的指针&#34; state&#34;意思是&#34;用户退出&#34;,这与&#34;未初始化的用户&#34;不同(这将是nullptr)。您可以使用std :: variant来实现它(如果您可以使用C ++ 17,否则boost :: variant也应该工作)

#include <memory>
#include <variant>
#include <iostream>

struct LoggedOut{
    int operator*(){return 50;}
};


int main(){
    std::variant<std::shared_ptr<int>, LoggedOut> pCurrentUser(nullptr);
    {
        pCurrentUser = std::make_shared<int>(1);
        auto newuser = pCurrentUser;
    }
    std::cout<< *std::get<0>(pCurrentUser)<<"\n";

    auto logout = [](auto& p){ p=LoggedOut(); };

    logout(pCurrentUser);
    std::cout<<*std::get<1>(pCurrentUser)<<"\n"; //prints 50
    //std::cout<<*std::get<0>(pCurrentUser)<<"\n"; //runtime error: bad variant access                                                                                                                             
}

如果您不想使用std :: get,您可以轻松地将它们隐藏在自定义指针类中,这样就可以了解

#include <memory>
#include <variant>
#include <iostream>

struct LoggedOut{
    int operator*(){return 50;}
};

template<typename T>
struct myPointer : public std::variant<std::shared_ptr<int>, LoggedOut>{
    using super =  std::variant<std::shared_ptr<int>, LoggedOut>;
    using super::variant;
    auto operator*(){ return std::get_if<0>(this) ? *std::get<0>(*this) : *std::get<1>(*this); }
};

int main(){
    myPointer<int> pCurrentUser(nullptr);
    {
        pCurrentUser = std::make_shared<int>(1);
        auto newuser = pCurrentUser;
    }
    std::cout<< *pCurrentUser<<"\n";

    auto logout = [](auto& p){ p=LoggedOut(); };

    logout(pCurrentUser);
    std::cout<<*pCurrentUser<<"\n";
    std::cout<<*std::get<0>(pCurrentUser)<<"\n"; //runtime error: bad variant access                                                                                                                               
}