引用、向量、std::vector::at 和易于使用的接口
Posted
技术标签:
【中文标题】引用、向量、std::vector::at 和易于使用的接口【英文标题】:References, vectors, std::vector::at and easily usable interfaces 【发布时间】:2014-01-17 19:39:39 【问题描述】:最近我一直在处理一些音频代码,虽然理解这个问题不需要领域经验,但我认为这可能有助于理解我的意图。
我有一个带有音频通道对象的 std::vector 的控制器对象。该向量中的每个音频通道都用于保持每个通道的状态(播放,未播放......)。我正在使用的一个特定库与回调一起使用,因此您播放声音时将通道标记为“正在播放”,并且在播放完成后会进行回调,因此您可以将其标记为“空闲”。出于本示例的目的,我们假设 Audio_channel::play_something() 存在并按预期执行:标记为正在播放并开始播放声音,等待声音完成时的回调。
不管怎样,大多数时候你可以通过控制器对象来播放声音,像这样:
int channel=0;
audio_controller.play_some_sound(channel); //It would really do something like this->channels.at(0).play_something();
当然,因为 audio_controller 真正拥有这些 Audio_channels,所以它会起作用。
有时您会想要一个完全属于自己的频道并且会这样做:
Audio_channel c=audio_controller.get_me_this_channel(0); //This returns the channel by reference with vector.at(). Try and catch blocks are ommited.
c.play_something();
虽然它会起作用(因为它包装了一个不知道这些抽象的库),但我知道这个 Audio_channel 是原始的副本,因此不能从控制器查询(因为任何更改都不会'不反映)。
我总是可以去:
Audio_channel& c=audio_controller.get_me_this_channel(0);
c.play_something();
这一次我得到了真正的交易,任何变化都反映在任何地方......事情是,从“调用代码”的角度来看,在那里强制引用可能是违反直觉的 - 特别是在不会产生错误的情况下编译器,因为不存在错误。总是有指针,但我想把它们隐藏在表面之下。我想智能指针也是一种选择,但我想再次让它尽可能接近原始代码。
您还可以在这里看到哪些我可能遗漏的选项?我考虑将 Audio_channel 包装到其他执行脏引用工作并返回这个其他接口的副本的东西中......我会进入很多代码重定向和方法,这些方法只是调用引用通道的方法但是很好...... .
如前所述,有什么我可能会遗漏的吗?我正在使用最近的 gcc 编译器,因此允许使用 C++X11 热门内容。非常感谢。
【问题讨论】:
制作get_me_this_channel
返回参考
实际原型是canal_audio& obtener_canal(int) throw();它确实已经返回了引用。还是谢谢。
“特别是编译器不会发出错误,因为不存在错误”如果您使 Audio_channel 不可复制,就会出现这种情况。这是一个选择吗?这对我来说似乎很有意义,因为您还说使用副本有其自身的问题。
除非您使用指针,否则只有两种方法可以做到这一点,即您的方法或audio_controller.get_me_this_channel(0).play_something();
stijn,我之前也尝试过,但如果使其不可复制,我将无法填充向量。
【参考方案1】:
编译器不会产生任何错误,因为不存在错误
如果这里要报错,改Audio_channel的设计,用C++11可以写:
class Audio_channel
Audio_channel( const Audio_channel& ) = delete;
Audio_channel& operator=( const Audio_channel& ) = delete;
...
;
Audio_channel c=audio_controller.get_me_this_channel(0);
这将导致编译错误。现在调用代码被强制通过引用获取返回值。
如果你真的想要一个价值语义,就像你的回答所暗示的那样,你已经走在正确的轨道上了。您正在将 proxy pattern 实现为音频通道的引用。比如:
class Audio_channel_proxy
public:
Audio_channel_proxy( Audio_channel& c ) : m_channel( c )
void play_something() m_channel.play_something();
...
private:
Audio_channel &m_channel;
默认情况下,我更喜欢第一种方法,强制引用是很常见的,自文档化且易于实现。
第二种方法并不常见,但也不少见。它有一个潜在的陷阱。特别是如果您使用 Audio_channel 重命名代理:它不是自记录的。
Audio_channel c=audio_controller.get_me_this_channel(0);
此行表明该频道的唯一所有权,因为它是按价值复制的。但实际上它只是一个频道的别名,其他人也可以修改。所以你最好把它记录好(我会从命名开始)。我想你已经注意到了。每次看到这种方法,至少有一个人做错了,直到他吸取教训,包括我在内。此外,您需要在代理中实现和维护 Audio_channel 的接口。只是为了不被强迫写参考的语法糖,这并不值得。
另一方面,如果您希望通过 audio_controller 调用或其他人直接访问它的通道的不同行为(或至少识别),则代理具有实际价值。但只有在需要时才开始使用它。
【讨论】:
支持,因为我真的很感激时间和知识:)。我对代码进行了一些修改以适应删除这些方法,尽管它可以自行工作,但在填充矢量时我再次撞到了墙上......没有复制功能,这些东西是不可分配的,我不能让它过去。很遗憾,因为我真的需要它们的向量(在程序运行时可能会有一个变化的数字)......更改内部代码,因此有一个 std::vector 可以通过 return * vector.at(index) 使引用被强制执行。当一些其他对象必须临时跟踪可能稍后更改的通道时,它也会产生一些奇怪的习惯用法(强制指针,但同样,有意义并且存在诸如存储索引之类的替代方案)。我不知道我会选择哪种解决方案,但非常感谢! @TheMarlboroMan 如果你想要一个不可复制对象的向量,那么 C++11 的做法是使用 std::vector<:reference_wrapper>>。我必须承认到目前为止我还没有真正使用过它,但也许这对你来说是一个起点。 稍后可能会看一下...到目前为止,我有一个可行的解决方案,答案如下,但我想了解更多信息。非常感谢!【参考方案2】:我会在这里回答自己,但仍会接受任何建议,因为这个解决方案似乎是正确的,尽管不是最佳的。
我所做的是将 Audio_channel 转换为 Audio_channel_nonpublic 并将其私有给控制器。控制器现在返回一个围绕 Audio_channel_nonpublic(恰当地命名为 Audio_channel)的包装类,它具有对原始的引用并获得正确的复制内容。新的 Audio_channel 实现了与 Audio_channel_nonpublic 相同的公共接口,并将每个调用转发到引用的对象。这种方式根本不需要更改客户端代码。
结果有效,但是有很多前向声明和代码块,几个月后我再回来看它们时可能会有点混乱......现在是记录时间!!。
感谢您的 cmets。
【讨论】:
以上是关于引用、向量、std::vector::at 和易于使用的接口的主要内容,如果未能解决你的问题,请参考以下文章
查找 std::vector.at() 抛出 std::out_of_range 的位置