0

I hope to prevent the users from creating new instance through the constructor, so I mark the constructor as a private method.

What's more, I need to provide a method to return an type which is used to automatically manage the life of the instance. I don't hope the user to use the instance of Foo class directly, I hope they always use std::shared_ptr<Foo> instead.

#include <iostream>
#include <memory>
 
class Foo : public std::enable_shared_from_this<Foo> {
private:     //the user should not construct an instance through the constructor below.                    
    Foo(int num):num_(num) { std::cout << "Foo::Foo\n"; }
public:
    Foo(const Foo&) = delete;
    Foo(Foo&&) = default;
    Foo& operator=(const Foo&) = delete;
    Foo& operator=(Foo&&) = default;

public:
    ~Foo() { std::cout << "Foo::~Foo\n"; } 

    int DoSth(){std::cout << "hello world" << std::endl; return 0;}

    std::shared_ptr<Foo> getPtr() { return shared_from_this();}

    static std::shared_ptr<Foo> Create() {
        Foo* foo = new Foo(5);
        return std::shared_ptr<Foo>(foo);
    }

private:
    int num_;

};

int main()
{
    auto sp = Foo::Create();
    sp->DoSth();

    Foo& foo = *sp.get();
    auto sp1 = foo.getPtr();

    Foo foo1(std::move(foo));

    std::cout << sp.use_count() << std::endl;
}

I am not sure whether or not I should mark the move constructor & move assignment operator as deleted.

What I am worried about is that there is a thread safety issue if one thread is invoking move constructor while the other thread is calling some member function at the same time. How do you think about it?

John
  • 2,963
  • 11
  • 33
  • 1
    You believe that a class can be made thread-safe simply be getting rid of move semantics? No, it's not that easy... – Sam Varshavchik Jun 11 '22 at 02:02
  • You believe there is a downside to allowing move construction. Before analyzing the validity of that, ask yourself: What benefit is there to allowing your object to be moved (from shared pointer ownership)? – JaMiT Jun 11 '22 at 02:10
  • If you have a function being called on an object controlled by a `shared_ptr` at the same time as it is being destroyed, you've got bigger problems because in order for the destructor to fire, there must be no remaining `shared_ptr`s to the object. So what's the function being invoked upon? – user4581301 Jun 11 '22 at 02:19
  • @JaMiT To be honest, I don't have any suitable\meaningful reason indeed. I just know it's ***rarely seen*** that copy constructor, copy assignment operator, move constructor and move assignment operator are ***all marked as deleted***. – John Jun 11 '22 at 02:23
  • I'm not sure what the goal is with the class (and I'm not a big fan of shared ownership) but if you'd like to make the copy and move semantics thread safe you could set it up [like this](https://stackoverflow.com/a/72141682/7582247). Perhaps you can grab some ideas from that. – Ted Lyngmo Jun 11 '22 at 02:36
  • @John *"I just know it's* **rarely seen** *that copy constructor, copy assignment operator, move constructor and move assignment operator are* **all marked as deleted**." -- your knowledge is incomplete. It might be the *you* have rarely seen this, but that does not mean everyone shares your experience. – JaMiT Jun 11 '22 at 02:52
  • @user4581301 Sorry for my poor English. Though I carefully read your comment again and again, I afraid that I still don't fully get your idea, the most confusing part for me is "a function being ***called on*** an object". If there is something wrong with my current understanding, Could you please explain that in simpler words? Here is my current understanding for your comment: for a function whose parameter's type is `std::shared_ptr`, there is not any problem if the said function is passed with a `shared_ptr`, the instance would not be destroyed until the function finishes its work. – John Jun 11 '22 at 03:29
  • I mean you don't have to worry about the object managed by `sp` vanishing part way through `sp->DoSth()`. because `sp` can't go out of scope or be tampered with until after the function has returned without significantly larger problems with the program. You can of course have the program shoot itself in the head with `Foo& foo = *sp.get();` and then letting `sp` go out of scope, but you can't protect people who willfully go out of their way to find trouble. – user4581301 Jun 11 '22 at 04:03
  • @John this post is an exact duplicate of your [previous question](https://stackoverflow.com/q/72581029/65863). If you get a question closed, DON'T just repost it, as it is very likely to get closed again. Instead, edit the closed question and fix it as requested so it can then be reopened. – Remy Lebeau Jun 11 '22 at 18:59

1 Answers1

2

I think your question answers itself.

I don't hope the user to use the instance of Foo class directly, I hope they always use std::shared_ptr<Foo> instead.

followed by

Foo foo1(std::move(foo));

demonstrating the ability to use an instance of Foo directly, not managed by a shared pointer. Defining the move constructor as deleted prevents this thing you want users to avoid.

JaMiT
  • 14,422
  • 4
  • 15
  • 31