Destructor without constructor

I'm trying to create a little brother of shared_ptr. Until now I failed miserably. Following code:
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include <iostream>
#include <map>
#include <assert.h>


namespace test
{
  namespace memory { namespace manager { using reference_count_type = unsigned short; } }
  struct x
  {
    std::map<void *, memory::manager::reference_count_type> m_Map;
    memory::manager::reference_count_type *alloc_reference_count(void *p)
    {
      return &(m_Map[p]);
    }
  };
  x g_Memory;

  template<typename object_type>
  class shared_ptr
  {
    memory::manager::reference_count_type *m_ReferenceCount;
    object_type *m_Type;

  public:
	  template<class other_type>
    shared_ptr(const shared_ptr<other_type> &p) : m_ReferenceCount(nullptr), m_Type(nullptr)
    {
      std::cout << "shared_ptr(" << this << ") <- " << &p << "\n";
      assign(p);
    }
//	  template<class other_type>
//    shared_ptr(shared_ptr<other_type> &&p) : m_ReferenceCount(nullptr), m_Type(nullptr)
//    {
//      std::cout << "shared_ptr(" << this << ") && " << &p << "\n";
//      assign(p);
//      p.reset();
//    }
    explicit shared_ptr(object_type *const p = nullptr) : m_ReferenceCount(nullptr), m_Type(nullptr)
    {
      std::cout << "shared_ptr(" << this << ") <- p(" << p << ")\n";
      if(p)
        reset(p);
    }
    ~shared_ptr()
    {
      std::cout << "~shared_ptr(" << this << ")\n";
      reset();
    }

  public:
    shared_ptr &operator=(const shared_ptr<object_type> &p)
    {
      std::cout << this << " = " << &p << "\n";
      assign(p);

      return *this;
    }
//    shared_ptr &operator=(shared_ptr<object_type> &&p)
//    {
//      std::cout << this << " && " << &p << "\n";
//      assign(p);
//      p.reset();
//
//      return *this;
//    }

    void reset(object_type *const p)
    {
      m_Type = p;
      if(m_Type)
      {
        m_ReferenceCount = g_Memory.alloc_reference_count(m_Type);
        assert(m_ReferenceCount);

        std::cout << "++ " << this << "\n";
        ++(*m_ReferenceCount);
      }
    }
    void reset()
    {
      if(m_Type and m_ReferenceCount)
      {
        assert((*m_ReferenceCount) > 0);
        std::cout << "-- " << this << "\n";
        --(*m_ReferenceCount);
        if((*m_ReferenceCount) > 0) { } else
          delete m_Type;
      }
      m_Type = nullptr;
      m_ReferenceCount = nullptr;
    }

    object_type *get() const
    {
      return (m_ReferenceCount and m_Type) ? m_Type : nullptr;
    }
    memory::manager::reference_count_type *get_reference_count() const
    {
      return m_ReferenceCount;
    }

  private:
	  template<class other_type>//, class = typename std::enable_if<std::is_convertible<other_type *, object_type *>::value, void>::type>
    void assign(const shared_ptr<other_type> &p)
    {
      m_ReferenceCount = p.get_reference_count();
      m_Type = p.get();

      if(m_Type and m_ReferenceCount)
      {
        std::cout << "++ " << this << "\n";
        ++(*m_ReferenceCount);
      }
    }
  };
}

class A { int i = 0; };

struct B
{
  test::shared_ptr<A> m_a;

  test::shared_ptr<A> create_a()
  {
    test::shared_ptr<A> result(new A);
    m_a = result;
//    return result;
    return m_a;
  }
};

int main(int argc, char * argv[])
{
  B t0;
  test::shared_ptr<A> t = t0.create_a();

  return 0;
}

produces this output:
shared_ptr(0x22ff08) <- p(0)
shared_ptr(0x22fed8) <- p(0x861510)
++ 0x22fed8
0x22ff08 = 0x22fed8
++ 0x22ff08
~shared_ptr(0x22fed8)
-- 0x22fed8
~shared_ptr(0x22ff00)
-- 0x22ff00
~shared_ptr(0x22ff08)
Assertion failed: (*m_ReferenceCount) > 0, file D:\temp\temp\test2\main.cpp, line 84
Notice 0x22ff00. No constructor that I provided was called for this object. How is this even possible?

Further note: Line 32/59 is the move constructor/operator. If I include them GCC fails to compile with the following error:
D:\temp\temp\test2\main.cpp|130|error: use of deleted function 'constexpr test::shared_ptr<A>::shared_ptr(const test::shared_ptr<A>&)'|

Last edited on
The output is not the problem. It reflects exactly what happend. The problem is that the assertion failed. The reason for this is that I have 2 increments and 3 [tries] decrements.

I don't know how the call of the constructor is circumvented or what I am doing wrong so that it happens.
The implicitly declared move constructor is called. (The explicit move constructor is commented out).

1
2
3
4
5
6
7
    test::shared_ptr<A> create_a()
    {
        test::shared_ptr<A> result( new A );
        m_a = result;
        //    return result;
        return m_a; // move constructor
    }


With the move constructor uncommented: http://coliru.stacked-crooked.com/a/890fd8cb0dec7746
Last edited on
The explicit move constructor is commented out
Yes, when I include them I get the error:

D:\temp\temp\test2\main.cpp|130|error: use of deleted function 'constexpr test::shared_ptr<A>::shared_ptr(const test::shared_ptr<A>&)'


Honestly I don't know what that means.

When I include the move constructor/operator Visual C++ compiles, but the assertion remains. It looks like as if my move constructor/operator is ignored somehow.
Define the copy constructor: test::shared_ptr<A>::shared_ptr(const test::shared_ptr<A>&)

See: http://www.cplusplus.com/forum/beginner/189513/#msg918076
Line 27 does not count as the copy constructor?

A move constructor is implicitly generated while a copy constructor is not?
> Line 27 does not count as the copy constructor?

A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments
...
A non-template constructor for class X is a move constructor if its first parameter is of type X&&, const X&&, volatile X&&, or const volatile X&&, and either there are no other parameters or else all other parameters have default arguments

- IS


> A move constructor is implicitly generated while a copy constructor is not?

If any move, copy, or destructor is explicitly specified (declared, defined, =default, or =delete) by the user, no move is generated by default. If any move, copy, or destructor is explicitly specified (declared, defined, =default, or =delete) by the user, any undeclared copy operations are generated by default, but this is deprecated, so don't rely on that.
http://www.stroustrup.com/C++11FAQ.html#default
So, I'd say no move if not explicitly told. I don't want it since I want to keep the inteface as sparse as possible and for a pointer move seems not to make overly sense.

For the missing constructor I use two:
1
2
3
4
5
6
7
8
9
10
11
    template<class other_type>
    shared_ptr(const shared_ptr<other_type> &p) : m_ReferenceCount(nullptr), m_Type(nullptr)
    {
      std::cout << "shared_ptr(" << this << ") <- " << &p << "\n";
      assign(p);
    }
    shared_ptr(const shared_ptr<object_type> &p) : m_ReferenceCount(nullptr), m_Type(nullptr)
    {
      std::cout << "shared_ptr(" << this << ") <- " << &p << "\n";
      assign(p);
    }


That seems sufficient but alas I have a hard to determine crash where std::shared_ptr worked...
Topic archived. No new replies allowed.