Is calling a d-tor inside move assignment operator good practice?
here some example code:
VectorList &operator = (VectorList &&other){
~VectorList(); // if this is not a good practice,
// I will need to paste whole d-tor here.
_buffer = std::move(other._buffer );
_dataCount = std::move(other._dataCount );
_dataSize = std::move(other._dataSize );
other._clear();
return *this;
}
Should I use this code, or should I use swap() with move constructed object?
~VectorList does more than run the code in the dtor body: it actually destroys the object.
After that, the storage is unused. You can construct a new object there using a constructor, but simply accessing members is going to either be undefined behaviour, or require language-lawyers to find the loophole that lets it be defined.
Even if defined, it is dangerous, as an exception thrown while an automatic storage object is destroyed is bad news. Plus if the assigned-to class is actually of derived type, the dtor call itself is UB!
Neither is a worthwhile approach. The benefits are too small.
The better alternative is copy-swap (which is at least easy to get correct: it can prevent some optimizations), or refactor out the 'clear' code from both the dtor and assignment. Then call it at both spots.
Scott Meyers says don't use swap(): http://scottmeyers.blogspot.sg/2014/06/the-drawbacks-of-implementing-move.html
Regarding your current implementation, it seems you can do it more simply. I imagine that the destructor actually deletes _buffer and does little else. If that's true, you should just replace your harder-to-reason-about explicit destructor call with delete _buffer.
Related
I'm still new to c++, apologies if this is obvious, but I could not find a good answer after much googling.
I wish I could write the following code.
class Test {
public:
Test();
private:
std::unique_ptr<Dummy> m_Dummy;
};
Test::Test() {
auto data = // generate some data here
m_Dummy = std::make_unique<Dummy>(data);
}
What happens:
The assignment operator on m_Dummy calls unique_ptr::reset,
which calls delete on the pointer,
which calls the m_Dummy destructor,
and running the destructor creates a segfault because it was never initialized.
The correct way would be to initialize it in the constructor initialization list.
But then I would not be able to pass in the data I want.
Test::Test() : m_Dummy{std::make_unique<Dummy>(data)} { // Don't have time to generate data
}
I am not sure how to make this cleaner.
My current idea is to change Dummy to have default constructor then an initialize function which takes the data.
It feels wrong though.
Is there a cleaner way to handle this?
What is normally done with smart pointers that need parameters and also need to be a class member?
Thanks,
Nathan
Edit:
From the answer below perhaps there is a completely different problem somewhere in my code causing this.
This is the callstack from the debugger just before the segfault is thrown.
Dummy::~Dummy Dummy.cpp:24
std::default_delete<Dummy>::operator() unique_ptr.h:78
std::unique_ptr<Dummy, std::default_delete<Dummy> >::reset unique_ptr.h:371
std::unique_ptr<Dummy, std::default_delete<Dummy> >::operator= unique_ptr.h:278
Test::Test Test.cpp:42
std::make_unique<Test, int&, double, double> unique_ptr.h:821
World::World World.cpp:25
Application::Run Application.cpp:77
main main.cpp:10
__libc_start_main 0x00007fbd47bbdb97
_start 0x0000555e1df657ea
Edit2:
The problem was that in the process of creating my data I was corrupting my memory and Dummy just happened to be the victim.
My original proposal to create the unique_ptr works now.
Thanks
What happens:
The assignment operator on m_Dummy calls unique_ptr::reset,
which calls delete on the pointer,
which calls the m_Dummy destructor,
and running the destructor creates a segfault because it was never initialized.
That is NOT what happens under normal conditions.
m_Dummy is not initialized explicitly in the Test constructor, so it gets implicitly default-constructed instead, and its default constructor sets its held pointer to nullptr.
When a unique_ptr holds nullptr, reset() is a no-op. It is perfectly safe to assign to a unique_ptr that is holding nullptr.
Even if reset() were not a no-op, it is perfectly safe to call delete on a nullptr.
That said, the ONLY way Dummy's destructor could be getting called when assigning to m_Dummy is when m_Dummy is not holding a nullptr. For that to happen in the Test constructor you have shown, m_Dummy would have to be in an invalid state, either because:
the Test constructor was called on invalid memory (unlikely, unless you are misusing placement-new)
your code to initialize data, or even the Dummy constructor itself, is corrupting random memory, and m_Dummy is an unwitting victim of that corruption (more likely).
Create a static method to generate the data?
class Test {
public:
Test();
private:
static Dummy makeConstructionData()
{
return Dummy();
}
std::unique_ptr<Dummy> m_Dummy;
};
Then later on you can do:
Test::Test() : m_Dummy{std::make_unique<Dummy>(makeConstructionData())} {
}
I have a class, the member variable is std::unique_ptr<double[]>. Initializing this variable is always done by make_unique<double[]>(size).
But I do not know how to write code for destructor for this member variable
I know std::unique_ptr has a method get_deleter(), but when I look at the docs, it just provide self-defined delete. I do some research about similiar examples. But all of them are about new className(), not make_unique().
the verison of cpp is c++17
My code
class test{
public:
test(int size) : size_{size}, arr_{make_unique<double[]>( size )} {}
~test(){
// how to destroy arr_
}
private:
int size_;
std::unique_ptr<double[]> arr_;
};
I do not know how to start. I know there is a keyword delete, but I think it is not useful in this case.
Do not write a destructor at all.
std::unique_ptr will do the cleanup for you correctly, and indeed that's the main point of using a unique_ptr in the first place.
Whenever possible, follow the Rule Of Zero: Use smart pointers and containers correctly, and don't declare any destructor, copy constructor, move constructor, copy assignment, or move assignment. (See the link for discussion of the main exception, declaring an interface's destructor virtual, and deleting or defaulting the rest.)
I do not know how to write code for destructor for this member variable
Nothing in particular needs to be written in order to destroy any member variable. All member objects as well as base class objects are destroyed by all destructors after the body of the destructor has been executed.
There is no need to do anything in the body of the destructor, and therefore the implicitly generated destructor is sufficient for the class.
Im reading a book about c++ and in the "Copy Control" section the author teach us to write the operator= in a class telling us that we have to be sure that the method is safe of self-assigment when we have a class that use dynamic memory.
So, imagine that we have a class named "Bank_Client" with a std::string in created with new. The book teach us to do this to avoid the self-assigment case:
Bank_Client& Bank_Client::operator=(const Bank_Client &addres){
std::string *temp = new std::string(*addres.name);
delete name;
name = temp;
return *this;
}
So if i do
Bank_Client bob("Bobinsky");
bob = bob;
The program will not just blow-up. But right when i thought that temp variable was a waste of time the writer of the book show us another way to do it:
Bank_Client& Bank_Client::operator=(const Bank_Client &addres){
if (this != &addres){
delete name;
name = new std::string(*addres.name);
}
return *this;
}
Like if he read my mind. BUT right after that he tell us to never do that, that is better to do it by the other way but never explain why.
Why is the first way better? It is slower, isn't it?
What is the best why to do it?
what about use assert to check there is no self-assignment? (because we dont really need it). And then desactivate it with the corresponding NDEBUG then there is no waste of time in the checking.
The first way is slower if an object is being self-assigned. However, self-assignment is rare. In all other cases, the additional if check from the second approach is a waste.
That said, both approaches are poor ways to implement the copy-assignment operator: neither is exception-safe. If the assignment fails partway through, you'll be left with a half-assigned object in some inconsistent state. It's also bad that it partly duplicates logic from the copy-constructor. Instead, you should implement the copy-assignment operator using the copy-and-swap idiom.
You should use copy and swap. To that end you need a copy constructor (and maybe a move contructor as well if you want to also use move semantics).
class Bank_Client {
// Note that swap is a free function:
// This is important to allow it to be used along with std::swp
friend void swap(Bank_Client& c1, Bank_Client& c2) noexcept {
using std::swap;
swap(c1.name, c2.name);
// ...
}
// ...
};
// Note that we took the argument by value, not const reference
Bank_Client& Bank_Client::operator=(Bank_Client address) {
// Will call the swap function we defined above
swap(*this, adress);
return *this;
}
Now, let's take a look at the client code:
Bank_Client bob("Bobinsky");
// 1. Copy constructor is called to construct the `address` parameter of `operator=()`
// 2. We swap that newly created copy with the current content of bob
// 3. operator=() returns, the temporary copy is destroyed, everything is cleaned up!
bob = bob;
Obviously, you make a useless copy when doing self assignment, but it has the merit of allowing you to reuse logic in your copy constructor (or move constructor) and it is exception safe: nothing is done if the initial copy throws an exception.
The other benefit is you only need one implementation for operator=() to handle both copy and move semantics (copy-and-swap and move-and-swap). If the performance is a big issue you can still have an rvalue overload operator=(Bank_Client&&) to avoid the extra move (though I discourage it).
As a final word, I would also advise you to try to rely on the rule of 0 to the best of your ability as in the example above, should the content of the class change, you must also update the swap function accordingly.
template<class T>
T Stack<T>::pop()
{
if (vused_ == 0)
{
throw "Popping empty stack";
}
else
{
T result = v_[used_ - 1];
--vused_;
return result;
}
}
I didn't understand all of it, or rather I understood none of it, but it was said that this code doesn't work, because it returns by value, I am guessing he was referring to result, and that calls the copy constructor and I have no idea how that's even possible. Can anyone care to explain?
Unlike the code in the question's example, std::stack<T>::pop does not return a value.
That's because if the item type needs to be copied, and the copying throws, then you have an operation failure that has changed the state of the object, with no means of re-establishing the original state.
I.e. the return-a-value-pop does not offer a strong exception guarantee (either succeed or no change).
Similarly, throwing a literal string is unconventional to say the least.
So while the code doesn't have any error in itself (modulo possible typing errors such as vused_ versus v_ etc.), it's weak on guarantees and so unconventional that it may lead to bugs in exception handling elsewhere.
A different viewpoint is that the non-value-returning pop of std::stack is impractical, leading to needlessly verbose client code.
And for using a stack object I prefer to have a value-returning pop.
But it's not either/or: a value-returning convenience method popped can be easily defined in terms of pop (state change) and top (inspection). This convenience method then has a weaker exception guarantee. But the client code programmer can choose. :-)
An improvement within the existing design would be to support movable objects, that is, replace
return result;
with
return move( result );
helping the compiler a little.
↑ Correction:
Actually, the above deleted text has the opposite effect of the intended one, namely, it inhibits RVO (guaranteeing a constructor call). Somehow my thinking got inverted here. But as a rule, don't use move on a return expression that is just the name of a non-parameter automatic variable, because the default is optimization, and the added move can not improve things, but can inhibit an RVO optimization.
Yes, returning by value formally calls the copy constructor. But that's not a problem at all, because in practice, compilers will typically be able to optimize away the additional copy. This technique is called "Return-Value Optimization".
More than the return statement (which can work if the class is movable but not copyable, e.g. you can return std::unique_ptrs), the problem is the copy you do here:
T result = v_[used_ - 1];
To make this copy possible, the type T must be copyable (e.g. T should have public copy constructor - required by the above statement - and copy assignment operator=).
As a side note, throwing a string is really bad: you should throw an exception class, e.g.
throw std::runtime_error("Popping empty stack.");
or just define an ad hoc class for this case and throw it, e.g.:
class StackUnderflowException : public std::runtime_error
{
public:
StackUnderflowException()
: std::runtime_error("Popping empty stack.")
{ }
};
....
throw StackUnderflowException();
If I move-construct a from b, is it still necessary to destruct b, or can I get away without doing so?
This question crossed my mind during the implementation of an optional<T> template. Excerpt:
~optional()
{
if (initialized)
{
reinterpret_cast<T*>(data)->~T();
}
}
optional(optional&& o) : initialized(o.initialized)
{
if (initialized)
{
new(data) T(std::move(*o)); // move from o.data
o.initialized = false; // o.data won't be destructed anymore!
}
}
Of course, I could just replace the bool initialized with a three-valued enumeration that distinguishes between initialized, non-initialized and moved-from. I just want to know if this is strictly necessary.
Yes, it is still necessary to destruct b. A moved from object is a valid, constructed object. In some cases, it may even hold resources that still need to be disposed of. In generic code such as you show, T may not even have a move constructor. You may invoke a copy constructor instead in this case. So you can definitely not assume that ~T() is a no-op and can be elided.
Yes, you do still have to destruct them. One of the designs that can show this flaw is for example, observer-based patterns where one object keeps lists of pointers to another. Not running the destructor won't remove the pointer and the code will crash when it attempts to access an object that no longer exists.
The easier thing to do in your example is just to not set initialized to false in the moved-from object. The value is still defined to be in a valid state after being moved from, and the destructor of the rvalue you're referring to would clean it up with no further intervention.
I'd like to answer 'No' to your question but I'm not sure it's even the right question to ask. Consider the following:
{ // start of scope
T maybe_moved;
if(some_condition) {
T(std::move(maybe_moved));
}
// end of scope
}
T::~T() should obviously be called only once for the maybe_moved object. If a move constructor would call it, how would you make such innocuous code work?