Почему enable_shared_from_this вылетает, если наследование не является общедоступным, а не вызывает ошибку - PullRequest
1 голос
/ 10 июня 2019

Я использовал shared_ptr в проекте.И в какой-то момент мне пришлось сохранить необработанные указатели как пустые, а затем преобразовать их обратно в форму shared_ptr в обратном вызове, где был передан void *.Но по какой-то причине код продолжал падать.Я не понимаю, почему, так как я не получал никаких ошибок или предупреждений компилятора.Но я заметил, что когда я наследовал от std::enable_shared_from_this, я не назначал его публичным наследством.И это было причиной сбоя.

Я написал пример кода, и мне просто интересно, почему это происходит.

#include <memory>
#include <iostream>

class TestShared : public std::enable_shared_from_this<TestShared>{
private:
    int32_t id;
public:
    TestShared(int32_t id){
        this->id = id;
    }
    std::shared_ptr<TestShared> getshared(){
        return shared_from_this();
    }
    int32_t getid(){
        return id;
    }
};

int main(){
    std::shared_ptr<TestShared> ts(new TestShared(0xFF));
    void* tsp = ts.get();
    std::shared_ptr<TestShared> tsn = ((TestShared*)tsp)->getshared();
    std::cout << std::hex << tsn->getid();
    return 0;
}

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

Но когда я удаляю public из наследства:

#include <memory>
#include <iostream>

class TestShared : std::enable_shared_from_this<TestShared>{
private:
    int32_t id;
public:
    TestShared(int32_t id){
        this->id = id;
    }
    std::shared_ptr<TestShared> getshared(){
        return shared_from_this();
    }
    int32_t getid(){
        return id;
    }
};

int main(){
    std::shared_ptr<TestShared> ts(new TestShared(0xFF));
    void* tsp = ts.get();
    std::shared_ptr<TestShared> tsn = ((TestShared*)tsp)->getshared();
    std::cout << std::hex << tsn->getid();
    return 0;
}

Тогда это приводит к сбою.Итак, почему public имеет значение здесь и почему компилятор не выдает предупреждение / ошибку?

Ответы [ 2 ]

3 голосов
/ 10 июня 2019

Быть public важно, потому что системе shared_ptr необходим доступ к базовому классу enable_shared_from_this данного типа. И он не может этого сделать, если он public не доступен только для данного типа.

Нет предупреждения / ошибки для недоступного базового класса, потому что система не может узнать , что ваш код неверен.

Концептуально можно использовать конструктор shared_ptr, который может "включить shared_from_this", даже если enable_shared_from_this является приватным. Зачем? Учтите следующее:

class B : public enable_shared_from_this<B> {...};

class D : private B {...};

Теперь, B ожидает, что сможет сделать shared_from_this гимнастику. Но D конфиденциально унаследован от него. Так что отношение D к B (и, следовательно, к B::shared_from_this) является частным. Может быть, D использует B таким образом, что он не вызывает использование shared_from_this.

Так что, если D не полагается на B::shared_from_this, если B - это просто деталь реализации D, то почему возникает ошибка, если кто-то вставляет D в shared_ptr?

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

3 голосов
/ 10 июня 2019

Согласно https://en.cppreference.com/w/cpp/memory/enable_shared_from_this

Обычная реализация для enable_shared_from_this заключается в том, чтобы удерживать слабую ссылку (например, std::weak_ptr) на this.Конструкторы std::shared_ptr обнаруживают наличие однозначной и доступной (начиная с C ++ 17) базы enable_shared_from_this и присваивают вновь созданный std::shared_ptr внутренне хранимой слабой ссылке, если она еще не принадлежит активному std::shared_ptr (начиная с C ++ 17).

Если наследование является общедоступным, то при инициализации ts в базовом подобъекте enable_shared_from_this он "записывает", что является владельцем TestShared объект.Когда позднее вызывается getshared, выполняется обращение к базовому подобъекту, и создается новый объект shared_ptr, который разделяет владение с ts.

Если наследование не является общедоступным, тогда, когда tsинициализированный, он не «знает», что существует подобъект enable_shared_from_this, в который он должен писать.Таким образом, когда вызывается getshared, подобъект enable_shared_from_this не содержит никакой информации о том, кто в данный момент владеет объектом.В C ++ 17 это приводит к исключению;до C ++ 17 результат не определен.

...