Priyanka Sabharwal81 Ответов: 2

Какая необходимость в enable_shared_from_this ?


Я новичок в C++11 и наткнулся на enable_shared_from_this. Я не понимаю, чего он добивается? Итак, у меня есть программа, которая использует enable_shared_from_this.

struct TestCase: enable_shared_from_this<testcase>
{
    std::shared_ptr<testcase> getptr() {
        return shared_from_this();
    }
    
    ~TestCase() { std::cout << "TestCase::~TestCase()"; }
};


int main()
{
    std::shared_ptr<testcase> obj1(new TestCase);
    std::shared_ptr<testcase> obj2 = obj1->getptr();
   // The above can be re written as below, then what is the need for shared_from_this()
  //  std::shared_ptr<testcase> obj2 = shared_ptr<testcase>(obj1);
}
Мой вопрос в том, когда мне нужен указатель на 'this- почему бы не использовать сам объект? Почему, чтобы вернуть 'this- из функции такого класса, как использование getptr() а потом возвращение shared_from_this()????
Я не понимаю.

Второй вопрос, если enable_shared_from_this не используется, почему dtor вызывается дважды, что создает проблему, сбой!!!!

Еще один способ, который я могу обойти, используя enable_shared_from_this быть такой.
Добавьте это в класс TestCase

std::shared_ptr<testcase> getptr1(shared_ptr<testcase> obj) {
        return std::shared_ptr<testcase>(obj);
    }


а из главного позвони это это:

std::shared_ptr<testcase> bp2 = bp1->getptr1(bp1);


И готово. Нам это не нужно enable_shared_from_this- С какой стати нам это нужно??

Что я уже пробовал:

Я попытался поискать в google, но все, что мне удалось найти, - это то, что такое enable_shared_from_this, почему он используется. Но мой вопрос заключается в том, нет ли другой альтернативы enable_shared_from_this (как я уже упоминал в своем вопросе.)

2 Ответов

Рейтинг:
1

Member 14516475

A case when this would come in handy is 
* When there is a design guarantee that an object will always be owned by a shared pointer.
* In this case, we can blindly use shared_from_this() to return our own shared instance to another function which accepts a shared_ptr<> version of our class.

For example, 

The DBusConnection object is always shared by many instances which means that is is always passed through shared_ptr<DBUsConnection> everywhere. So in that case we can write a code snippet like this:


В git источнике CommonAPI мы можем увидеть ссылки:

capicxx-dbus-runtime/DBusServiceRegistry.cpp at 77fd3ae09034b6f7cb87be7fd77d08cf106f53cb · GENIVI/capicxx-dbus-runtime · GitHub[^]

