所以我认为这是一个非常常见的涉及对象的C ++问题 组成。问题领域是这样的:动画gif可以有很多 框架和绘制框架取决于Gif的其他内容 帧。
所以我有这个模型(这个问题很简单,但应该 示出):
class Gif {
std::vector<Frame> _frames;
// Makes Frame objects from file and puts them in _frames
Gif(const char* file){...};
// For clarity, works and copies the _frames member
Gif(const Gif& other) = default;
};
class Frame {
Frame(...){
// Make a frame object
}
void draw(const Gif& context){
// draws self considering context's other frames
}
};
这是有效的,但为了简单起见并避免绘制Frame
错误的上下文,我希望draw
方法不会采用context
参数。所以我想到用const
创建框架
参考成员_context
:
class Frame {
const Gif& _context;
// Make a frame object
Frame(const Gif& context) _context(context){...}
// Explicit default copy-constructor, for clarity. Breaks horribly
// when called from Gif's copy constructor, since the new Frame will
// reference the wrong context, which might be deleted.
Frame(const& Frame other) = default;
void draw(){
// draws self considering _context's other frames
}
};
这会编译但在复制Gif
时可怕地破坏。 Frame
新Gif
的对象是副本,但它们引用了
错误的上下文,通常已被删除。
我认为const参考成员可能是一个坏主意
你打算复制的对象......我应该使用指针吗?
为自定义Frame
复制构造函数体内的新Gif
明确重置它?
如果是这样,那是什么样的 指针(原始/智能)?是不是有一个很好的C ++ 11制作技术 这种情况发生了#34;自动&#34;?
或者我应该打破循环依赖以及如何?
编辑(感谢KillianDS):要清楚,你从gif1开始,有一些框架(frame1_1,frame1_2,...)指向gif1,现在你想用复制的框架将gif1复制到gif2(frame2_1,frame2_2 ,...)但是那指向gif2?答案 0 :(得分:1)
基本上你想让复制的帧指向复制的gif。这不会在两个级别上使用默认的复制构造函数,因为您不会盲目地复制对象。你的Gif
拷贝构造函数可能需要变成这样的东西:
class Gif {
std::vector<Frame> _frames;
Gif(const Gif& other) : some_var(other.some_var), _frames(other.frames) ...
{
for(auto& frame: _frames)
{
frame.update_context(this);
}
}
};
现在的问题是,在Frame类中,您使用了无法重新设置的引用,因此您无法更新它们。如果你的用例很简单,通常你可以用一个裸指针逃脱:
class Frame {
const Gif* _context;
// Make a frame object
Frame(const Gif* context) _context(context){...}
Frame(const& Frame other) = default;
void update_context(const Gif* context) { _context = context; }
};
为什么是裸指针而不是智能指针:
std::nullptr
。你应该在什么时候认为这是一个智能指针(在这种情况下为std::weak_ptr
),如果第2点不再正确。如果一个框架以gif开头且gif在某个地方丢失,但框架没有,那么使用weak_ptr
更容易,你可以实际检查gif上下文是否仍然存在。使用裸指针时,您应始终将指针指向nullptr
,否则这更容易出错。
答案 1 :(得分:0)
所以我想到了更多关于这一点,并来到这个工作的例子。
这实际上是第二个版本,第一个使用unique_ptr
和push_back
。此版本使用emplace_back
并根据@ KillianDS的建议完美转发。禁止在不经过Gif::add_frame
的情况下创建新框架也会使用建议here的FrameFriend
。
class Gif;
class FrameFriend {
friend class Gif;
private:
FrameFriend(){}
};
class Frame {
public:
int _number;
const Gif& _context;
// Constructors are public but a FrameFriend is needed and only Gif
// can make one. And frames can't be copied or copy-assigned.
Frame(int number, const Gif& gif, const FrameFriend&) : _number(number),_context(gif) {}
Frame(Frame&& other) = default;
Frame operator=(Frame other) = delete;
Frame(const Frame& another) = delete;
// ... but they can be constructed from ther frames
Frame(const Frame& another, const Gif& gif, const FrameFriend&)
: _number(another._number),
_context(gif) {}
void draw(){}
};
class Gif {
// Each gif owns its frames exclusively
std::vector<Frame> _frames;
// Privately, frames can be copied from other gifs
// calling the appropriate private constructor of Frame
void copy_frames(const Gif& other){
_frames.clear();
_frames.reserve(other._frames.size());
for (auto& f: other._frames) {
_frames.emplace_back(f, *this, FrameFriend());
}
}
public:
// Public constructors
Gif(){};
// Public constructors. Copying and assigning both call
// copy_frames() which updates the the gif context
Gif(const Gif& other) { copy_frames(other); }
Gif& operator=(Gif& other) { copy_frames(other); return *this;}
// Move constructor can be the default
Gif(Gif&& other) = default;
// Add a frame
void add_frame(){
_frames.emplace_back(_frames.size(), *this, FrameFriend());
}
// The set of frames can be publicly accessed and frames can even be modified
// individually, but as no
std::vector<Frame>& frames() { return _frames; };
};