Create shared_ptr from reference

2020-08-24 06:57发布

问题:

I'm relativly new to C++ and this seams like a noob question but I wasn't able to solve it with other resources on the internet.

I'm trying to create a shared_ptr from a reference. I have following Book class:

#include <memory>
#include "Author.hpp"

class Book
{
   public:
      void setAuthor(const Author& t_author);

   private:
      std::shared_ptr<Author> m_author;
}

And this is my Author class:

#include <memory>
class Book;

class Author
{
   public:
      void addBook(const Book& t_book);

   private:
      std::vector<std::weak_ptr<Book>> m_books;
}

I tired to implement the Book::setAuthor method like so:

void Book::setAuthor(const Author& t_author)
{
   m_author = std::shared_ptr<Author>(&t_author);
}

But if I try to compile this I get:

Invalide conversion from const Author* to Author*

Invalide conversion from sizeof to const Author

Can you please tell me what is wrong with my code? I also tried the same with the weak_ptr but this does not work either.

回答1:

Though, your error stems from the fact that the std::shared_ptr<Author> constructor in use expects Author*, but the expression &t_author results to an object of type const Author*


Another wrong thing:

void Book::setAuthor(const Author& t_author)
{
   m_author = std::shared_ptr<Author>(&t_author);
}

Imagine calling book.setAuthor(Author("Herb Sutter"));, you will have a dangling pointer because t_author will cease to exist after that function completes.


You need to copy or move the object into your std::shared_ptr instance. Use std::make_shared<T> to create your std::shared_ptr<T> objects whenever possible.

void Book::setAuthor(const Author& t_author)
{
   m_author = std::make_shared<Author>(t_author);
}

Better still:

void Book::setAuthor(Author t_author)
{
   m_author = std::make_shared<Author>(std::move(t_author));
}


回答2:

If you want to make a copy use std::make_shared:

void Book::setAuthor(const Author& t_author)
{
   m_author = std::make_shared<Author>(t_author);
}

but this is a wrong design, if you expect to keep ownership of passed objects you should pass std::shared_ptr to your function instead of const reference:

void Book::setAuthor( std::shared_ptr<Author> t_author)
{
   m_author = std::move( t_author );
}


回答3:

This is likely undefined behavior. shared_ptr indicates ownership of the object it points to. In just about every conceivable scenario, t_author refers to an existing Author that is owned by something else. There will almost surely be two locations that try to destroy the instance.

If you must create a shared_ptr to an existing instance, you can look into using enable_shared_from_this, but this only works if t_author was created with std::make_shared. And if this is the case, you might as well change your function to accept the shared_ptr directly. Alternatively, you can create a shared_ptr with a custom deleter that does nothing. But at that point, there is nothing to gain from using shared_ptr, except perhaps compatibility with some interface.