Suppose I have a class like this:
class Node {
public:
Node(Node* parent = 0) : mParent(parent) {}
virtual ~Node() {
for(auto p : mChildren) delete p;
}
// Takes ownership
void addChild(Node* n);
// Returns object with ownership
Node* firstChild() const;
// Does not take ownership
void setParent(Node* n) { mParent = n; }
// Returns parent, does not transfer ownership
Node* parent() const { return mParent; }
private:
list<Node*> mChildren;
Node* mParent;
};
I'd now like to use smart pointers and/or rvalue references to indicate where ownership is and isn't transferred.
My first guess would be to change mChildren to contain unique_ptrs, adapting the function signatures as follows.
// Takes ownership
void addChild(unique_ptr<Node> n);
// Returns object with ownership
unique_ptr<Node>& firstChild() const;
// Does not take ownership
void setParent(Node* n) { mParent = n; }
// Returns parent, does not transfer ownership
Node* parent() const { return mParent; }
Now, this would be kind of problematic when I need to pass the result of Node::firstChild() to some function that observes it, but does not take ownership, as I'd need to explicitly call .get() on the unique_ptr, which as I understand it, is not recommended.
What is the correct and recommended way to indicate ownership using unique_ptr without having to resort to using .get() and passing around bare pointers?
At first, I would use std::vector rather than std::list to contain the children. Unless you have a strong motivation for not using it, std::vector should be the default container. If you are worried about performance, don't be, because contiguous allocation done by std::vector is likely to cause higher cache hit rate, thus speeding up access enormously with respect to std::list, which implies a scattered allocation/access pattern.
Secondly, you are correct in having a std::vector<std::unique_ptr<Node>> for holding children, since it is reasonable to assume a node to hold ownership of its child nodes. All other pointers except the one accepted by addChild(), on the other hand, should be non-owning raw pointers.
This applies to the mParent pointer and to pointers returned by Node's member functions. In fact the, firstChild() member function could even return a reference, throwing an exception if the node has no children. This way you will create no confusion whatsoever about who is owning the returned object.
Returning a unique_ptr, or a reference to a unique_ptr, is not the correct idiom: unique pointers represent ownership, and you do not want to give ownership to clients of Node.
This is how your class could look like:
#include <vector>
#include <memory>
#include <stdexcept>
class Node {
public:
Node() : mParent(nullptr) { }
void addChild(std::unique_ptr<Node>&& ptr) {
mChildren.push_back(std::move(ptr));
ptr->setParent(this);
}
Node& firstChild() const {
if (mChildren.size() == 0) { throw std::logic_error("No children"); }
else return *(mChildren[0].get());
}
Node& parent() const {
if (mParent == nullptr) { throw std::logic_error("No parent"); }
else return *mParent;
}
private:
void setParent(Node* n) {
mParent = n;
}
std::vector<std::unique_ptr<Node>> mChildren;
Node* mParent;
};
You could of course decide to return non-owning, potentially null raw pointers instead of references if you want to avoid throwing exceptions. Or you could add a pair of hasParent() and getNumOfChildren() methods to retrieve information about a Node's state. That would allow clients to perform the check if they do not want to handle exceptions.
Related
I'm trying to make a tree with unique_ptr as children. This is the class for the moment :
class Part
{
public:
vector<Part>& getChildren() const {
return *m_children;
}
void attachChild(const unique_ptr<Part>& child) {
m_children.push_back(std::move(child));
}
vector<Part>& getAtoms() const {
vector<Part> atoms;
for (const auto& child : m_children) {
if (child->hasChildren()) {
vector<Part> childChildren = child->getAtoms();
atoms.insert(atoms.end(), childChildren.begin(), childChildren.end());
} else {
atoms.push_back(child);
}
}
return atoms;
}
vector<Part>& getAbsoluteAtoms() const {
vector<Part> atoms;
for (auto child : m_children) { // Not const because I modify the child
if (child->hasChildren()) {
vector<Part> childChildren = child->getAbsoluteAtoms();
atoms.insert(atoms.end(), childChildren.begin(), childChildren.end());
} else {
child.setPosition(child->getPosition() + m_position);
atoms.push_back(child);
}
}
return atoms;
}
private:
vector<unique_ptr<Part>> m_children;
};
I have a lot of errors, because of the pointers, like this one :
D:\Programmes\Qt\Tools\mingw530_32\i686-w64-mingw32\include\c++\bits\stl_construct.h:75: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Part; _Dp = std::default_delete<Part>]'
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^
Without them, everything works well but as a child can get really huge, I need them. Can you tell me why my code isn't correct?
With all due respect, I think you need to work on the basics of C++. For me it has been impossible to deduce what you want to achieve with this code. Maybe I could help you pointing out some mistakes, as I believe that should be your first step.
Bad use of std::move
void attachChild(const unique_ptr<Part>& child) {
m_children.push_back(std::move(child));
}
Why would you want to move a const&? And if it weren't a constant reference, do you think it would be worth moving it? And why is this a unique_ptr? m_children holds elements of type unique_ptr, and you want to fill that vector with the addChild method? Can we copy unique_ptr? They wouldn't be unique anymore. All of this is very strange. Impossible to understand what your intentions are.
Return type
vector<Part>& getAtoms() const { vector<Part> atoms; /* fill atoms */ return atoms; }
Are you sure you want to return a reference to a variable which gets destructed at the end of the getAtoms() function? The returntype should be
vector<Part>
Same here:
vector<Part>& getAbsoluteAtoms() const { ... }
vector<unique_ptr<Part>> m_children needed?
vector<unique_ptr<Part>> m_children;
I really wonder why you need to store a vector of unique_ptr in a class attribute. I'm sure you'll have your reasons. But before continuing, I'd revisit the basics of C++ (copy, reference, pointer, moving semantics, ...).
The error you mentioned tells you that you use the deleted copy constructor of unique_ptr. The reason why it is deleted, is that as the name implies there must always be just one unique_ptr that owns an allocated object.
One use of this copy constructor is in the default generated copy constructor of your Part class, which is used for example whenever you insert a Part into a vector<Part>. Another use is in the for (auto child : m_children) loops.
std::unique_ptr can only be moved, that means the source does not refer to the allocated object after the move to a new unique_ptr. Since the purpose of unique_ptr is to ensure that the owned object is deleted properly in all cases and never deleted twice, it makes sense that it cannot be blindly copied.
By using std::unique_ptr, you express that the parent "owns" the children, i.e. by copying the parent, you copy the children, by deleting the parent you delete the children.
This works nicely for trees, because every node has at most one parent, and you can probably take care of the roots.
If Part was representing a directed acyclic graph, you could use shared_ptr.
This is how I would write Part with hierarchical ownership:
#include <vector>
#include <memory>
class Part
{
public:
// or whatever constructor is useful
Part() = default;
~Part() = default;
// copy constructor needed because we cannot copy unique_ptr
Part(const Part &other) {
// cannot use just auto here, as that would try to copy the unique_ptrs
for(const auto &child_ptr : other.m_children) {
// we recursively copy all children
m_children.emplace_back(std::make_unique<Part>(*child_ptr));
}
}
// move constructor does what we expect
Part(Part && other) = default;
// since we need an explicit copy constructor it is
// good practice to mention also the assignment operators
// see also the "rule of five"
// copy assignment should be similar to the copy constructor, but first clear the children
Part &operator=(const Part &other) {
m_children.clear();
for(const auto &child_ptr : other.m_children) {
// we recursively copy all children
m_children.emplace_back(std::make_unique<Part>(*child_ptr));
}
return *this;
}
// moving should work as expected, we get all the children of other
Part &operator=(Part &&other) = default;
const std::vector<std::unique_ptr<Part>>& getChildren() const {
return m_children;
}
// we take a unique_ptr by value because it (the pointer) is small
void attachChild(std::unique_ptr<Part> child) {
m_children.push_back(std::move(child));
}
bool hasChildren() const {
return !m_children.empty();
}
private:
std::vector<std::unique_ptr<Part>> m_children;
};
// note how we return the vector by value,
// to avoid passing a stale reference
// the user will get a completely new vector
std::vector<Part> makeLotsOfParts(const Part &part) {
std::vector<Part> parts;
for(int i = 0; i < 10; ++i) {
// now we can copy parts!
parts.push_back(part);
}
// here the compiler will either apply the return value optimization
// or move the vector cheaply into the return value
return parts;
}
std::unique_ptr<Part> assemblePart() {
std::unique_ptr<Part> parent = std::make_unique<Part>();
std::unique_ptr<Part> child1 = std::make_unique<Part>();
// we do not need child1 any more, so we move from it
parent->attachChild(std::move(child1));
std::unique_ptr<Part> child2 = std::make_unique<Part>();
parent->attachChild(std::move(child2));
// again we can rely on RVO or move
return parent;
}
I'm in the situation where I'm not sure which type of smart pointer to use because I'm not sure of all the use cases for my class. I could just use shared pointers but I'm not fond of the idea of passing shared pointers everywhere in my code when I don't necessarily need shared ownership. This article by Herb Sutter says that when in doubt, use unique_ptr and convert to shared_ptr when you have to. This is what I'd like to do but I'm unclear as to how this is supposed to be done, consider this example:
class Example
{
public:
Example(): _ptr(std::make_unique<Node>()) {}
std::unique_ptr<Node>& getPtr()
{
return _ptr;
}
private:
// I am unsure if I will eventually need shared ownership or not
std::unique_ptr<Node> _ptr;
};
Example* example = new Example();
// Some function somewhere
void f()
{
// I've decided I need shared ownership, converting
std::shared_ptr<Node> ptr(std::move(example->getPtr()));
// Oops, example is no longer valid...
}
If someone has a better idea of how to deal with situations like this I'd be glad to hear it.
I think you are asking a kind of optimization question. You want Example to use unique_ptr because it has simpler and more efficient semantics (paraphrasing your referenced article). But, when the need arises, you wish to allow the pointer to be converted to shared_ptr.
Example should simply provide an interface for that, and itself needs to convert from unique_ptr to shared_ptr, when its user invokes that interface. You could use state pattern to capture whether the instance is in unique_ptr mode or shared_ptr mode.
class Example
{
struct StateUnique;
struct StateShared;
struct State {
State (std::unique_ptr<State> &s) : _state(s) {}
virtual ~State () = default;
virtual Node & getPtr () = 0;
virtual std::shared_ptr<Node> & getShared() = 0;
std::unique_ptr<State> &_state;
};
struct StateUnique : State {
StateUnique (std::unique_ptr<State> &s)
: State(s), _ptr(std::make_unique<Node>()) {}
Node & getPtr () { return *_ptr.get(); }
std::shared_ptr<Node> & getShared() {
_state = std::make_unique<StateShared>(*this);
return _state->getShared();
}
std::unique_ptr<Node> _ptr;
};
struct StateShared : State {
StateShared (StateUnique &u)
: State(u._state), _ptr(std::move(u._ptr)) {}
Node & getPtr () { return *_ptr.get(); }
std::shared_ptr<Node> & getShared() { return _ptr; }
std::shared_ptr<Node> _ptr;
};
public:
Example(): _state(std::make_unique<StateUnique>(_state)) {}
Node & getNode() { return _state->getPtr(); }
std::shared_ptr<Node> & getShared() { return _state->getShared(); }
private:
std::unique_ptr<State> _state;
};
If the state machine looks scary (which it should, since it is over-engineered), then you can just maintain two pointers in the Example, and your methods which need to test which one it needs to use.
class Example
{
public:
Example(): _u_node(std::make_unique<Node>()) {}
Node & getNode() { return _u_node ? *_u_node.get() : *_s_node.get(); }
std::shared_ptr<Node> & getShared() {
if (_u_node) _s_node = std::move(_u_node);
return _s_node;
}
private:
std::unique_ptr<Node> _u_node;
std::shared_ptr<Node> _s_node;
};
If you have a class, say UniqueResourceHolder, it may be implemented as follows:
class UniqueResourceHolder{
public:
std::unique_ptr<Widget> ptr;
};
Later, if you want to get said resource from UniqueResourceHolder and put it in a SharedResourceHolder that looks as follows:
class SharedResourceHolder{
public:
std::shared_ptr<Widget> ptr;
};
the code to do so may look like this:
{
UniqueResourceHolder urh;
SharedResourceHolder srh;
//initialization of urh.ptr
srh.ptr = std::shared_ptr<Widget>(urh.release());
}
UniqueResourceHolder will no longer have the Widget it once did, so any attempt to use urh to get at the widget will be invalid (unless you repopulate it with a new one), but srh now will have that widget and will be willing to share. Anyway, that's how I understand the answer to the question in the link you provided. My own two cents is that it is also a trivial matter to replace occurrences of std::unique_ptr with std::shared_ptr; any business logic your program followed to ensure the uniqueness of the resource is still valid, but to go from business logic where you took advantage of the shared-nature of std::shared_ptr would take some time and focus to rework to a unique mindset.
Shared pointers are for shared resources. If you want to share resources I would declare everything shared from the beginning and not go through special conversions.
If you want to use unique pointer you are saying that there "must" only exist one pointer for the the resource you have allocated. That does not limit your way in using these objects. Why not rather use unique pointer and "move" your object through the classes instances and functions... If you have to convert to your pointer why not move it as unique...
Regarding performance: It is said, that compared to a primite pointer, the shared pointer is about 6 times slower. The unique pointer is about twice as slow. Of course I'm talking about accesses. If you really have performance critical portions of the code you can still get primitive pointers of these classes and operate. It does not even have to be a pointer, there are still the C++ References.
I think you should decide whether your object will be "shared" or "moved" and go on with the paradigm, instead of converting and changing all the time.
std::unique_ptr<Node>& getPtr()
{
return _ptr;
}
No, no. You are passing multiple references if you call that from different places. Use the "move" operator instead (With declarations like type& operator=(type&& other) for example) and pass your unique pointer.
I need to have access to an object which is implemented in a different concrete class. So I decided to use std::shared_ptr. I would like to know whether usage of std::shared_ptr is appropriate here, if not please suggest whether I should go with std::weak_ptr. So far I have been using raw pointers but now I decided to use smart pointers in my project, But I'm unable to decide which one should I use here. The following code snippet is analogous to what I'm trying to do in my project.
#include <iostream>
#include <memory>
class data
{
public:
data()
{
std::cout<<"\n data constructor Called"<<std::endl;
}
~data()
{
std::cout<<"\n data destructor Called"<<std::endl;
}
int GetData()
{
return val;
}
void SetData(int & val)
{
this->val = val;
}
private:
int val;
};
class sample
{
public:
sample();
~sample();
void GetShared(std::shared_ptr<data> & arg);
std::shared_ptr<data> sPtr;
};
sample::sample()
{
sPtr = std::make_shared<data>();
}
sample::~sample()
{
}
void sample::GetShared(std::shared_ptr<data> & arg)
{
arg = sPtr;
}
int main()
{
int val = 40;
sample obj;
{
std::shared_ptr<data> temp1;
obj.GetShared(temp1);
temp1->SetData(val);
std::cout<<"\n Data : "<<temp1->GetData()<<std::endl;
} // Just to understand whether pointer gets deleted if temp1 goes out of scope.
{
std::shared_ptr<data> temp2;
obj.GetShared(temp2);
val = 20;
temp2->SetData(val);
std::cout<<"\n Data : "<<temp2->GetData()<<std::endl;
}
return 0;
}
You would use shared_pointer to share ownership of some resource, when you don't have a clear owner of that resource.
Here it would be useful if you don't know if obj goes out of scope before temp1 and temp2 is done with the data. However, in this example it is clear that obj and the data object it holds will outlive the user. In that case you could just as well return a normal pointer, or perhaps a reference to the data.
Using a shared_pointer (or a weak_pointer) doesn't buy you anything, except added complexity.
In your code
sample obj;
{
std::shared_ptr<data> temp1;
obj.GetShared(temp1);
temp1->SetData(val);
std::cout<<"\n Data : "<<temp1->GetData()<<std::endl;
} // Just to understand whether pointer gets deleted if temp1 goes out of scope.
The data will not get deleted, because it is pointing to something that is held by obj, which is still alive.
I think the answer should depend on whether a data object can live after its corresponding sample object died:
If yes, then it should return a std::shared_ptr.
If not, then it should return a std::weak_ptr.
The difference between shared_ptr and weak_ptr is that weak_ptr does not increase the ref count on the object and does prevent the object from being deleted.
This has its pros and cons, if you perform async operations after you obtain the pointer and are unsure whether the object that provided the data has been destroyed then you can use weak_ptr and check if you still have access to the object.
If not then keep it simple and use shared_ptr.
You have two questions you need to ask yourself.
1) Will the calling object that gets the pointer from sample outlive the sample object?
If not then sample should use a std::unique_ptr and return a raw pointer or a reference.
2) If the calling object does outlive the sample object, does it mind if the data object gets destroyed before it does?
If not then return a std::weak_ptr that can be used to test if the data object is still alive before using it.
Otherwise return a std::shared_ptr to guarantee the data object lives as long as the calling object.
Summary:
If the calling object will not outlive the sample object:
class sample
{
std::unique_ptr<data> ptr; // UNIQUE (not shared)
public:
data* GetData() { return ptr.get(); }
};
If the calling object may outlive the sample object but doesn't care if the data object lives as long as it does:
class sample
{
std::shared_ptr<data> ptr;
public:
std::weak_ptr<data> GetData() { return ptr; }
};
If the calling object may outlive the sample object and needs to data object to keep living too:
class sample
{
std::shared_ptr<data> ptr;
public:
std::shared_ptr<data> GetData() { return ptr; }
};
In book C++ Primer 13.5.1, it implement a Smart Pointer Class using a Use-Count Class. Their implementation is as follows:
Use-Count Class
// private class for use by HasPtr only
class U_Ptr {
friend class HasPtr;
int *ip;
size_t use;
U_Ptr(int *p): ip(p), use(1) { }
~U_Ptr() { delete ip; }
};
Smart Pointer Class
/*
smart pointer class: takes ownership of the dynamically allocated
object to which it is bound
User code must dynamically allocate an object to initialize a HasPtr
and must not delete that object; the HasPtr class will delete it
*/
class HasPtr {
public:
// HasPtr owns the pointer; p must have been dynamically allocated
HasPtr(int *p, int i)
: ptr(new U_Ptr(p)), val(i) { }
// copy members and increment the use count
HasPtr(const HasPtr &orig)
: ptr(orig.ptr), val(orig.val) { ++ptr->use; }
HasPtr& operator=(const HasPtr&);
// if use count goes to zero, delete the U_Ptr object
~HasPtr() { if (--ptr->use == 0) delete ptr; }
friend ostream& operator<<(ostream&, const HasPtr&);
// copy control and constructors as before
// accessors must change to fetch value from U_Ptr object
int *get_ptr() const { return ptr->ip; }
int get_int() const { return val; }
// change the appropriate data member
void set_ptr(int *p) { ptr->ip = p; }
void set_int(int i) { val = i; }
// return or change the value pointed to, so ok for const objects
// Note: *ptr->ip is equivalent to *(ptr->ip)
int get_ptr_val() const { return *ptr->ip; }
void set_ptr_val(int i) { *ptr->ip = i; }
private:
U_Ptr *ptr; // points to use-counted U_Ptr class
int val;
};
Wonder: I am curious about why not simply using a int * to act like the Use-Count Class, just like the int* countPtr; used in the following new Smart Pointer Class:
class T
{
private:
int* countPtr; //
int* p;
int val;
public:
T(){
p = new int();
countPtr = new int();
*countPtr = 1;
val = 0;
}
T(T& t){
p = t.p;
countPtr = t.countPtr;
val = t.val;
*countPtr += 1;
}
T& operator = ( const T& rT){
if(*countPtr>1){
*countPtr -= 1;
}
else{
delete p;
delete countPtr;
}
p = rT.p;
countPtr = rT.countPtr;
val = rT.val;
*countPtr += 1;
return *this;
}
~T(){
if(*countPtr>1){
*countPtr -= 1;
}
else{
delete p;
delete countPtr;
}
}
int *get_ptr() const { return p; }
int get_int() const { return val; }
// change the appropriate data member
void set_ptr(int *ptr) { p = ptr; }
void set_int(int i) { val = i; }
};
Test: I tested the above Smart Pointer Class using code like the following and it seems working well.
int main()
{
T t1;
T t2(t1);
T t3(t1);
T t4;
t4 = t1;
return 0;
}
Real question: Is this new Smart Pointer Class with simply a int *countPtr sufficient enough? If yes, why bother to use an extra Use-Count Class like in the book? If no, what do I miss?
One property of the original implementation is that the delete is performed, in the control block object, with the original pointer type. This is a partial type erasure. No matter how much the smart pointer objects are copied, with somewhat different types, the original control block remains the same, with delete via the original pointer type.
However, since the original code you show is not templated, one must assume that it is an early example, followed later by similar templated code.
Converting a pointer up in a base class hierarchy, as can happen with copying of a smart pointer, means that delete on the new pointer type is only valid if the statically known new type has a virtual destructor.
For example, std::shared_ptr also deletes (guaranteed) via the original pointer type, unless one explicitly supplies a deleter functor that does something else.
My guess is that the author - whether consciously or subconsciously - is aware that having a separate class is useful in real-world smart pointers, e.g.:
a count of weak pointers (not sure if you'll have heard of them yet - they track an object without extending its lifetime, such that you can try to convert one into a (normal) shared pointer later, but it only works if there's at least one shared pointer to the object around to have kept it alive)
a mutex to make the shared pointer thread safe (though atomic operations may be better when available),
debugging information (e.g. boost::shared_ptr has a #ifdef to include an shared counter id)
virtual dispatch table, used by e.g. boost shared pointers to dispatch to OS-appropriate code (see boost/smart_ptr/detail/sp_counted_base_*.hpp headers)
I don't know the book, but perhaps they'll go on to explain what else might go into U_Ptr....
Your code is equivalent to the standard code reported by the book. However it is worst in some respects:
you need two allocations/deallocations instead of one (two ints instead of a single object). This might be slower and a little bit more difficult to manage.
you have a copy of the pointer duplicated in every object. So: duplicated information which you should guarantee to keep valid.
your object is larger (two pointers instead of one)
You only have one positive note:
the access to the pointer is direct instead of having one indirection. This could mean that the access to the referred object is slightly faster with your implementation...
(Updated 14.4.2017)
I by myself tried out unique_ptr and shared_ptr and was bit surprised to see that those classes does not make your life easier. I had function in one API where function would pick up Object*& - fill it out (pointer) and after that you need to delete that object. It's possible to use c++11, but you need to add extra temporary pointer for that purpose. (So use of *_ptr classes does not makes my life easier)
How complex would it be to implement smart pointer ?
Just by quickly glancing auto_ptr class implementation, I've quickly coded simple smart point container class, but then I've noticed that I need to support multiple smart pointers referencing the same object pointer.
Ok, so how complex could it be to code reference counting - I through, and went to google - and found one interesting article about it:
http://www.codingwisdom.com/codingwisdom/2012/09/reference-counted-smart-pointers-are-for-retards.html
Somehow I tend to agree with author of that article and comments written in that article that reference counting makes life even more complex, but still trying to stick to plain C also sounds bit dumb.
I'll now add code snippet here of my own class, and if you want to get newest version, you can check in this svn repository: https://sourceforge.net/p/testcppreflect/code/HEAD/tree/SmartPtr.h
Below is older version.
#pragma once
//
// If you're using multithreading, please make sure that two threads are not accessing
// SmartPtr<> pointers which are cross linked.
//
template <class T>
class SmartPtr
{
public:
SmartPtr() : ptr( nullptr ), next( nullptr )
{
}
SmartPtr( T* pt ) : ptr( pt ), next( nullptr )
{
}
SmartPtr( SmartPtr<T>& sp ) : ptr( nullptr ), next( nullptr )
{
operator=(sp);
}
~SmartPtr()
{
release();
}
// Reference to pointer - assumed to be filled out by user.
T*& refptr()
{
release();
return ptr;
}
// Pointer itself, assumed to be used.
T* get()
{
return ptr;
}
T* operator->() const
{
return ptr;
}
T* operator=( T* _ptr )
{
release();
ptr = _ptr;
return ptr;
}
SmartPtr<T>& operator=( SmartPtr<T>& sp )
{
release();
ptr = sp.ptr;
if ( ptr ) // If we have valid pointer, share ownership.
{
if( sp.next == nullptr )
{
next = &sp;
sp.next = this;
} else {
SmartPtr<T>* it = &sp;
while( it->next != &sp )
it = it->next;
next = &sp;
it->next = this;
}
}
return *this;
}
void release()
{
if ( !ptr )
return;
// Shared ownership.
if( next != nullptr )
{
// Remove myself from shared pointer list.
SmartPtr<T>* it = next;
while( it->next != this )
it = it->next;
if( it == it->next->next )
it->next = nullptr;
else
it->next = next;
next = nullptr;
ptr = nullptr;
return;
}
// Single user.
delete ptr;
ptr = nullptr;
}
T* ptr; // pointer to object
SmartPtr<T>* next; // nullptr if pointer is not shared with anyone,
// otherwise cyclic linked list of all SmartPtr referencing that pointer.
};
I've replaced reference counting with simple linked list - linked list keeps all class instances referenced, each destructor removes one reference away.
I've decided to rename operator* to refptr() function just to avoid developers writing extra fancy code. ("C++ jewels")
So in general I agree with article above - please don't make smart pointers too smart. :-)
I'm free to any improvement suggestions concerning this class and potential bugfixes.
And I wanted also to answer to original author's questions:
Real question: Is this new Smart Pointer Class with simply a int *countPtr
sufficient enough? If yes, why bother to use an extra Use-Count Class
like in the book? If no, what do I miss?
You're using separate mechanics for count management, like article link above mentions - it will becomes non-trivial to follow and debug reference counting. In my code snippet I use linked list of smart pointer instances, which does not perform any allocation ( so implementation above is faster than any other existing smart pointer implementation ), also it easier debugging of smart pointer itself - you can check by link (next) who locks down your memory from being collected.
But in overall - if you are experiencing memory leaks, I would say that it's highly non-trivial to find where they are if you're not originally made that code. Smart class pointer does not help in that sense to figure out who and how much has leaked out. Better to code once and properly that to fight with your own beast later on.
For memory leaks I recommend to find existing tools and use them - for example this one:
https://sourceforge.net/projects/diagnostic/
(There are plenty of them, but none of them works reliably/good enough).
I know that you're eager to put dislike to this implementation, but really - please tell me what obstacles you see in this implementation ?!
I'm in the process of writing a smart pointer countedptr and I've hit a speed bump. The basic function of countedptr is to work like any other smart pointer and also have a count of how many pointers are pointing to a single object. So far, the code is:
[SOLVED]
#include "std_lib_facilities.h"
template <class T>
class counted_ptr{
private:
T* pointer;
int* count;
public:
counted_ptr(T* p = 0, int* c = new int(1)) : pointer(p), count(c) {} // default constructor
explicit counted_ptr(const counted_ptr& p) : pointer(p.pointer), count(p.count) { ++*count; } // copy constructor
~counted_ptr() { --*count; delete pointer; }
counted_ptr& operator=(const counted_ptr& p)
{
pointer = p.pointer;
count = p.count;
++*count;
return *this;
}
T* operator->() const{ return pointer; }
T& operator*() const { return *pointer; }
int Get_count() const { return *count; }
};
int main()
{
counted_ptr<double> one;
counted_ptr<double>two(one);
int a = one.Get_count();
cout << a << endl;
}
When I try to do something like
one->pointer = new double(5);
then I get a compiler error saying "request for member 'pointer' in '*(&one)->counted_ptr::operator->with T = double' which is of non-class type double".
I considered making a function to do this, and while I could make a function to allocate an array of T's, I can't think of a way of making one for allocating actual objects. Any help is appreciated, thanks.
Old Solution
What about another assignment operator?
counted_ptr& counted_ptr::operator=(T* p)
{
if (! --*count) { delete count; }
pointer = p;
count = new int(1);
return *this;
}
...
one = new double(5);
Also, your destructor always deletes a shared pointer, which is probably what caused *one to be a random nomber. Perhaps you want something like:
counted_ptr::~counted_ptr() { if (! --*count) { delete pointer; delete count; } }
New Solution
As you want repointing a counted_ptr (eg one = new double(5)) to update all related counted_ptrs, place both the pointer and the count in a helper class, and have your pointer class hold a pointer to the helper class (you might already be headed down this path). You could go two ways in filling out this design:
Make the helper class a simple struct (and a private inner class) and place all the logic in the outer class methods
Make counted_ptr the helper class. counted_ptr maintains a reference count but doesn't automatically update the count; it's not a smart pointer, it only responds to release and retain messages. If you're at all familiar with Objective-C, this is basically its traditional memory management (autoreleasing aside). counted_ptr may or may not delete itself when the reference count reaches 0 (another potential difference from Obj-C). counted_ptrs shouldn't be copyable. The intent is that for any plain pointer, there should be at most one counted_ptr.
Create a smart_ptr class that has a pointer to a counted_ptr, which is shared among smart_ptr instances that are supposed to hold the same plain pointer. smart_ptr is responsible for automatically updating the count by sending its counted_ptr release and retain methods.
counted_ptr may or may not be a private inner class of shared_ptr.
Here's an interface for option two. Since you're doing this as an exercise, I'll let you fill out the method definitions. Potential implementations would be similar to what's already been posted except that you don't need a copy constructor and copy assignment operator for counted_ptr, counted_ptr::~counted_ptr doesn't call counted_ptr::release (that's smart_ptr::~smart_ptr's job) and counted_ptr::release might not free counted_ptr::_pointer (you might leave that up to the destructor).
// counted_ptr owns its pointer an will free it when appropriate.
template <typename T>
class counted_ptr {
private:
T *_pointer;
size_t _count;
// Make copying illegal
explicit counted_ptr(const counted_ptr&);
counted_ptr& operator=(const counted_ptr<T>& p);
public:
counted_ptr(T* p=0, size_t c=1);
~counted_ptr();
void retain(); // increase reference count.
bool release(); // decrease reference count. Return true iff count is 0
void reassign(T *p); // point to something else.
size_t count() const;
counted_ptr& operator=(T* p);
T& operator*() const;
T* operator->() const;
};
template <typename T>
class smart_ptr {
private:
counted_ptr<T> *_shared;
void release(); // release the shared pointer
void retain(); // retain the shared pointer
public:
smart_ptr(T* p=0, int c=1); // make a smart_ptr that points to p
explicit smart_ptr(counted_ptr<T>& p); // make a smart_ptr that shares p
explicit smart_ptr(smart_ptr& p); // copy constructor
~smart_ptr();
// note: a smart_ptr's brethren are the smart_ptrs that share a counted_ptr.
smart_ptr& operator=(smart_ptr& p); /* Join p's brethren. Doesn't alter pre-call
* brethren. p is non-const because this->_shared can't be const. */
smart_ptr& operator=(counted_ptr<T>& p); /* Share p. Doesn't alter brethren.
* p is non-const because *this isn't const. */
smart_ptr& operator=(T* p); // repoint this pointer. Alters brethren
size_t count() const; // reference count
T& operator*() const; // delegate these to _shared
T* operator->() const;
};
Hopefully, the only ambiguous points above are the intentional ones.
(Sorry, newbie here, and can't leave comments). What Adatapost added, "one=new double(5);" should work. One other change needed, though: the reference counting needs a little help.
...
~counted_ptr() {
--*count;
// deallocate objects whose last reference is gone.
if (!*count)
{
delete pointer;
delete count;
}
}
counted_ptr& operator=(const counted_ptr& p)
{
// be careful to accommodate self assignment
++*p.count;
// may lose a reference here
--*count;
if (!*count)
{
delete pointer;
delete count;
}
count=p.count;
pointer=p.pointer;
return *this;
}
Of course, there's some code repetition here. It might make sense to refactor that code into its own function, e.g.
private:
/** remove our reference */
void release()
{
--*count;
if (!*count)
{
delete pointer;
delete count;
}
}
Did you, perhaps, mean "one.pointer=new double(5);"? Writing "one->pointer=new double(5);" invokes counted_ptr<double>::operator->. That is, it is approximately equivalent to:
double *tmp = one.operator->(); // returns one.pointer
tmp->pointer = new double(5);
But a double pointer isn't a structure, and so it doesn't have a pointer member.
Since the immediate problem has already been solved, I want to offer something more long term:
As you continue to develop this code, you'll definitely want to offer it up for full review by experienced programmers, whether here or elsewhere. There were a few obvious problems with your code as you posted it, though outis has helped correct them. But even once your code all compiles and seems to work in your own tests, there may be tests and situations which you haven't yet learned to think about. Smart pointers can easily have subtle problems that don't show up until very specific situations. So you'll want others to look over your code to find anything which you may have missed.
Please don't take this as any kind of insult towards your current code. I'm just offering this as friendly advice to ensure you learn the most you can out of this project.
Unless you are not doing this for academic reasons, you might want to use consider using the use_count() member of boost::shared_ptr. It's not entirely efficient, but it does work and you're better off using something well tested, mature, and thread safe. If you are doing this for learning purposes, be sure to check out the treatment of Reference Counting and Smart Pointers in More Effective C++.
You need to decrement the count and possibly delete the pointer to the old value in operator = before you overwrite it. You also need 'delete count' everywhere you have 'delete pointer' to avoid leaking memory