Custom shared_ptr

Jun 1, 2013 at 2:41am
I'm trying to write my own version of shared_ptr and I just don't get what this error is about.

Shared_ptr header.
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
66
67
68
#ifndef SHARED_PTR_H_INCLUDED
#define SHARED_PTR_H_INCLUDED

#include <iostream>
#include "Make_shared.h"

template <typename A> class Make_shared;

template <typename T>
class Share
{
template <typename A> friend class Make_shared;
template <typename A>
friend std::ostream& operator<<(std::ostream&, const Share<A>&);

public:
    //Constructors
    Share();
    Share(T *p);

    //Overloaded operators
    T &operator*();
    void operator()(T *p);
    Share &operator=(Make_shared<T>&);

private:
    T *pointer;
};

//Constructors
template <typename T>
Share<T>::Share(): pointer(nullptr) { }

template <typename T>
Share<T>::Share(T *p)
{
    pointer = new T(*p);
}

//Non-member overloaded operators
template <typename T>
std::ostream& operator<<(std::ostream &os, const Share<T> &obj)
{
    os << obj.pointer;
}

//Member overloaded operators
template <typename T>
T &Share<T>::operator*()
{
    return *pointer;
}

template <typename T>
void Share<T>::operator()(T *p)
{
    pointer = new T(*p);
}

template <typename T>
Share<T> &Share<T>::operator=(Make_shared<T> &rhs)
{
    *pointer = *rhs.new_data;
    pointer = rhs.new_data;
    return this;
}

#endif // SHARED_PTR_H_INCLUDED 


Make_shared header

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
#ifndef MAKE_SHARED_H_INCLUDED
#define MAKE_SHARED_H_INCLUDED

#include "Shared_ptr.h"

template <typename A> class Share;

template <typename T>
class Make_shared
{
template <typename A> class Share;
public:
    //constructor
    Make_shared() = delete;
    Make_shared(T);

private:
    T *new_data;
};

template <typename T>
Make_shared<T>::Make_shared(T n)
{
    new_data = new T(n);
}

#endif // MAKE_SHARED_H_INCLUDED 


main.cpp
1
2
3
4
5
6
7
8
9
10
11
#include<iostream>

#include "Shared_ptr.h"
#include "Make_shared.h"

using namespace std;

int main()
{
    Share<int> p = Make_shared<int>(50);
}


Error: conversion from 'Make_shared<int> to non-scalar type 'Share<int>' requested.

Can anyone shed some light on this please?
Last edited on Jun 1, 2013 at 2:50am
Jun 1, 2013 at 6:30am
You should either make Make_shared into a function returning rvalue reference to Share object and create a move constructor to Share

or you can create constructor of Share which takes Make_shared object as argument (less intuitive solution)

Yor problem lies in fact that line 10 in main.cpp does not call an assigment operator. It tries to convert Make_shared to Share.
Jun 2, 2013 at 7:04am
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#ifndef SHARED_PTR_H_INCLUDED
#define SHARED_PTR_H_INCLUDED

#include <iostream>

template <typename T> class Share;

template <typename T>
Share<T>&& Make_shared(const T n)
{
    return Share<T>(new T(n));
}

template <typename T>
class Share
{
template <typename A>
friend std::ostream& operator<<(std::ostream&, const Share<A>&);

public:
    //Constructors
    Share();
    Share(T*);
    Share(Share &&) noexcept;

    //Copy Constructors
    Share(const Share&);
    Share& operator=(Share &&);

    //Destructor
    ~Share()
    {
        if (--*cnt == 0)
        {
            delete pointer;
            delete cnt;
        }
    }

    //Overloaded operators
    T &operator*();

private:
    T *pointer;
    size_t *cnt;
};

//Constructors
template <typename T>
Share<T>::Share(): pointer(nullptr), cnt(new size_t(1)) { }

template <typename T>
Share<T>::Share(T *p): pointer(nullptr), cnt(new size_t(1))
{
    pointer = new T(*p);
}

template <typename T>
Share<T>::Share(Share<T> &&rhs) noexcept : pointer(nullptr), cnt(new size_t(1))
{
    pointer = new T (*rhs.pointer);
    *cnt = *rhs.cnt;
}

//Copy constructors
template <typename T>
Share<T>::Share(const Share<T> &rhs):
    pointer(rhs.pointer), cnt(rhs.cnt) { ++*cnt; }

