gpt4 book ai didi

c++ - 创建库以覆盖迭代器的 operator*() - 风险悬空指针

转载 作者:IT老高 更新时间:2023-10-28 23:02:55 27 4
gpt4 key购买 nike

我正在尝试创建自己的 boost::adaptors::transformed .

这里是相关的boost code .

这是它的用法(从 a SO answer by LogicStuff 修改):-

C funcPointer(B& b){ 
//"funcPointer" is function convert from "B" to "C"
return instance-of-C
}

MyArray<B> test; //<-- any type, must already have begin() & end()

for(C c : test | boost::adaptor::transformed(funcPointer)) {
//... something ....
}

结果将与以下内容相同:-

for(auto b : test) {
C c = funcPointer(b);
//... something ...
}

我的尝试

我创建了 CollectAdapter旨在像 boost::adaptor::transformed 一样工作.
在大多数常见情况下都可以正常工作。

这里是完整的 demoback up .(与下面的代码相同)

有问题的部分是CollectAdapter - 我图书馆的核心。
我不知道是否应该缓存 collection_ 按指针按值

CollectAdapter 封装底层 collection_ (例如指向 std::vector<> 的指针):-

template<class COLLECTION,class ADAPTER>class CollectAdapter{
using CollectAdapterT=CollectAdapter<COLLECTION,ADAPTER>;
COLLECTION* collection_; //<---- #1 problem? should cache by value?
ADAPTER adapter_; //<---- = func1 (or func2)
public: CollectAdapter(COLLECTION& collection,ADAPTER adapter){
collection_=&collection;
adapter_=adapter;
}
public: auto begin(){
return IteratorAdapter<
decltype(std::declval<COLLECTION>().begin()),
decltype(adapter_)>
(collection_->begin(),adapter_);
}
public: auto end(){ ..... }
};

IteratorAdapter (上面使用)封装底层迭代器,改变operator*的行为:-

template<class ITERATORT,class ADAPTER>class IteratorAdapter : public ITERATORT {
ADAPTER adapter_;
public: IteratorAdapter(ITERATORT underlying,ADAPTER adapter) :
ITERATORT(underlying),
adapter_(adapter)
{ }
public: auto operator*(){
return adapter_(ITERATORT::operator*());
}
};

CollectAdapterWidget (在下面使用)只是构造 CollectAdapter 的辅助类-实例。

可以这样使用:-

int func1(int i){   return i+10;   }
int main(){
std::vector<int> test; test.push_back(5);
for(auto b:CollectAdapterWidget::createAdapter(test,func1)){
//^ create "CollectAdapter<std::vector<int>,func1>" instance
//here, b=5+10=15
}
}

问题

上面的代码在大多数情况下都可以正常工作,除非 COLLECTION是一个临时对象。

更具体地说,当我创建 适配器的适配器 ... 时,可能会出现悬空指针。

int func1(int i){   return i+10;    }
int func2(int i){ return i+100; }
template<class T> auto utilityAdapter(const T& t){
auto adapter1=CollectAdapterWidget::createAdapter(t,func1);
auto adapter12=CollectAdapterWidget::createAdapter(adapter1,func2);
//"adapter12.collection_" point to "adapter1"
return adapter12;
//end of scope, "adapter1" is deleted
//"adapter12.collection_" will be dangling pointer
}
int main(){
std::vector<int> test;
test.push_back(5);
for(auto b:utilityAdapter(test)){
std::cout<< b<<std::endl; //should 5+10+100 = 115
}
}

这将导致运行时错误。这里是 the dangling-pointer demo .

在实际使用中,如果界面更棒,例如使用 |运算符(operator),这个错误将更难被发现:-

//inside "utilityAdapter(t)"
return t|func1; //OK!
return t|func1|func2; //dangling pointer

问题

如何改进我的库以修复此错误,同时保持 性能 & 健壮性 & 可维护性 接近相同水平?

也就是说,如何缓存COLLECTION的数据或指针(可以是适配器真正的数据结构)?

或者,如果从头开始编码(比修改我的代码)更容易回答,那就去做吧。 :)

我的解决方法

当前代码缓存按指针
变通办法的主要思想是改为按值缓存

解决方法 1(始终“按值”)

适配器缓存COLLECTION .
这是主要的变化:-

COLLECTION collection_;    //<------ #1 
//changed from .... COLLECTION* collection_;

缺点:-

  • 整个数据结构(例如 std::vector )将被值(value)复制 - 浪费资源。
    (直接用于 std::vector 时)

解决方法 2(两个版本的库,最好吗?)

我将创建 2 个版本的库 - AdapterValueAdapterPointer .
我还必须创建相关的类(WidgetAdapterIterator 等)。

  • AdapterValue - 按值(value)。 (专为 utilityAdapter() 设计)
  • AdapterPointer - 通过指针。 (专为 std::vector 设计)

缺点:-

  • 大量重复代码 = 可维护性低
  • 用户(编码人员)必须非常清楚选择哪一个 = 低稳健性

解决方法 3(检测类型)

我可以使用模板特化来做到这一点:-

If( COLLECTION is an "CollectAdapter" ){ by value }  
Else{ by pointer }

缺点:-

  • 许多适配器类之间不能很好地配合。
    它们必须相互识别:已识别 = 应按值缓存

抱歉,帖子太长了。

最佳答案

我个人会选择模板特化——但是,不是特化原始模板,而是一个嵌套类:

template<typename Collection, typename Adapter>
class CollectAdapter
{
template<typename C>
class ObjectKeeper // find some better name yourself...
{
C* object;
public:
C* operator*() { return object; };
C* operator->() { return object; };
};
template<typename C, typename A>
class ObjectKeeper <CollectAdapter<C, A>>
{
CollectAdapter<C, A> object;
public:
CollectAdapter<C, A>* operator*() { return &object; };
CollectAdapter<C, A>* operator->() { return &object; };
};

ObjectKeeper<Collection> keeper;

// now use *keeper or keeper-> wherever needed
};

然后,外部类通过始终使用指针来涵盖这两种情况,而嵌套类将差异隐藏起来。

当然,不完整(你还需要添加适当的构造函数,例如,外部和内部类),但它应该给你的想法......

您甚至可以允许用户选择她/他是否想要复制:

template<typename Collection, typename Adapter, bool IsAlwaysCopy = false>
class CollectAdapter
{
template<typename C, bool IsCopy>
class ObjectWrapper // find some better name yourself...
{
C* object;
public:
C* operator*() { return object; };
C* operator->() { return object; };
};
template<typename C>
class ObjectWrapper<C, true>
{
C object;
public:
C* operator*() { return &object; };
C* operator->() { return &object; };
};

// avoiding code duplication...
template<typename C, bool IsCopy>
class ObjectKeeper : public ObjectWrapper<C, IsCopy>
{ };
template<typename C, typename A, bool IsCopy>
class ObjectKeeper <CollectAdapter<C, A>, IsCopy>
: public ObjectWrapper<CollectAdapter<C, A>, true>
{ };

ObjectKeeper<Collection> keeper;
};

关于c++ - 创建库以覆盖迭代器的 operator*() - 风险悬空指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42360760/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com