I want a safe C++ pointer container similar to boost's scoped_ptr, but with value-like copy semantics. I intend to use this for a very-rarely used element of very-heavily used class in the innermost loop of an application to gain better memory locality. In other words, I don't care about performance of this class so long as its "in-line" memory load is small.
I've started out with the following, but I'm not that adept at this; is the following safe? Am I reinventing the wheel and if so, where should I look?
template <typename T>
class copy_ptr {
T* item;
public:
explicit copy_ptr() : item(0) {}
explicit copy_ptr(T const& existingItem) : item(new T(existingItem)) {}
copy_ptr(copy_ptr<T> const & other) : item(new T(*other.item)) {}
~copy_ptr() { delete item;item=0;}
T * get() const {return item;}
T & operator*() const {return *item;}
T * operator->() const {return item;}
};
Edit: yes, it's intentional that this behaves pretty much exactly like a normal value. Profiling shows that the algorithm is otherwise fairly efficient but is sometimes hampered by cache misses. As such, I'm trying to reduce the size of the object by extracting large blobs that are currently included by value but aren't actually used in the innermost loops. I'd prefer to do that without semantic changes - a simple template wrapper would be ideal.
No it is not.
You have forgotten the Assignment Operator.
You can choose to either forbid assignment (strange when copying is allowed) by declaring the Assignment Operator private (and not implementing it), or you can implement it thus:
copy_ptr& operator=(copy_ptr const& rhs)
{
using std::swap;
copy_ptr tmp(rhs);
swap(this->item, tmp.item);
return *this;
}
You have also forgotten in the copy constructor that other.item may be null (as a consequence of the default constructor), pick up your alternative:
// 1. Remove the default constructor
// 2. Implement the default constructor as
copy_ptr(): item(new T()) {}
// 3. Implement the copy constructor as
copy_ptr(copy_ptr const& rhs): item(other.item ? new T(*other.item) : 0) {}
For value-like behavior I would prefer 2, since a value cannot be null. If you go for allowing nullity, introduces assert(item); in both operator-> and operator* to ensure correctness (in debug mode) or throw an exception (whatever you prefer).
Finally the item = 0 in the destructor is useless: you cannot use the object once it's been destroyed anyway without invoking undefined behavior...
There's also Roger Pate's remark about const-ness propagation to be more "value-like" but it's more a matter of semantics than correctness.
You should "pass on" the const-ness of the copy_ptr type:
T* get() { return item; }
T& operator*() { return *item; }
T* operator->() { return item; }
T const* get() const { return item; }
T const& operator*() const { return *item; }
T const* operator->() const { return item; }
Specifying T isn't needed in the copy ctor:
copy_ptr(copy_ptr const &other) : item (new T(*other)) {}
Why did you make the default ctor explicit? Nulling the pointer in the dtor only makes sense if you plan on UB somewhere...
But these are all minor issues, what you have there is pretty much it. And yes, I've seen this invented many times over, but people tend to tweak the semantics just slightly each time. You might look at boost::optional, as that's almost what you have written here as you present it, unless you're adding move semantics and other operations.
In addition to what Roger has said, you can Google 'clone_ptr' for ideas/comparisons.
Related
As explained in this answer, the copy-and-swap idiom is implemented as follows:
class MyClass
{
private:
BigClass data;
UnmovableClass *dataPtr;
public:
MyClass()
: data(), dataPtr(new UnmovableClass) { }
MyClass(const MyClass& other)
: data(other.data), dataPtr(new UnmovableClass(*other.dataPtr)) { }
MyClass(MyClass&& other)
: data(std::move(other.data)), dataPtr(other.dataPtr)
{ other.dataPtr= nullptr; }
~MyClass() { delete dataPtr; }
friend void swap(MyClass& first, MyClass& second)
{
using std::swap;
swap(first.data, other.data);
swap(first.dataPtr, other.dataPtr);
}
MyClass& operator=(MyClass other)
{
swap(*this, other);
return *this;
}
};
By having a value of MyClass as parameter for operator=, the parameter can be constructed by either the copy constructor or the move constructor. You can then safely extract the data from the parameter. This prevents code duplication and assists in exception safety.
The answer mentions you can either swap or move the variables in the temporary. It primarily discusses swapping. However, a swap, if not optimised by the compiler, involves three move operations, and in more complex cases does additional extra work. When all you want, is to move the temporary into the assigned-to object.
Consider this more complex example, involving the observer pattern. In this example, I've written the assignment operator code manually. Emphasis is on the move constructor, assignment operator and swap method:
class MyClass : Observable::IObserver
{
private:
std::shared_ptr<Observable> observable;
public:
MyClass(std::shared_ptr<Observable> observable) : observable(observable){ observable->registerObserver(*this); }
MyClass(const MyClass& other) : observable(other.observable) { observable.registerObserver(*this); }
~MyClass() { if(observable != nullptr) { observable->unregisterObserver(*this); }}
MyClass(MyClass&& other) : observable(std::move(other.observable))
{
observable->unregisterObserver(other);
other.observable.reset(nullptr);
observable->registerObserver(*this);
}
friend void swap(MyClass& first, MyClass& second)
{
//Checks for nullptr and same observable omitted
using std::swap;
swap(first.observable, second.observable);
second.observable->unregisterObserver(first);
first.observable->registerObserver(first);
first.observable->unregisterObserver(second);
second.observable->registerObserver(second);
}
MyClass& operator=(MyClass other)
{
observable->unregisterObserver(*this);
observable = std::move(other.observable);
observable->unregisterObserver(other);
other.observable.reset(nullptr);
observable->registerObserver(*this);
}
}
Clearly, the duplicated part of the code in this manually written assignment operator is identical to that of the move constructor. You could perform a swap in the assignment operator and the behaviour would be right, but it would potentially perform more moves and perform an extra registration (in the swap) and unregistration (in the destructor).
Wouldn't it make much more sense to reuse the move constructor's code in stead?
private:
void performMoveActions(MyClass&& other)
{
observable->unregisterObserver(other);
other.observable.reset(nullptr);
observable->registerObserver(*this);
}
public:
MyClass(MyClass&& other) : observable(std::move(other.observable))
{
performMoveActions(other);
}
MyClass& operator=(MyClass other)
{
observable->unregisterObserver(*this);
observable = std::move(other.observable);
performMoveActions(other);
}
It looks to me like this approach is never inferior to the swap approach. Am I right in thinking that the copy-and-swap idiom would be better off as the copy-and-move idiom in C++11, or did I miss something important?
First of all, it is generally unnecessary to write a swap function in C++11 as long as your class is movable. The default swap will resort to moves:
void swap(T& left, T& right) {
T tmp(std::move(left));
left = std::move(right);
right = std::move(tmp);
}
And that's it, the elements are swapped.
Second, based on this, the Copy-And-Swap actually still holds:
T& T::operator=(T const& left) {
using std::swap;
T tmp(left);
swap(*this, tmp);
return *this;
}
// Let's not forget the move-assignment operator to power down the swap.
T& T::operator=(T&&) = default;
Will either copy and swap (which is a move) or move and swap (which is a move), and should always achieve close to the optimum performance. There might be a couple redundant assignments, but hopefully your compiler will take care of it.
EDIT: this only implements the copy-assignment operator; a separate move-assignment operator is also required, though it can be defaulted, otherwise a stack overflow will occur (move-assignment and swap calling each other indefinitely).
Give each special member the tender loving care it deserves, and try to default them as much as possible:
class MyClass
{
private:
BigClass data;
std::unique_ptr<UnmovableClass> dataPtr;
public:
MyClass() = default;
~MyClass() = default;
MyClass(const MyClass& other)
: data(other.data)
, dataPtr(other.dataPtr ? new UnmovableClass(*other.dataPtr)
: nullptr)
{ }
MyClass& operator=(const MyClass& other)
{
if (this != &other)
{
data = other.data;
dataPtr.reset(other.dataPtr ? new UnmovableClass(*other.dataPtr)
: nullptr);
}
return *this;
}
MyClass(MyClass&&) = default;
MyClass& operator=(MyClass&&) = default;
friend void swap(MyClass& first, MyClass& second)
{
using std::swap;
swap(first.data, second.data);
swap(first.dataPtr, second.dataPtr);
}
};
The destructor could be implicitly defaulted above if desired. Everything else needs to be explicitly defined or defaulted for this example.
Reference: http://accu.org/content/conf2014/Howard_Hinnant_Accu_2014.pdf
The copy/swap idiom will likely cost you performance (see the slides). For example ever wonder why high performance / often used std::types like std::vector and std::string don't use copy/swap? Poor performance is the reason. If BigClass contains any std::vectors or std::strings (which seems likely), your best bet is to call their special members from your special members. The above is how to do that.
If you need strong exception safety on the assignment, see the slides for how to offer that in addition to performance (search for "strong_assign").
It's been a long time since I asked this question, and I've known the answer for a while now, but I've put off writing the answer for it. Here it is.
The answer is no. The Copy-and-swap idiom should not become the Copy-and-move idiom.
An important part of Copy-and-swap (which is also Move-construct-and-swap) is a way to implement assignment operators with safe cleanup. The old data is swapped into a copy-constructed or move-constructed temporary. When the operation is done, the temporary is deleted, and its destructor is called.
The swap behaviour is there to be able to reuse the destructor, so you don't have to write any cleanup code in your assignment operators.
If there's no cleanup behaviour to be done and only assignment, then you should be able to declare the assignment operators as default and copy-and-swap isn't needed.
The move constructor itself usually doesn't require any clean-up behaviour, since it's a new object. The general simple approach is to make the move constructor invoke the default constructor, and then swap all the members with the move-from object. The moved-from object will then be like a bland default-constructed object.
However, in this question's observer pattern example, that's actually an exception where you have to do extra cleanup work because references to the old object need to be changed. In general, I would recommend making your observers and observables, and other design constructs based around references, unmovable whenever possible.
Consider the following code:
struct s
{
const int id;
s(int _id):
id(_id)
{}
};
// ...
vector<s> v; v.push_back(s(1));
I get a compiler error that 'const int id' cannot use default assignment operator.
Q1. Why does push_back() need an assignment operator?
A1. Because the current c++ standard says so.
Q2. What should I do?
I don't want to give up the const specifier
I want the data to be copied
A2. I will use smart pointers.
Q3. I came up with a "solution", which seems rather insane:
s& operator =(const s& m)
{
if(this == &m) return *this;
this->~s();
return *new(this) s(m);
}
Should I avoid this, and why (if so)? Is it safe to use placement new if the object is on the stack?
C++03 requires that elements stored in containers be CopyConstructible and Assignable (see §23.1). So implementations can decide to use copy construction and assignment as they see fit. These constraints are relaxed in C++11. Explicitly, the push_back operation requirement is that the type be CopyInsertable into the vector (see §23.2.3 Sequence Containers)
Furthermore, C++11 containers can use move semantics in insertion operations and do on.
I don't want to give up the const specifier
Well, you have no choice.
s& operator =(const s& m) {
return *new(this) s(m);
}
Undefined behaviour.
There's a reason why pretty much nobody uses const member variables, and it's because of this. There's nothing you can do about it. const member variables simply cannot be used in types you want to be assignable. Those types are immutable, and that's it, and your implementation of vector requires mutability.
s& operator =(const s& m)
{
if(this == &m) return *this;
this->~s();
return *new(this) s(m);
}
Should I avoid this, and why (if so)? Is it safe to use placement new if the object is on the stack?
You should avoid it if you can, not because it is ill-formed, but because it is quite hard for a reader to understand your goal and trust in this code. As a programmer, you should aim to reduce the number of WTF/line of code you write.
But, it is legal. According to
[new.delete.placement]/3
void* operator new(std::size_t size, void* ptr) noexcept;
3 Remarks: Intentionally performs no other action.
Invoking the placement new does not allocate or deallocate memory, and is equivalent to manually call the copy constructor of s, which according to [basic.life]/8 is legal if s has a trivial destructor.
Ok,
You should always think about a problem with simple steps.
std::vector<typename T>::push_back(args);
needs to reserve space in the vector data then assigns(or copy, or move) the value of the parameter to memory of the vector.data()[idx] at that position.
to understand why you cannot use your structure in the member function std::vector::push_back , try this:
std::vector<const int> v; // the compiler will hate you here,
// because this is considered ill formed.
The reason why is ill formed, is that the member functions of the class std::vector could call the assignment operator of its template argument, but in this case it's a constant type parameter "const int" which means it doesn't have an assignment operator ( it's none sense to assign to a const variable!!).
the same behavior is observed with a class type that has a const data member. Because the compiler will delete the default assignment operator, expel
struct S
{
const int _id; // automatically the default assignment operator is
// delete i.e. S& operator-(const S&) = delete;
};
// ... that's why you cannot do this
std::vector<S> v;
v.Push_back(S(1234));
But if you want to keep the intent and express it in a well formed code this is how you should do it:
class s
{
int _id;
public:
explicit s(const int& id) :
_id(id)
{};
const int& get() const
{
return _id;
}; // no user can modify the member variable after it's initialization
};
// this is called data encapsulation, basic technique!
// ...
std::vector<S> v ;
v.push_back(S(1234)); // in place construction
If you want to break the rules and impose an assignable constant class type, then do what the guys suggested above.
Q2. What should I do?
Store pointers, preferably smart.
vector<unique_ptr<s>> v;
v.emplace_back(new s(1));
It's not really a solution, but a workaround:
#include <vector>
struct s
{
const int id;
s(int _id):
id(_id)
{}
};
int main(){
std::vector<s*> v;
v.push_back(new s(1));
return 0;
}
This will store pointers of s instead of the object itself. At least it compiles... ;)
edit: you can enhance this with smart c++11 pointers. See Benjamin Lindley's answer.
Use a const_cast in the assignment operator:
S& operator=(const S& rhs)
{
if(this==&rhs) return *this;
int *pid=const_cast<int*>(&this->id);
*pid=rhs.id;
return *this;
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 months ago.
Improve this question
I'm learning c++ and I recently learned (here in stack overflow) about the copy-and-swap idiom and I have a few questions about it. So, suppose I have the following class using a copy-and-swap idiom, just for example:
class Foo {
private:
int * foo;
int size;
public:
Foo(size_t size) : size(size) { foo = new int[size](); }
~Foo(){delete foo;}
Foo(Foo const& other){
size = other.size;
foo = new int[size];
copy(other.foo, other.foo + size, foo);
}
void swap(Foo& other) {
std::swap(foo, other.foo);
std::swap(size, other.size);
}
Foo& operator=(Foo g) {
g.swap(*this);
return *this;
}
int& operator[] (const int idx) {return foo[idx];}
};
My question is, suppose I have another class that have a Foo object as data but no pointers or other resources that might need custom copying or assignment:
class Bar {
private:
Foo bar;
public:
Bar(Foo foo) : bar(foo) {};
~Bar(){};
Bar(Bar const& other) : bar(other.bar) {};
Bar& operator=(Bar other) {bar = other.bar;}
};
Now I have a series of questions:
Are the methods and constructors as implemented above for the Bar class safe? Having used the copy-and-swap for Foo make me sure that no harm can be done when assigning or copying Bar?
Passing the argument by reference in the copy constructor and in swap is mandatory?
Is it right to say that when the argument of operator= is passed by value, the copy constructor is called for this argument to generate a temporary copy of the object, and that it is this copy which is then swapped with *this? If I passed by reference in operator= I would have a big problem, right?
Are there situations in which this idiom fails to provide complete safety in copying and assigning Foo?
As much as possible, you should initialize the members of your class in the initializer list. That will also take care of the bug I told you about in the comment. With this in mind, your code becomes:
class Foo {
private:
int size;
int * foo;
public:
Foo(size_t size) : size(size), foo(new int[size]) {}
~Foo(){delete[] foo;} // note operator delete[], not delete
Foo(Foo const& other) : size(other.size), foo(new int[other.size]) {
copy(other.foo, other.foo + size, foo);
}
Foo& swap(Foo& other) {
std::swap(foo, other.foo);
std::swap(size, other.size);
return *this;
}
Foo& operator=(Foo g) {
return swap(g);
}
int& operator[] (const int idx) {return foo[idx];}
};
and
class Bar {
private:
Foo bar;
public:
Bar(Foo foo) : bar(foo) {};
~Bar(){};
Bar(Bar const& other) : bar(other.bar) { }
Bar& swap(Bar &other) { bar.swap(other.bar); return *this; }
Bar& operator=(Bar other) { return swap(other); }
}
which uses the same idiom throughout
note
as mentioned in a comment on the question, Bar's custom copy constructors etc. are unnecessary, but we'll assume Bar has other things as well :-)
second question
Passing by reference to swap is needed because both instances are changed.
Passing by reference to the copy constructor is needed because if passing by value, you'd need to call the copy constructor
third question
yes
fourth question
no, but it is not always the most efficient way of doing things
1 - Are the methods and constructors as implemented above for the Bar class safe? Having used the copy-and-swap for Foo make me sure that no harm can be done when assigning or copying Bar?
Regarding the copy-ctor: that's always safe (all-or-nothing). It either completes (all), or it throws an exception (nothing).
If your class is only made up of one member (i.e. no base classes as well), the assignment operator will be just as safe as that of the member's class. If you have more then one member, the assignment operator will no longer be "all or nothing". The second member's assignment operator could throw an exception, and in that case the object will be assigned "half way". That means you need to implement copy-and-swap again in the new class to get "all or nothing" assignment.
However it will still be "safe", in the sense that you won't leak any resources. And of course the state of each member individually will be consistent - just the state of the new class won't be consistent, because one member was assigned and the other was not.
2 - Passing the argument by reference in the copy constructor and in swap is mandatory?
Yes, passing by reference is mandatory. The copy constructor is the one that copies objects, so it cannot take it's argument by value, since that would mean the argument has to be copied. This would lead to infinite recursion. (The copy-ctor would be called for the argument, which would mean calling the copy-ctor for the argument, which would mean ...). For swap, the reason is another: if you were to pass the argument by value, you could never use swap to really exchange the contents of two objects - the "target" of the swap would be a copy of the object originally passed in, which would be destroyed immediately.
3 - Is it right to say that when the argument of operator= is passed by value, the copy constructor is called for this argument to generate a temporary copy of the object, and that it is this copy which is then swapped with *this? If I passed by reference in operator= I would have a big problem, right?
Yes, that's right. However it's also quite common to take the argument by reference-to-const, construct a local copy and then swap with the local copy. The by reference-to-const way has some drawbacks however (it disables some optimizations). If you're not implementing copy-and-swap though, you should probably pass by reference-to-const.
4 - Are there situations in which this idiom fails to provide complete safety in copying and assigning Foo?
None that I know of. Of course one can always make things fail with multi-threading (if not properly synchronized), but that should be obvious.
This is a classic example of where following an idiom leads to unnecessary performance penalties (premature pessimization). This isn't your fault. The copy-and-swap idiom is way over-hyped. It is a good idiom. But it should not be followed blindly.
Note: One of the most expensive things you can do on a computer is allocate and deallocate memory. It is worthwhile to avoid doing so when practical.
Note: In your example, the copy-and-swap idiom always performs one deallocation, and often (when the rhs of the assignment is an lvalue) one allocation as well.
Observation: When size() == rhs.size(), no deallocation or allocation need be done. All you have to do is a copy. This is much, much faster.
Foo& operator=(const Foo& g) {
if (size != g.size)
Foo(g).swap(*this);
else
copy(other.foo, other.foo + size, foo);
return *this;
}
I.e. check for the case where you can recycle resources first. Then copy-and-swap (or whatever) if you can't recycle your resources.
Note: My comments do not contradict the good answers given by others, nor any correctness issues. My comment is only about a significant performance penalty.
I have several members in my class which are const and can therefore only be initialised via the initialiser list like so:
class MyItemT
{
public:
MyItemT(const MyPacketT& aMyPacket, const MyInfoT& aMyInfo)
: mMyPacket(aMyPacket),
mMyInfo(aMyInfo)
{
}
private:
const MyPacketT mMyPacket;
const MyInfoT mMyInfo;
};
My class can be used in some of our internally defined container classes (e.g. vectors), and these containers require that operator= is defined in the class.
Of course, my operator= needs to do something like this:
MyItemT&
MyItemT::operator=(const MyItemT& other)
{
mMyPacket = other.mPacket;
mMyInfo = other.mMyInfo;
return *this;
}
which of course doesn't work because mMyPacket and mMyInfo are const members.
Other than making these members non-const (which I don't want to do), any ideas about how I could fix this?
You're kind of violating the definition of const if you have an assignment operator that can change them after construction has finished. If you really need to, I think Potatoswatter's placement new method is probably best, but if you have an assignment operator your variables aren't really const, since someone could just make a new instance and use it to change their values
Rather than storing objects in your containers directly, you might be able to store pointers (or smart pointers). That way, you don't have to mutate any of the members of your class -- you get back exactly the same object as you passed in, const and all.
Of course, doing this will probably change the memory management of your application somewhat, which may well be a good enough reason not to want to.
It's a dirty hack, but you can destroy and reconstruct yourself:
MyItemT&
MyItemT::operator=(const MyItemT& other)
{
if ( this == &other ) return *this; // "suggested" by Herb Sutter ;v)
this->MyItemT::~MyItemT();
try {
new( this ) MyItemT( other );
} catch ( ... ) {
new( this ) MyItemT(); // nothrow
throw;
}
return *this;
}
Edit: lest I destroy my credibility, I don't actually do this myself, I would remove the const. However, I've been debating changing the practice, because const simply is useful and better to use wherever possible.
Sometimes there is a distinction between the resource and the value represented by an object. A member may be const through changes to value as long as the resource is the same, and it would be nice to get compile-time safety on that.
Edit 2: #Charles Bailey has provided this wonderful (and highly critical) link: http://gotw.ca/gotw/023.htm.
Semantics are tricky in any derived class operator=.
It may be inefficient because it doesn't invoke assignment operators that have been defined.
It's incompatible with wonky operator& overloads (whatever)
etc.
Edit 3: Thinking through the "which resource" vs "what value" distinction, it seems clear that operator= should always change the value and not the resource. The resource identifier may then be const. In the example, all the members are const. If the "info" is what's stored inside the "packet," then maybe the packet should be const and the info not.
So the problem isn't so much figuring out the semantics of assignment as lack of an obvious value in this example, if the "info" is actually metadata. If whatever class owns a MyItemT wants to switch it from one packet to another, it needs to either give up and use an auto_ptr<MyItemT> instead, or resort to a similar hack as above (the identity test being unnecessary but the catch remaining) implemented from outside. But operator= shouldn't change resource binding except as an extra-special feature which absolutely won't interfere with anything else.
Note that this convention plays well with Sutter's advice to implement copy construction in terms of assignment.
MyItemT::MyItemT( MyItemT const &in )
: mMyPacket( in.mMyPacket ) // initialize resource, const member
{ *this = in; } // assign value, non-const, via sole assignment method
I think you could get away with a special const proxy.
template <class T>
class Const
{
public:
// Optimal way of returning, by value for built-in and by const& for user types
typedef boost::call_traits<T>::const_reference const_reference;
typedef boost::call_traits<T>::param_type param_type;
Const(): mData() {}
Const(param_type data): mData(data) {}
Const(const Const& rhs): mData(rhs.mData) {}
operator const_reference() const { return mData; }
void reset(param_type data) { mData = data; } // explicit
private:
Const& operator=(const Const&); // deactivated
T mData;
};
Now, instead of const MyPacketT you would have Const<MyPacketT>. Not that the interface only provides one way to change it: through an explicit call to reset.
I think any use of mMyPacket.reset can easily be search for. As #MSalters said it protects against Murphy, not Machiavelli :)
You might consider making the MyPacketT and MyInfoT members be pointers to const (or smart pointers to const). This way the data itself is still marked const and immutable, but you can cleanly 'swap' to another set of const data in an assignment if that makes sense. In fact, you can use the swap idiom to perform the assignment in an exception safe manner.
So you get the benefit of const to help you prevent accidentally allowing changes that you want the design to prevent, but you still allow the object as a whole to be assigned from another object. For example, this will let you use objects of this class in STL containers.
You might look at this as a special application of the 'pimpl' idiom.
Something along the lines of:
#include <algorithm> // for std::swap
#include "boost/scoped_ptr.hpp"
using namespace boost;
class MyPacketT {};
class MyInfoT {};
class MyItemT
{
public:
MyItemT(const MyPacketT& aMyPacket, const MyInfoT& aMyInfo)
: pMyPacket(new MyPacketT( aMyPacket)),
pMyInfo(new MyInfoT( aMyInfo))
{
}
MyItemT( MyItemT const& other)
: pMyPacket(new MyPacketT( *(other.pMyPacket))),
pMyInfo(new MyInfoT( *(other.pMyInfo)))
{
}
void swap( MyItemT& other)
{
pMyPacket.swap( other.pMyPacket);
pMyInfo.swap( other.pMyInfo);
}
MyItemT const& operator=( MyItemT const& rhs)
{
MyItemT tmp( rhs);
swap( tmp);
return *this;
}
private:
scoped_ptr<MyPacketT const> pMyPacket;
scoped_ptr<MyInfoT const> pMyInfo;
};
Finally, I changed my example to use scoped_ptr<> instead of shared_ptr<> because I thought it was a more general representation of what the OP intended. However, if the 'reassignable' const members can be shared (and that's probably true, given my understanding of why the OP wants them const), then it might be an optimization to use shared_ptr<>'s and let the copy and assignment operations of the shared_ptr<> class take care of things for those objects - if you have no other members that require special copy or assign sematics, then your class just got a lot simpler, and you might even save a significant bit of memory usage by being able to share copies of the MyPacketT and MyInfoT objects.
i have small problem understanding why my smart pointer class is leaking on self assing.
If i do something like this
SmartPtr sp1(new CSine());//CSine is a class that implements IFunction iterface
sp1=sp1;
my colleagues told me that my smart pointer leaks. I added some log messages in my smart pointer to track what is going on and a test and reported this:
SmartPtr sp1(new CSine());
->CSine constructor
->RefCounter increment 0->1
->RefCounter constructor
->SmartPtr constructor
sp1=sp1;
->checks if this.RefCounter == to parameter.RefCounter, if true returns the smart pointer unmodified else modifies the object and returns it with the new values; in this case it returns true and returns the object unchanged.
at the end
->SmartPtr destructor
->RefCounter decrement 1->0
->RefCounter destructor
->CSine destructor
i can't understand why they consider that my smart pointer leaks...any ideas?
Thank you in advance!
class SmartPtr
{
private:
RefCounter* refCnt;
void Clear()
{
if(!isNull() && refCnt->Decr() == 0)
delete refCnt;
refCnt = 0;
};
public:
explicit SmartPtr();
explicit SmartPtr(IFunction *pt):refCnt(new RefCounter(pt)){};
SmartPtr(SmartPtr& other)
{
refCnt = other.refCnt;
if (!isNull())
refCnt->Incr();
};
virtual ~SmartPtr(void){Clear();};
SmartPtr& operator=(SmartPtr& other)
{
if(other.refCnt != refCnt)
{
if(!rVar.isNull())
other.refCnt->Incr();
Clear();
refCnt = other.refCnt;
}
return *this;
};
SmartPtr& operator=(IFunction* _p)
{
if(!isNull())
{
Clear();
}
refCnt = new RefCounter(fct);
return *this;
};
IFunction* operator->();
const IFunction* operator->() const;
IFunction& operator*();
const IFunction& operator*() const;
bool isNull() const { return refCnt == 0; };
inline bool operator==(const int _number) const;
inline bool operator!=(const int _number) const;
inline bool operator==(IFunction* _other) const;
inline bool operator!=(IFunction* _other) const;
inline bool operator==(SmartPtr& _other) const;
inline bool operator!=(SmartPtr& _other) const;
};
class RefCounter
{
friend class SmartPtr;
private:
IFunction* p;
unsigned c;
explicit RefCounter(IFunction* _p):c(0),p(_p)
{
if(_p != NULL)
Incr();
cout<<"RefCounter constructor."<<endl;
}
virtual ~RefCounter(void)
{
cout<<"RefCounter destructor."<<endl;
if(c == 0)
delete p;
}
unsigned Incr()
{
++c;
cout<<"RefCounter increment count:"<<c-1<<" to "<<c<<endl;
return c;
}
unsigned Decr()
{
if(c!=0)
{
--c;
cout<<"RefCounter decrement count:"<<c+1<<" to "<<c<<endl;
return c;
}
else
return 0;
}
};
SmartPtr& operator=(SmartPtr& other)
{
if(rVar.refCnt != refCnt)
should be:
if ( this != & other )
You might want to look at the following quote from A Proposal to Add General Purpose Smart Pointers to the Library Technical Report:
The Boost developers found a shared-ownership smart pointer exceedingly difficult to implement correctly. Others have made the same observation. For example, Scott Meyers [Meyers01] says:
"The STL itself contains no reference-counting smart pointer, and writing a good one - one that works correctly all the time - is tricky enough that you don't want to do it unless you have to. I published the code for a reference-counting smart pointer in More Effective C++ in 1996, and despite basing it on established smart pointer implementations and submitting it to extensive pre- publication reviewing by experienced developers, a small parade of valid bug reports has trickled in for years. The number of subtle ways in which reference-counting smart pointers can fail is remarkable."
If this is homework, read about how to implement copy ctor and assignment operator using a swap() (member) function. Otherwise, do not try to write your own smart pointer. You cannot win.
I don't see a leak either, but I think there are some other problems (other than many compiler errors - this cannot be the code you are using):
SmartPtr& operator=(SmartPtr& other)
should take the argument by const reference. You don't have to increment the reference count of other, because you can do it on the non-const left-hand side, as they will be sharing the same reference count instance.
Next, the canonical way to implement assignment for such classes is using the copy-and-swap idiom - which means you should also define a trivial swap method (which just swaps the pointers), and worry less about self-assignment :)
My impression is that there is no memory leak.
To be sure:
test with valgrind or the VS-alternative
use std::tr1::shared_ptr (if this is more than educational)
Your code doesn't compile, which leads me to believe that the version you posted can't be the version you colleagues are complaining about.
Pretty much any smart pointer will have cases where it leaks. It's just the way that it has to be if you implement it using references. There's also a million other problems plus they are slow. Since they are buggier than raw pointers there's really not much use if all you get out of it is reference counting. I have been tempted to use them for some very special purposes but they are not for general programming use. There's a reason that they are not allowed in STL containers for example.