Ссылаясь на "std :: unique_ptr", который вам не принадлежит (использовать необработанный указатель?) - PullRequest
1 голос
/ 06 марта 2019

Как правило, если вы используете std::shared_ptr для указания на объект и хотите создать еще один указатель на этот объект, который не имеет общего владения, вы создадите std::weak_ptr.

// Create a shared pointer to own the object
std::shared_ptr<int> p = std::make_shared<int>(42);

// Create a weak pointer (that does not own the object)
std::weak_ptr<int> q(p);

// Use the weak pointer some time later
if (std::shared_ptr ptr = q.lock()) {
  // use *ptr
}

Мой вопрос: как вы это делаете, когда дело доходит до std::unique_ptr?

Использование уникального указателя гарантирует, что текущий ресурс принадлежит исключительно самому std::unique_ptr. Но что, если я хочу создать указатель на тот же ресурс, которому этот ресурс не принадлежит? Я не могу использовать std::weak_ptr, потому что слабые указатели предназначены для работы со счетчиком ссылок из std::shared_ptr. Буду ли я просто использовать необработанный указатель здесь? Или есть лучшая альтернатива?

// Create a unique pointer to own the object
std::unique_ptr<int> p = std::make_unique<int>(42);

// Create a non-owning pointer to the same object
// Is this really the best way?
int* q = p.get();

// Use the pointer some time later
if (q != nullptr) {

  // Imagine this may be multithreaded...
  // what happens if p.reset() is called by another thread while the current thread is RIGHT HERE.

  // use *q
}

Единственный способ, которым я могу думать о создании не принадлежащего указателю на объект, принадлежащий std::unique_ptr, - это использовать необработанный указатель, но, как вы можете видеть из приведенного выше кода, это может вызвать проблемы в многопоточных приложениях. Есть ли лучший способ достичь той же цели?

Ответы [ 2 ]

6 голосов
/ 06 марта 2019

Зависит от того, может ли дополнительный указатель пережить unique_ptr.

  • Если не владеющий указатель абсолютно не может пережить , то unique_ptr: сделать его простым указателем. Тот, кто получает указатель такого типа, может предположить, что он действителен, но не может предположить, что ему принадлежит память, на которую он указывает.
  • Если не владеющий указатель мог бы пережить , то unique_ptr: Ваш исходный указатель на самом деле не «уникален»; и вы должны заменить unique_ptr на shared_ptr и передать weak_ptr в качестве не владеющей копии.

Я только что заметил, что этот ответ в основном является кратким изложением ответа Xirema . Пожалуйста, подтвердите / примите этот ответ!

3 голосов
/ 06 марта 2019

Основываясь на вашем последнем примере, это сценарий, в котором должны использоваться std::shared_ptr и std::weak_ptr.

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

class A {
    std::unique_ptr<int> ptr = std::make_unique<int>(5);
public:
    int* get_ptr() const{return ptr.get();}
};

class B {
    A a;
public:
    void do_something() {
        //int * ptr = a.get_ptr();//Valid, but not advised
        int & ref = *a.get_ptr();//Preferred
        ref++;
    }
};

Если вы можете сделать эту гарантию, вы должны использовать std::unique_ptr и необработанный указатель для представления этого объекта.Это идеологически правильно.

Если, однако, вы не можете гарантировать срок службы в то время, когда вам нужно манипулировать объектом, тогда ссылки должны предоставляться std::weak_ptr, которые используются для приобретения права собственности (даже еслитолько временно!) вносить изменения.

class A {
    std::shared_ptr<int> ptr = std::make_shared<int>(5);
public:
    std::weak_ptr<int> get_ptr() const {
        return ptr;//Implicitly converts
    }
    void destroy() {
        ptr.reset();
    }
};

class B {
    std::weak_ptr<int> ptr;
public:
    B(std::weak_ptr<int> ptr) : ptr(ptr) {}
    void do_something() {
        if(auto owned_ptr = ptr.lock()) {//owned_ptr will be deduced to be of type std::shared_ptr<int>
            *owned_ptr++; //Guaranteed to only execute if the pointer is still valid
        }
    }
};

int main() {
    A a;
    B b(a.get_ptr());
    if(get_random_value() > 10)
        a.destroy();
    b.do_something();
}
...