在循环迭代期间将对空指针的引用保存在向量中

时间:2018-06-19 14:02:09

标签: c++ pointers pass-by-reference

我有这样的功能(已给出,不应修改)。

void readData(int &ID, void*&data, bool &mybool) {
    if(mybool)
    {
       std::string a = "bla";
       std::string* ptrToString = &a;
       data = ptrToString;
    }
    else
    {
        int b = 9;
       int* ptrToint = &b;
       data = ptrToint;
    }
}

因此,我想循环使用此函数,并将返回的函数参数保存在向量中(每次迭代)。 为此,我编写了以下结构:

template<typename T>
struct dataStruct {
     int id;
     T** data;   //I first has void** data, but would not be better to 
                 //  have the type? instead of converting myData back 
                 // to void* ?
     bool mybool;
};

我的main.cpp看起来像这样:

int main()
{
    void* myData = nullptr;
    std::vector<dataStruct> vec;  // this line also doesn't compile. it need the typename
    bool bb = false;

    for(int id = 1 ; id < 5; id++) {
        if (id%2) { bb = true; }
        readData(id, myData, bb);       //after this line myData point to a string
        vec.push_back(id, &myData<?>); //how can I set the template param to be the type  myData point to?       
    }
}

或者有没有模板的更好方法吗?我用的是c ++ 11(我不能用c ++ 14)

4 个答案:

答案 0 :(得分:0)

您说的功能无法修改,即readData()是应该提醒您的功能!

由于指针设置为局部变量,因此会导致未定义行为。这意味着当函数终止时,这些指针将成为 dangling 指针。

答案 1 :(得分:0)

让我们暂时忽略readData函数的恶作剧,前提是它只是为了举例(在您的实际用例中不会产生UB)。

您不能直接在std::vector中存储具有不同(静态)类型的值。值得注意的是,dataStruct<int>dataStruct<std::string>是完全无关的类型,您不能按原样存储在相同的向量中。

您的问题归结为“我拥有以类型不安全的方式提供给我的数据,并希望最终获得对它的类型安全的访问”。解决方案是创建一个将类型不安全的数据解析为的数据结构。例如,似乎您在示例数据中加入了intstd::string对的结构(请注意,您的id%2并没有这样做,因为其他情况丢失了,并且布尔值再也不会设置为false,但是我想您想让它交替显示。

因此,让我们将那堆void*转换为结构化数据:

std::pair<int, std::string> readPair(int pairIndex)
{
  void* ptr;
  std::pair<int, std::string> ret;
  // Copying data here.
  readData(2 * pairIndex + 1, ptr, false);
  ret.first = *reinterpret_cast<int*>(ptr);
  readData(2 * pairIndex + 2, ptr, true);
  ret.second = *reinterpret_cast<std::string*>(ptr);
}

void main()
{
  std::vector<std::pair<int, std::string>> parsedData;
  parsedData.push_back(readPair(0));
  parsedData.push_back(readPair(1));
}

Demo

(为简洁起见,我从readData()签名中删除了引用-通过将临时表达式存储在变量中,您将获得相同的效果。)

通常来说:id与预期数据类型之间的任何关系都应该变成数据结构-否则,只有在知道当前ID和当前ID的情况下,才可以推断数据条目的类型关系,这正是您应该封装在数据结构中的东西。

答案 2 :(得分:0)

您的readData不是有用的函数。任何尝试使用它产生的东西都会产生不确定的行为。

是的,没有模板就可以大致完成您要的内容。要有意义地做到这一点,您有两种选择。 “老派”方法是将数据存储在带标签的联合中:

struct tagged_data {
    enum { T_INT, T_STR } tag;
    union {
        int x;
        char *y;
    } data;
};

这使您可以存储字符串或整数,并且可以设置tag来告诉您特定tagged_data项包含哪些项。然后,(至关重要的)将字符串存储到其中时,将动态分配它指向的数据,因此它将保持有效,直到您明确释放数据为止。

不幸的是,(至少在有内存的情况下)C ++ 11不支持在联合中存储非POD类型,因此,如果采用这种方式,则必须如上所述使用char * ,而不是实际的std::string

消除(大部分)这些限制的一种方法是使用基于继承的模型:

class Data {
public:
    virtual ~Data() { }
};


class StringData : public Data {
    std::string content;
public:
    StringData(std::string const &init) : content(init) {}
};

class IntData : public Data {
    int content;
public:
    IntData(std::string const &init) : content(init) {}
};

这有点不完整,但是我认为可能足以给出一般的想法-您将有一个指向基类的指针数组(或向量)。要插入数据,您需要创建一个StringDataIntData对象(动态分配),然后将其地址存储到Data *的集合中。当您需要找回一个时,可以使用dynamic_cast(除其他事项外)来确定它是从哪一个开始的,然后安全地返回该类型。一切都有些丑陋,但确实有效。

即使在C ++ 11中,您也可以使用基于模板的解决方案。例如,Boost::variant可以很好地完成这项工作。这将提供重载的构造函数和值语义,因此您可以执行以下操作:

boost::variant<int, std::string> some_object("input string");

换句话说,如果花费时间和精力完成上面概述的基于继承的代码,这将是很不错的选择,除了它明显更干净外,因为它摆脱了存储指针的要求对于基类,请使用dynamic_cast检索正确类型的对象,依此类推。简而言之,这是解决该问题的正确方法(除非/除非您可以升级到较新的编译器,否则请使用std::variant)。

答案 3 :(得分:-2)

除了注释/回复中描述的给定代码中的问题外。 我正在尝试回答您的问题

 vec.push_back(id, &myData<?>); //how can I set the template param to be the type  myData point to? 

在您需要按照以下说明修改vec定义

vector<dataStruct<void>> vec;

现在您可以在vector中简单推送元素

vec.push_back({id, &mydata, bb});

我试图修改您的代码,使其可以正常工作

#include<iostream>
#include<vector>

using namespace std;

template<typename T>
struct dataStruct
{
        int id;
        T** data;
        bool mybool;
};

void readData(int &ID, void*& data, bool& mybool)
{
        if (mybool)
        {
                data = new string("bla");
        }
        else
        {
                int b = 0;
                data = &b;
        }
}

int main ()
{
        void* mydata = nullptr;
        vector<dataStruct<void>> vec;
        bool bb = false;
        for (int id = 0; id < 5; id++)
        {
                if (id%2) bb = true;
                readData(id, mydata, bb);
                vec.push_back({id, &mydata, bb});
        }



  }