如果存在具有以下方法签名的接口,
class Interface2
{
public:
virtual ~Interface2() {}
virtual uint getId() = 0;
virtual std::string getName() = 0;
};
class Interface
{
public:
virtual ~Interface(){}
virtual std::list<Interface2*> getData(uint id) = 0;
};
因此,getData(uint id)
的基本目的是公开Interface2
接口的内部存储空间
实施具体类Interface
时,它将保留由Interface2
具体类组成的对象的存储。
例如,以下可能是实现这些接口的一种方式
class Interface2Imp : public Interface2
{
public:
uint getId() { return id;}
std::string getName() { return name;}
private:
uint id;
std::string name;
};
class InterfaceImp : public Interface
{
public:
std::list<Interface2*> getData(uint id)
{
std::list<Interface2*> returnList;
std::list<Interface2Imp> & a = storage[id];
for(auto & it : a)
{
returnList.push_back(&it);
}
return returnList;
}
private:
std::map<int, std::list<Interface2Imp> > storage;
}
但是,看看getData(uint id)
的实现方式,我们可以看到它在其内部存储上进行了迭代,然后再次将其填充到接口列表中,这看起来很昂贵(而且很丑陋)。
是否有更好的方法来公开恰好也作为接口公开的接口内部数据?
答案 0 :(得分:2)
按原样进行迭代和复制。该类存储一个X实例的列表,您需要一个指向X实例的指针的列表。这是两件事。您也许可以做一些std :: algorithm之类的事情,但这并不能改变必须完成迭代和克隆的事实。
如何更改接口和实现。使类InterfaceImp
将shared_ptr的列表存储到Interface
,并使getData方法返回shared_ptrs的列表。现在,容器可以直接返回其数据。 (而且您也不会移动原始指针)
话虽如此,您目前拥有的是更好的封装。您没有将内部数据公开给呼叫者,而是在克隆它。
答案 1 :(得分:1)
您所拥有的基本上是multimap<int, Interface2Imp>
,但有一点曲折:外部用户需要查看Interface2*
而不是Interface2Imp&
。
这是 iterator适配器的模型用例。您可以从Boost窃取一个或编写自己的一个。在后一种情况下,您需要一个类,其中包含一个真实的multimap<int, Interface2Imp *>::const_iterator
成员,将大多数迭代器y方法直接转发给该成员,并且仅以稍微不同的方式适应operator *()
和operator ->()
(返回内部成员返回,但向上转换为基类的指针/引用。
如果即使在迭代器适配器的私有部分中也不想提及Interface2Imp
,请使用Pimpl惯用法进一步隐藏它。
答案 2 :(得分:1)
另一种解决方案是将接口实现实例的智能指针存储在第一位置:
class InterfaceImp : public Interface
{
public:
const std::list<std::shared_ptr<Interface2>>& getData(uint id)
{
return storage[id];
}
private:
std::map<uint, std::list<std::shared_ptr<Interface2>> > storage;
}
如果您需要访问Interface2Imp
实例以访问函数或数据,则无法通过Interface2
看到它,您可以始终将static_cast<Interface2Imp*>
与这些实例一起使用,因为您的{{1} }类知道用于实例化InterfaceImp
实现的内容。
甚至有必要,甚至可以使用std::shared_ptr<Interface2>
进行完整的运行时查询,并且不会影响应用程序的性能。