问题描述
我尝试将抽象类(list<shared_ptr<Base>> list_
)的智能指针列表包装到某些类(Item
,Drawer
,Box
)中。然后在主函数中,我有一个map
的{{1}},它不起作用。我找到了解决方法,可以使用Box
,但是我怀疑它只会导致我看不到的错误。如何使其运作?这是代码:
new
结果如下:
#include <iostream>
#include <list>
#include <map>
#include <memory>
using namespace std;
class Base {
public:
virtual int get() = 0;
};
class Derived : public Base {
public:
Derived(int x) { x_ = x; }
int get() override { return x_; }
private:
int x_;
};
class Item {
public:
Item() {
for (int i = 1; i <= 10; i++) {
list_.push_back(make_shared<Derived>(i));
}
}
list<shared_ptr<Base>>& get_list() { return list_; }
private:
list<shared_ptr<Base>> list_;
};
class Drawer {
public:
Drawer(Item& item) : item_(item) {}
void Draw() {
list<shared_ptr<Base>>& list = item_.get_list();
cout << list.size() << ": ";
while (!list.empty()) {
shared_ptr<Base> pointer = dynamic_pointer_cast<Derived>(list.front());
cout << pointer->get() << " ";
list.pop_front();
}
cout << endl;
}
private:
Item& item_;
};
class Box {
public:
Box() : drawer_(item_) {}
void Draw() { drawer_.Draw(); }
private:
Item item_;
Drawer drawer_;
};
int main() {
Box Box;
Box.Draw();
map<int,Box> Boxes; // it doesn't work,why?
for (int i = 0; i < 3; i++) {
Boxes.insert(std::pair<int,Box>(i,Box()));
}
for (auto& b : Boxes) { b.second.Draw(); }
map<int,Box*> pointers; // it does work,why?
for (int i = 0; i < 3; i++) {
pointers.insert(std::pair<int,Box*>(i,new Box()));
}
for (auto& b : pointers) { b.second->Draw(); }
for (auto& b : pointers) { delete b.second; }
}
解决方法
在此行
boxes.insert(std::pair<int,Box>(i,Box()));
您正在对中创建一个临时Box
对象,该对象已移到地图中。
我们称它们为Box1
(创建的临时对象)和Box2
(地图中移动构建的对象)。
在创建Box1
时,它正确地具有一个抽屉,该抽屉引用了Box1
中的项目。
当我们将其移至地图中时,会得到Box2
,它的抽屉仍指向Box1
中的项目。
当我们继续前进时
for (auto& b : boxes) { b.second.Draw(); }
Box1
已被销毁,不再存在。因此,当我们尝试使用对它的引用时,我们使用的是UB的悬空引用。在这种情况下,您得到的结果为0,但同样可能会导致崩溃或任何随机输出。
要解决此问题,我们可以向Box
添加一个副本构造函数来解决此问题。
class Box {
public:
Box() : drawer_(item_) {}
Box(const Box& other) : item_(other.item_),drawer_(item_) {}
void Draw() { drawer_.Draw(); }
private:
Item item_;
Drawer drawer_;
};
现在副本的抽屉将指向正确的项目。
关于带有指针的版本为什么起作用的原因,因为我们正在复制指针,所以相同的对象将一直保留直到被删除。没有对象被移动或复制,只有指针被复制,并且复制的指针仍然指向正确的对象。
, Box() : drawer_(Drawer(item_)) {}
您已经创建了Drawer(item_)
对象,然后为drawer_()
调用了复制构造函数。默认的复制构造函数并不总是处理复杂的数据结构。
尝试
Box() : drawer_(item_) {}
调用Drawer
的普通构造函数