template <typename T>
Share<T> &Share<T>::operator=(Share<T> &&rhs)
{
    ++*rhs.cnt;
    pointer = rhs.pointer;
    cnt = rhs.cnt;

    return *this;
}


//Non-member overloaded operators
template <typename T>
std::ostream& operator<<(std::ostream &os, const Share<T> &obj)
{
    os << obj.pointer;
    return os;
}

//Member overloaded operators
template <typename T>
T &Share<T>::operator*()
{
    return *pointer;
}

#endif // SHARED_PTR_H_INCLUDED 


Not sure how to continue from here. I can't steal the data from Make_shared it just gets immediately destroyed.
Jun 2, 2013 at 7:17am
1
2
3
4
5
template <typename T>
Share<T>&& Make_shared(const T n)
{
    return std::move(Share<T>(new T(n)));
}

After code review I want to say that your Shared_Ptr is wrong. It would create many copies of the same object but will delete only last one. Also move constructor is inefficient
Last edited on Jun 2, 2013 at 7:26am
Jun 2, 2013 at 10:06am
Could you please elaborate how it would create many copies but just delete the last one?
Jun 2, 2013 at 11:09am
pointer = new T(*p);This will crete new copy of variable stored in p. And only last Shared_Ptr in group will actually delete his own copy. So there:
a) memory leak
b) Different Shared_Ptr will always point to different objects. You cannot have two shared pointer pointing to same object, which defats its purpose.
Jun 2, 2013 at 12:02pm
That's for a constructor that takes built-in pointers not other shared_ptr. I just copied what I could observe that the std::shared_ptr could do.

The other new in the move constructor I removed it and replaced with this

1
2
3
4
5
6
7
8
9
template <typename T>
Share<T>::Share(Share<T> &&rhs) noexcept: pointer(nullptr), cnt(new size_t(1))
{
    pointer = rhs.pointer;
    cnt = rhs.cnt;

    rhs.pointer = nullptr;
    *rhs.cnt = 0;
}


Not sure if the last two bits is necessary though.

Aren't shared_ptr suppose to share the object? I mean if they are a copy of each other.

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
    shared_ptr<int> p1 = make_shared<int>(4);
    shared_ptr<int> p2 = p1;
    shared_ptr<int> p3;

    p3 = p2;

    cout << p1 << endl;
    cout << p2 << endl;
    cout << p3 << endl;
}


They all point to the same address, doesn't that mean they are all pointing to the same object?
Jun 2, 2013 at 12:10pm
Ok, I was looking into move constructor. It is better now.
Some other promlems with move semantic:
1
2
3
4
5
{
    shared_ptr<int>* p1;
    *p1 =  make_shared<int>(4);
    shared_ptr<int> p2 = std::move(*p1);
}//Object stored in p2 and *p1 is not destroyed here. 
Move assumes that moved-from pointer will lose ownership of object and will not be counted in reference count.
Last edited on Jun 2, 2013 at 12:10pm
Jun 2, 2013 at 12:32pm
I couldn't quite figure out how to deal with the move thing and returning by rvalue with the Make_shared. During the return from Make_shared the data get's destroyed and nothing gets passed. What you just said sounds like it isn't quite the right solution. Then again I may be misunderstanding.

Looking around several documentations it says that Make_shared returns a shared_ptr<T> type. So I just settle with this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <typename T>
Share<T> Make_shared(const T n = T())
{
    Share<T> ret;
    ret.pointer = new T(n);
    ret.cnt = new size_t(1);

    return ret;
}

int main()
{
    Share<int> p = Make_shared<int>(5);
}


Would there be a memory leak here? I'm thinking that p is pointing to the object ret is pointing to and if p gets deleted, there shouldn't be a problem.
Last edited on Jun 2, 2013 at 12:33pm
Jun 2, 2013 at 12:52pm
Yes, it is fine.
Only problem with
1
2
3
4
5
int main()
{
    Share<int> p = Make_shared<int>(5);
    p = Make_shared<int>(10);
}
first in would not be destroyed. You should handle this situation. To test such situations I recommend to make class which will output each construction and destruction.


And provide non-move constructors and assigment operator too!
Jun 2, 2013 at 1:01pm
Okay I'll add them in. The assignment operator is already redefined to handled that problem, unfortunately I copied it from the book. I also have std::couts all over the place to find out the construction and destruction, but I should get into a habit of utilizing class for that like you said, or functs.

Thanks for the help =).
Topic archived. No new replies allowed.