void DBusConnection::notifyDBusSignalHandlers(DBusSignalHandlerPath handlerPath,
                                              const DBusMessage& dbusMessage,
                                              ::DBusHandlerResult& dbusHandlerResult) {

    // ensure, the registry survives
    std::shared_ptr<DBusServiceRegistry> itsRegistry_ = DBusServiceRegistry::get(shared_from_this());

    std::map<const DBusSignalHandler*,
        std::weak_ptr<DBusProxyConnection::DBusSignalHandler>> itsHandlers;
    {
        std::lock_guard<std::mutex> dbusSignalHandlersLock(signalHandlersGuard_);

        auto signalHandlerPathIt = dbusSignalHandlers_.find(handlerPath);
        if(signalHandlerPathIt != dbusSignalHandlers_.end()) {
            itsHandlers = signalHandlerPathIt->second;
        }
    }



Если мы проверим статические функции DBusServiceRegistry::get(..) и DBusServiceRegistry::Remove (..), то заметим, что экземпляр общего указателя добавляется в статическую карту реестра и удаляется соответственно.

std::shared_ptr<DBusServiceRegistry>
DBusServiceRegistry::get(std::shared_ptr<DBusProxyConnection> _connection, bool _insert) {
    std::unique_lock<std::mutex> itsGuard(registriesMutex_);
    auto registries = getRegistryMap();
    auto registryIterator = registries->find(_connection.get());
    if (registryIterator != registries->end())
        return registryIterator->second;

    std::shared_ptr<DBusServiceRegistry> registry
        = std::make_shared<DBusServiceRegistry>(_connection);
    if (registry) {
        if(_insert) {
            registries->insert( { _connection.get(), registry } );
        }
        itsGuard.unlock();
        registry->init();
    }
    return registry;
}

void
DBusServiceRegistry::remove(std::shared_ptr<DBusProxyConnection> _connection) {
    std::lock_guard<std::mutex> itsGuard(registriesMutex_);
    auto registries = getRegistryMap();
    registries->erase(_connection.get());
}


Как мы видим здесь, когда соединение с настольной шиной отключается, мы видим, что экземпляр удаляется из карты реестра.

void DBusConnection::disconnect() {
    std::unique_lock<std::recursive_mutex> dbusConnectionLock(connectionGuard_);

    std::shared_ptr<DBusServiceRegistry> itsRegistry = DBusServiceRegistry::get(shared_from_this());

    isDisconnecting_ = true;

    if (std::shared_ptr<CommonAPI::MainLoopContext> mainLoopContext = mainLoopContext_.lock()) {
        DBusServiceRegistry::remove(shared_from_this());
        Factory::get()->releaseConnection(connectionId_);
}


Рейтинг:
0

Philippe Mori

Используя Google, можно легко найти ответ и примеры, подобные этому: std::enable_shared_from_this - cppreference.com[^].

Если вам трудно понять документацию, попробуйте использовать примеры в отладчике.

Эти ссылки также полезны для понимания цели:
enable_shared_from_this класс[^].
Безопасное использование enable_shared_from_this | Musing Mortoray[^]
enable_shared_from_this - ссылка на C++ [^]
c++ - какова полезность enable_shared_from_this - переполнение стека[^]enable_shared_from_this - 1.60.0[^]

Ну, в большинстве случаев вы можете избежать этой проблемы, написав другой код. Использование enable_shared_from_this по существу позволяет коду работать должным образом в тех случаях, когда он не будет работать иначе. Используя это, он позволяет найти внутренние данные, используемые для поддержания количества ссылок.

Насколько я понимаю, он позволяет функции-члену вызывать другие функции, которые используют shared_pointers в качестве параметров.

Вероятно, можно было бы написать подобный код вручную...

Обновление:
Вот код, чтобы объяснить эту проблему...

void f(shared_pointer<A> x) { }

class A 
{
public:
  A() { }
  ~A() { std::cout << "~A" << std::endl; }
  void test() { /* How do you call f function here? */ }

private:
  A(const A&);
  A& operator=(const A&);
};

int main()
{
  shared_pointer<A> a(new A);
  a->test();
}


Сделайте так, чтобы приведенный выше код работал с вами простым решением... Если вы можете сделать это только изменив test функция и без того, чтобы деструктор вызывался дважды, тогда в этот момент было бы законно задаться вопросом, почему код не является сложным...

В противном случае, используя enable_shared_from_this, вы можете сделать так, чтобы этот код работал так, как ожидалось.

Это по существу встраивает слабый общий указатель в класс, так что вы можете создать несколько общих указателей из этого (то есть что-то, что доступно из функции-члена, например test выше.


Priyanka Sabharwal81

Спасибо, Филипп. Но если бы каждый орган понимал каждую вещь, тогда не было бы форума вопросов никогда!!! Ссылка, которую вы отправили, рассказывает, как использовать enable_shared_from_this и почему его использовать. Как и почему, не отвечает на мой вопрос. Меня беспокоит то, что синтаксис, который я написал, чтобы обойти использование enable_shared_from_this, не является сложным. Скорее я чувствовал, что enable_shared_from_this добавляет сложности. Есть ли какой-то другой сценарий, который я упускаю, который может быть разрешен только с помощью enable_shared_from_this, а не альтернативы, которую я написал.
Я знаю, что у каждой проблемы есть несколько решений, но мы предпочитаем использовать простое. Легкий означает, что он более удобочитаем и легко записывается.
ваше здоровье!

Philippe Mori

Я обновил свой ответ, чтобы показать проблемный случай. Если вы можете заставить его работать правильно с более простым кодом, то покажите его.

Помните, что этот код был написан экспертом...