对于上下文,我正在实现一个用户访问系统,人们可以登录我的应用程序。当前登录用户由指向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>
而不是直接对象吗?
答案 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;
}
答案 1 :(得分:2)
智能指针shared_ptr
和unique_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
}