C++ 引用、向量、std::vector::at和易于使用的接口

C++ 引用、向量、std::vector::at和易于使用的接口,c++,vector,reference,C++,Vector,Reference,最近我一直在使用一些音频代码,虽然不需要领域经验来理解这个问题,但我认为这可能有助于理解我的意图 我有一个控制器对象,带有音频通道对象的std::vector。该向量中的每个音频通道用于保持每个通道的状态(播放,不播放…)。我正在使用的一个特定的库与回调一起工作,因此您可以播放一个声音,将频道标记为“正在播放”,播放完毕后,会发出一个回调,以便您可以将其标记为“空闲”。在本例中,假设Audio_channel::play_something()存在并按预期执行:标记为playing并开始播放声音

最近我一直在使用一些音频代码,虽然不需要领域经验来理解这个问题,但我认为这可能有助于理解我的意图

我有一个控制器对象,带有音频通道对象的std::vector。该向量中的每个音频通道用于保持每个通道的状态(播放,不播放…)。我正在使用的一个特定的库与回调一起工作,因此您可以播放一个声音,将频道标记为“正在播放”,播放完毕后,会发出一个回调,以便您可以将其标记为“空闲”。在本例中,假设Audio_channel::play_something()存在并按预期执行:标记为playing并开始播放声音,等待声音完成后的回调

无论如何,大多数情况下,您可以通过控制器对象播放声音,如下所示:

int channel=0;
audio_controller.play_some_sound(channel); //It would really do something like this->channels.at(0).play_something();
当然,它会起作用,因为音频控制器确实拥有这些音频通道

有时,您希望自己拥有一个频道,并会这样做:

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& c=audio_controller.get_me_this_channel(0);
c.play_something();

这一次我得到了真正的交易,任何变化都会反映在每个地方。。。问题是,从“调用代码”的角度来看,强制引用可能是违反直觉的——特别是在编译器不会出现错误的地方,因为不存在错误。总是有指针,但我想把它们隐藏起来。我想智能指针也是一种选择,但我还是希望它尽可能接近原始代码

在这里你还能看到我可能遗漏的其他选项吗?。我想把音频频道包装成其他的东西,做肮脏的参考工作,并返回这个其他接口的副本。。。我将进入许多代码重定向和方法,这些方法只调用引用通道的方法,但是


正如我所说的,我有没有遗漏什么?。我正在使用一个最新的gcc编译器,因此允许使用C++X11热门内容。非常感谢。

我将在这里回答自己的问题,但我仍然愿意接受任何建议,因为这个解决方案似乎是正确的,尽管不是最优的

我所做的是将音频频道转换为非公开的音频频道,并将其保密给控制器。控制器现在返回一个围绕Audio_channel_nonpublic(恰当地命名为Audio_channel)的包装器类,该类引用了原始通道,并正确地复制了内容。新的Audio_channel实现与Audio_channel_nonpublic相同的公共接口,并将每个调用转发给引用的对象。这样,根本不需要更改客户机代码

结果是可行的,但是有很多前向声明和代码块,当我几个月后回到它们时可能会有点混乱。。。这是记录时间

谢谢您的评论。

编译器不会出现任何错误,因为不存在任何错误

如果您想在此处出错,请更改音频通道的设计,使用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);
这将导致编译错误。现在,调用代码被迫通过引用获取返回值

如果你真的想要一个语义值,就像你的答案所暗示的那样,你已经走上了正确的道路。您正在实现对音频通道引用的。比如:

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 c=audio_controller.get_me_this_channel(0);
这一行表示频道的独特所有权,因为它是按价值复制的。但实际上它只是一个频道的别名,其他人也可以修改。所以,您最好将其记录好(我将从命名开始)。我想你已经注意到了。每次我看到这个方法,至少有一个人弄错了,直到他吸取了教训,包括我。此外,您需要在代理中实现和维护音频通道的接口。只是为了不被强迫写一个引用的语法糖,这真的不值得


另一方面,如果通过音频控制器调用代理或其他人直接访问代理,则代理具有实际值,前提是您希望频道的不同行为(或至少识别)。但只有在需要时才开始使用它。

制作
获取此频道
返回参考实际原型为Canal_audio&obtener_Canal(int)throw();它已经返回了引用。无论如何,谢谢。“特别是在编译器不会出现错误的情况下,因为不存在错误”,如果您使音频通道不可复制,就会出现错误。这是一种选择吗?这对我来说似乎是有道理的,因为你也说使用副本有它自己的问题。除非你使用指针,否则只有两种方法可以做到这一点,即你使用的方式或
音频控制器。获取此频道(0)。播放某物()
stijn,我以前也尝试过,但如果使其不可复制,我将无法填充vector.Upvote,因为我非常感谢时间和知识:)。我对代码进行了一点修改,以适应删除这些方法的需要,尽管它自己可以工作,但当它填充向量时,我再次撞到了墙上。。。由于没有复制功能,这些东西是不可分配的,我不能让它过去。这是一个耻辱,因为我真的需要有一个向量的他们(可能有一个不断变化的数字作为程序运行)。。。更改内部代码以使std::vector与return*vector一起访问。at(索引)将强制执行引用。这也是一个很好的选择