C++0x move constructor gotcha [closed]
Edit: I re-asked this same question (after fixing the problems noted with this question) here: Why does this C++0x program generates unexpected output?
The basic idea is that pointing to moveable things may net you some odd results if you aren't careful.
The C++ move constructor and move assignment operator seem like really positive things. And they can be used in situations where the copy constructor makes no sense because they don't require duplicating resources being pointed at.
But there are cases where they will bite you if you aren't careful. And this is especially relevant as I've seen proposals to allow the compiler to generate default implementations of the move constructor. I will provide a link to such if someone can give me one.
So, here is some code that has some flaws that may not be completely obvious. I tested the code to make sure it compiles in g++ with the -std=gnuc++0x
flag. What are those flaws and how would you fix them?
#if (__cplusplus <= 199711L) && !defined(__GXX_EXPERIMENTAL_CXX0X__)
#error This requires c++0x
#endif
#include <unordered_set>
#include <vector>
#include <utility>
#include <algorithm>
class ObserverInterface {
public:
virtual ~ObserverInterface() {}
virtual void observedChanged() = 0;
virtual void observedGoingAway() = 0;
};
class Observed {
private:
typedef ::std::unordered_set<ObserverInterface *> obcontainer_t;
public:
Observed() {}
Observed(const Observed &) = delete;
const Observed &operator =(const Observed &b) = delete;
// g++ does not currently support defaulting the move constructor.
Observed(Observed &&b) : observers_(::std::move(b.observers_)) { }
// g++ does not currently support defaulting move assignment.
const Observed &operator =(Observed &&b) {
observers_ = ::std::move(b.observers_);
return *this;
}
virtual ~Observed() {
for (auto i(observers_.begin()); i != observers_.end(); ++i) {
(*i)->observedGoingAway();
}
}
void unObserve(ObserverInterface *v) {
auto loc(observers_.find(v));
if (loc != observers_.end()) {
observers_.erase(loc);
}
}
void changed() {
if (!observers_.empty()) {
// Copy observers_ to bector so unObserve works
::std::vector<ObserverInterface *> tmp;
tmp.reserve(observers_.size());
tmp.assign(observers_.begin(), observers_.end());
for (auto i(tmp.begin()); i != tmp.end(); ++i) {
(*i)->observedChanged();
}
}
}
private:
obcontainer_t observers_;
};
class Observer : public ObserverInterface {
public:
Observer() {}
Observer(const Observer &) = delete;
const Observer &operator =(const Observer &b) = delete;
// g++ does not currently support defaulting the move constructor.
Observer(Observer &&b) : observed_(b.observed_) {
b.observed_ = 0;
return *this;
}
// g++ does not currently support defaulting move assignment.
const Observer &operator =(Observer &&b) {
observed_ = b.observed_;
b.observed_ = 0;
return *this;
}
virtual ~Observer() {
if (observed_) {
observed_->unObserve(this);
observed_ = 0;
}
}
virtual void observedChanged() {
doStuffWith(observed_);
}
virtual void observedGoingAway() {
observed_ = 0;
}
private:
Observed 开发者_JS百科*observed_;
// Defined elsewhere
void doStuffWith(Observed *);
};
There are lots of problems with the code.
Observer::observed_
is left uninitialized in the default constructor, leading to an undefined behavior when the destructor gets called.- No value but 0 is ever assigned to
Observer::observed_
, making the variable superfluous. - Even if there was a way to associate an observer with an observed, you're not re-registering when moving the observer.
- You're trying to return a value from observer's move constructor.
- Boost.Signals already solves whatever problem you're trying to solve.
- It is more idiomatic to return non-const reference from assignment operators.
精彩评论