Общий указатель на статический объект const? - PullRequest
0 голосов
/ 29 мая 2018

Для контекста я внедряю систему доступа пользователей, где люди могут войти в мое приложение.Текущий вошедший в систему пользователь получает доступ к указателю на 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 голосов
/ 29 мая 2018

Вам нужен дополнительный указатель «состояние», означающий «пользователь вышел из системы», который не совпадает с «неинициализированным пользователем» (который будет равен nullptr).Вы можете использовать std :: variable для его реализации (если вы можете использовать C ++ 17, в противном случае boost :: option тоже будет работать)

#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                                                                                                                               
}
0 голосов
/ 29 мая 2018

Умные указатели shared_ptr и unique_ptr должны владеть объектом, на который они указывают, и они будут удалять его, когда он больше не нужен.

Конечно, вы всегда можете сделать:

m_pCurrentUser = std::make_shared(xDefaultUser);

ничего не сломается, указатель будет содержать копию вашего статического объекта const.Только необработанный указатель или weak_ptr не заботится о владении.

Так что, если вы можете принять накладные расходы на копирование статического объекта const, действуйте следующим образом: вы сохраняете чистую обработку интеллектуального указателя.

Если вы не можете принять это, вам придется создать специальный класс интеллектуального указателя, который обычно ведет себя как shared_ptr, но также не сможет уничтожить указанный объект при особых обстоятельствах. простой способ будет иметь элемент bool owner, который должен быть проверен непосредственно перед удалением объекта указателя.Я не уверен во всем этом, поэтому мой совет: воздержитесь, если сможете

0 голосов
/ 29 мая 2018

Как насчет метода доступа?

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

Он позволяет вам контролировать доступ к пользователю (например, в не-const версии вы можете вернуть копию или нулевой указатель), и онизбавляет вас от необходимости исправлять ошибки, когда вы забыли установить m_pCurrentUser для пользователя по умолчанию при выходе из системы, и в итоге он становится нулевым.

0 голосов
/ 29 мая 2018

Назначение указателя на статический объект для 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;
}

Демонстрация на coliru

...