我遇到了这样一个问题。
我需要设计一个接口类,看起来像是
struct IIDs
{
....
const std::set<int>& getAllIDs() = 0; //!< I want the collection of int to be sorted.
}
void foo()
{
const std::set<int>& ids = pIIDs->getAllIDs();
for(std::set<int>::const_iterator it = ids.begin();....;..) {
// do something
}
}
我认为返回std的容器有点不合适,因为它会强制实现使用std :: set来存储ID的值,但如果我把它写成如下:
struct IIDs
{
....
int count() const = 0;
int at(int index) = 0; //!< the itmes should be sorted
}
void foo()
{
for (int i = 0; i < pIIDs->count(); ++i) {
int val = pIIDs->at(u);
...
}
}
我发现std的容器都没有提供这些请求:
所以我只需要使用示例.1,这些是否可以接受?
答案 0 :(得分:3)
通常不应跨DLL边界使用STL容器和模板代码。
返回复杂类型(如STL容器)时必须记住的是,如果您的调用跨越了运行不同内存管理器的两个不同DLL(或DLL和应用程序)之间的边界,您的应用程序很可能会崩溃。
构成STL代码的模板将在实现DLL中执行,从而创建容器所使用的所有内存。稍后当它在您的调用代码中留下范围时,您自己的内存管理器将尝试释放它不拥有的内存,从而导致崩溃。
如果您知道您的代码不会跨越DLL边界,并且只会在单个内存管理器的上下文中调用,那么就内存管理而言,您就可以了。
但是,即使在您只返回引用的情况下,例如上面的示例,容器的生命周期将完全由接口实现代码管理,除非您知道完全相同的版本STL和完全相同的编译器和链接器设置用于编译作为调用者的实现,您要求麻烦。
答案 1 :(得分:1)
我看到的问题是你是通过const引用返回集合,这意味着你有一个该集合类型的成员并返回对它的引用,如果你将一个局部变量返回给该函数(无效的内存访问问题)。
如果它是一个成员变量,那么最好提供对开始和结束迭代器的访问。如果是局部变量,则可以按值返回(C ++ 11应该优化并且不复制任何内容)。如果它的DLL边界尝试所有意味着不使用任何C ++类型,只使用C类型。
答案 2 :(得分:1)
在设计和良好的通用代码方面,更喜欢STL方式:返回迭代器,让容器类型为IIDs
的实现细节,并使用typdef
s
struct IIDs
{
typedef std::set<int> Container;
typedef Container::iterator IDIterator;
// We only expose iterators to the data
IDIterator begin(); //!< I want the collection of int to be sorted.
IDIterator end();
// ...
};
答案 3 :(得分:0)
有各种方法:
IIDs
实现上客户端代码的耦合并确保在IIDs
对象仍然存在时完成迭代,则使用访问者模式:调用代码必须提供一些函数依次为每个成员元素调用,并且不负责迭代本身访客示例:
struct IIDs
{
template <typename T>
void visit(T& t)
{
for (int i : ids_) t(i);
}
...
private:
std::set<int> ids_;
};