Как мне передать интеллектуальный указатель из функции вызывающему?

#c #smart-pointers

#c #интеллектуальные указатели

Вопрос:

Я пытаюсь понять концепцию интеллектуальных указателей в C . У меня есть следующий фрагмент кода (модульный тест с использованием GoogleTest):

 TEST(SHT35Sensor, ValidInstruction) {
    auto sht35 = SampleSHT35::create();
    sht35->add(22.4, 56.5);
    char writeBuffer[100] = {0};
    auto serial = std::make_unique<SampleSerial>("", writeBuffer, 0);
    auto sensor = std::make_unique<SHT35Sensor>(0x03, serial.get(), sht35, 0);
    auto actual = sensor->execute(Instruction(0, 0, Bytes("x02", 1)));
    ASSERT_TRUE(actual);
}
  

Я хочу изолировать первые пять строк теста, чтобы их можно было использовать повторно. Я думал, что этого будет достаточно (и особенно это было бы правильно), чтобы сделать это:

 std::shared_ptr<SHT35Sensor> prepare() {
    auto sht35 = SampleSHT35::create();
    sht35->add(22.4, 56.5);
    char writeBuffer[100] = {0};
    auto serial = std::make_unique<SampleSerial>("", writeBuffer, 0);
    return std::make_shared<SHT35Sensor>(0x03, serial.get(), sht35, 0);
}

TEST(SHT35Sensor, ValidInstruction) {
    auto sensor = prepare();
    auto actual = sensor->execute(Instruction(0, 0, Bytes("x02", 1)));
    ASSERT_TRUE(actual);
}
  

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

Однако второй вариант приводит к ошибке сегментации при выполнении теста, что означает, что мое понимание интеллектуальных указателей неверно.

Что я делаю не так?

Комментарии:

1. serial.get() возвращает указатель, но не отделяет его от unique_ptr, поэтому, когда подготовка заканчивается, unique_ptr удаляет экземпляр SampleSerial, а shared_ptr содержит указатель на освобожденную память. Вы можете использовать serial.release() или напрямую использовать shared_ptr

2. @marcinj: естественно, это было так просто. Не могли бы вы добавить свой комментарий к ответу?

3. FWIW, вы можете возвращать локальные функции unique_ptr , даже если их нельзя скопировать. Локальные функции будут перемещены, поскольку срок их действия истекает.

4. Вы написали класс SHT35Sensor? Вы можете изменить его так, чтобы внутри него был shared_ptr или unique_ptr .

Ответ №1:

В вашем коде serial.get() возвращает указатель, но не отсоединяет его от unique_ptr , поэтому when prepare ends — unique_ptr удаляет SampleSerial экземпляр и shared_ptr содержит указатель на освобожденную память. Вы можете использовать serial.release() или использовать напрямую shared_ptr .

Приведенный выше ответ предполагает, что SHT35Sensor это будет обрабатывать время жизни SampleSerial экземпляра. Но если это не так, тогда перейдите unique_ptr<SampleErial> к SHT35Sensor :

 return std::make_shared<SHT35Sensor>(0x03, std::move(serial), sht35, 0);
  

Ваш SHT35Sensor должен быть принят std::unique_ptr<SampleErial> в качестве второго параметра — и передать его члену класса с помощью инициализации конструктора или еще раз std::move .

Я бы предпочел второе решение, поскольку никакой пустой указатель не будет принят SHT35Sensor — и это хорошо.

Комментарии:

1. Это решение, вероятно, приведет к утечке памяти. Кто тогда будет удалять этот release общий указатель?

2. @Evg вы правы, я отвечу udpdate (но, может быть, SHT35Sensor делает это, кто знает:-))

3. Если SHT35Sensor это сделать, исходный код, скорее всего, завершится сбоем из-за двойного обнаружения.