how to pass object to a class which stores it in a container as unique_pointer

1
2
3
4
5
6
7
class A (abstract)
class B : A
class C
{
 void add ( A(&*?) a )
 std::vector<std::unique_ptr<A>> data; //unique_ptr<A> because A is abstract and therefore vector<A> isn't possible
}


upper situation. What is the best way to pass add an object of class B to C?
with C::add(A* a){ vector.push_back( unique_ptr<A>(a) ); }
and
1
2
3
4
5
int main()
{
 C c;
 c.add( new B() );
}

This works, but i don't think it's very nice, because you could delete the pointer in main. What happens then with the unique_ptr?
I could probably make C::add( std::unique_ptr<A> u_p ); but maybe it can be avoided that the "user" (in main() ) has to create the unique_ptr himself.

Do you have a recommendation, what is a nice way to solve this?
Your main code is correct.

The reason you "can delete the pointer in main" is because the pointer is in the publicly accessible section of class C

To give C full control, make it private
C::data is private, I just left it out in the example. But i can still write in main:
1
2
3
4
5
6
7
int main()
{
 A*a=new B();
 C c;
 c.add( a );
 delete a;
}


That's not so good I'd suppose...
OK, but if you use

 
c.add(new B());


then you cannot get to the pointer in main.
yes of course, but this is the "user" part and you'll never know what the user does. So Is there a way to be save?
OK, I understand what you are getting at.

I don't think there is a way to guarantee the user doesn't create a pointer in this way.

edit

According to my reading, that user code should not compile

http://www.drdobbs.com/cpp/c11-uniqueptr/240002708
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <vector>
#include <memory>
class A
{
virtual void a()=0;
};
class B : public A
{
virtual void a(){};
};
class C
{
 public:
 void add ( A* a ) { data.emplace_back( a ); };
 private:
 std::vector<std::unique_ptr<A>> data;
};

int main()
{
 B* a=new B();
 C c;
 c.add( a );
 delete a;
}


this code compiles fine and gives an error when running the programm.

But according to your link, best would be to use unique_ptr<A> as argument, I think.
Last edited on
You could do something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <vector>
#include <memory>

struct A
{
    virtual void a()=0;
    virtual A* clone() const = 0 ;
};

struct B : public A
{
    void a(){};
    B* clone() const { return new B(*this) ; }
};

struct C
{
    void add ( const A& obj ) { data.emplace_back(obj.clone()); }

private:
    std::vector<std::unique_ptr<A>> data;
};

int main()
{
    C c ;
    c.add(B()) ;
}
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <memory>
#include <vector>
#include <utility>
#include <cassert>

template < typename T > struct A
{
    void add( std::unique_ptr<T>&& p  ) 
    { seq.push_back( std::forward< std::unique_ptr<T> >(p) ) ; }

    // invariant: p is the only owning pointer to an object
    // which can be deleted by std::default_delete<T>()
    void add( T* p ) { seq.emplace_back(p) ; }

    std::vector< std::unique_ptr<T> > seq ;

    //////////////////////////////// for exposition ////////////////////////////////////

    void add( const std::unique_ptr<T>& p  ) = delete ; // in effect

    A() = default ;
    ~A() = default ;

    A( const A& ) = delete ;
    A<T>& operator= ( const A& ) = delete ;

    A( A&& that ) noexcept
        : seq( std::forward< std::vector< std::unique_ptr<T> > >(that.seq ) )
        { /* that.seq.clear() ; */ }

    A<T>& operator= ( A&& that ) noexcept
    {
        if( this != std::addressof(that) )
        {
            seq = std::forward< std::vector< std::unique_ptr<T> > >(that.seq ) ;
            // that.seq.clear() ;
        }
        return *this ;
    }

    //////////////////////////////// for exposition ////////////////////////////////////
};

int main()
{
    struct base { virtual ~base() {} } ;
    A<base> a ;

    struct derived : base {} ;

    a.add( std::unique_ptr<derived>( new derived ) ) ;

    a.add( new derived ) ;

    std::unique_ptr<derived> p( new derived ) ;
    a.add( std::move(p) ) ;
    assert( p == nullptr ) ;

    std::unique_ptr<derived> q( new derived ) ;
    a.add( q.release() ) ;
    assert( q == nullptr ) ;

    A<base> copy_of_a = std::move(a) ;
    a = std::move( copy_of_a ) ;
}
Hey, thanks. That one seems nice!
A wee bit more robust: void add( T*&& p ) { seq.emplace_back(p) ; p = nullptr ; }
Topic archived. No new replies allowed.