【问题标题】:C++ vector<Data> to vector<IData>C++ 向量<Data> 到向量<IData>
【发布时间】:2019-01-29 17:09:39
【问题描述】:

我在 C++ 中有一个问题,如何将 vector&lt;Data&gt; 隐式转换为 vector&lt;IData&gt;

我对此的唯一答案是创建一个新的vector&lt;IData&gt; 并复制vector&lt;Data&gt; 的每个元素。

我想知道 C++ 中是否有优雅的解决方案来解决这种情况。

代码如下:

#include <iostream>
#include <vector>

using namespace std;

class IData
{
    public:
        virtual int getNumber() = 0;
};

class DataA : public IData
{
    public:
        DataA(int value) : _value(value) { }

        virtual int getNumber() override
        {
            return _value;
        }

    private:
        int _value = 0;
};

class DataB : public IData
{
    public:
        DataB(int value) : _value(value) { }

        virtual int getNumber() override
        {
            return _value;
        }

    private:
        int _value = 0;
};

int calculateDataSum(vector<IData> datas)
{
    int sum;
    for (int i = 0; i < datas.size(); i++)
    {
        sum += datas[i].getNumber();
    }
    return sum;
}

int main()
{
    DataA dA0(10);
    DataA dA1(20);
    DataB dB0(100);
    DataB dB1(200);

    vector<DataA> datasA;
    datasA.push_back(dA0);
    datasA.push_back(dA1);

    vector<DataB> datasB;
    datasB.push_back(dB0);
    datasB.push_back(dB1);

    int resultA = calculateDataSum(datasA);
    int resultB = calculateDataSum(datasB);

    cout << resultA << endl;
    cout << resultB << endl;

    return 0;
}

【问题讨论】:

  • DataIData 有什么关系?你打算用vector&lt;IData&gt;做什么?
  • 正方形数组不是矩形数组。此外,如果 IData 是一个接口,则意味着您应该使用 pointers 向量而不是实例化实例。也许您可以更新您的问题以显示 Data 和 IData 的声明。
  • 具体代码很好,但将其包含在问题中而不是链接到它。本网站的规则中明确说明了这一点,如果您遵守这些规则,您更有可能获得帮助。
  • 没关系,我们可以使用 IVehicle 和 Car / Skateboard ... 并且有一个函数(只有一个)能够获取 IVehicle 列表并避免使用函数 (car) (skateboard) ...
  • 我有点怀疑你不能在这样的类定义中初始化私有成员变量。我还想知道您认为该过程将如何处理您的语句“int resultA = calculateDataSum(datasA);”下一行当你没有提供完成工作的机器时......

标签: c++ interface containers


【解决方案1】:

最明显的方法是使用std::vector&lt;std::unique_ptr&lt;IData&gt;&gt;

#include <memory>

int calculateDataSum(vector<std::unique_ptr<IData>>& datas)
{
    int sum;
    for (auto& data : datas)
    {
        sum += data->getNumber();
    }
    return sum;
}

int main()
{
    vector<std::unique_ptr<IData>> datasA;
    datasA.push_back(std::unique_ptr<IData>(new DataA(10)));
    datasA.push_back(std::unique_ptr<IData>(new DataA(20)));

    vector<std::unique_ptr<IData>> datasB;
    datasB.push_back(std::unique_ptr<IData>(new DataB(30)));
    datasB.push_back(std::unique_ptr<IData>(new DataB(40)));

    int resultA = calculateDataSum(datasA);
    int resultB = calculateDataSum(datasB);

    cout << resultA << endl;
    cout << resultB << endl;

    return 0;
}

通过这种方法,您也可以混合使用,这样您就可以拥有一个同时包含DataADataB 的向量。

【讨论】:

  • 我也喜欢这种方法,但是我们需要用这种方法将数据放在堆内存上?
  • 是的。但是std::vector 已经将其内容存储在堆上。然而,它是连续记忆。使用指针时,您会丢失它,因此如果这是一个问题,它可能会对缓存局部性产生影响。
  • 感谢向量的精确度
  • 请注意,如果new 成功但push_back 失败,push_back(new ...); 将面临内存泄漏的风险。您应该先构造一个unique_ptr 以获取new'ed 指针的所有权,然后再将其移动到向量中,例如:unique_ptr&lt;IData&gt; d(new ...); push_back(move(d));push_back(unique_ptr&lt;IData&gt;(new ...));
  • @LightnessRacesinOrbit 它确实需要额外的费用,但它也提供了一些额外的灵活性。严格看问题示例,这种灵活性没有帮助。我想只是想指出性能成本始终是相对的。
【解决方案2】:

从 C++ 类型系统的角度来看,vector&lt;Data&gt;vector&lt;IData&gt; 是完全不同的类型,无论 DataIData 层次关系如何。

您的问题的一个解决方案是基于模板的临时多态性:

template<typename T>
int calculateDataSum(vector<T> datas)
{
    static_assert(std::is_base_of<IData, T>::value);
    int sum;
    for (int i = 0; i < datas.size(); i++)
    {
        sum += datas[i].getNumber();
    }
    return sum;
}

注意static_assert 行。这不是必需的,但它会将允许的向量元素限制为IData 的孩子。

【讨论】:

  • 谢谢!这就是我正在寻找的解决方案
  • 是的,您的解决方案运行良好,但我们需要“ISO C++ 17 (/std: c++ 17)”标准。
  • @Hadou 你只需要static_assert(condition) 的那个标准。如果你把它变成static_assert(condition, "my message")(或完全删除static_assert这一行),那么一切都很好,不需要C++17。
  • 请注意,datas 应该通过 (const) 引用传递,以避免在一起计算之前必须对所有向量元素进行本地复制。此外,sum 在进入循环之前未初始化。
【解决方案3】:

vector&lt;DataA&gt;vector&lt;DataB&gt; 元素复制到vector&lt;IData&gt; 将不起作用,因为它会受到object slicing 的影响。您需要改用vector&lt;IData*&gt; 来让多态性正常工作,例如:

int calculateDataSum(const vector<IData*> &datas)
{
    int sum = 0;
    for (auto d : datas)
    {
        sum += d->getNumber();
    }
    return sum;
}

int main()
{
    DataA dA0(10);
    DataA dA1(20);
    DataB dB0(100);
    DataB dB1(200);

    vector<DataA> datasA;
    datasA.push_back(dA0);
    datasA.push_back(dA1);

    vector<DataB> datasB;
    datasB.push_back(dB0);
    datasB.push_back(dB1);

    vector<IData*> datas;
    transform(begin(datasA), end(datasA), back_inserter(datas),
        [](DataA &a) -> IData* { return &a; } 
    );
    int resultA = calculateDataSum(datas);

    datas.clear();
    transform(begin(datasB), end(datasB), back_inserter(datas),
        [](DataB &b) -> IData* { return &b; } 
    );
    int resultB = calculateDataSum(datas);

    cout << resultA << endl;
    cout << resultB << endl;

    return 0;
}

或者,更简单地说:

int main()
{
    DataA dA0(10);
    DataA dA1(20);
    DataB dB0(100);
    DataB dB1(200);

    vector<IData*> datasA;
    datasA.push_back(&dA0);
    datasA.push_back(&dA1);

    vector<IData*> datasB;
    datasB.push_back(&dB0);
    datasB.push_back(&dB1);

    int resultA = calculateDataSum(datasA);
    int resultB = calculateDataSum(datasB);

    cout << resultA << endl;
    cout << resultB << endl;

    return 0;
}

或者,您可以完全摆脱 vectors:

int calculateDataSum(initializer_list<IData*> datas)
{
    int sum = 0;
    for (auto d : datas)
    {
        sum += d->getNumber();
    }
    return sum;
}

int main()
{
    DataA dA0(10);
    DataA dA1(20);
    DataB dB0(100);
    DataB dB1(200);

    int resultA = calculateDataSum({&dA0, &dA1});
    int resultB = calculateDataSum({&dB0, &dB1});

    cout << resultA << endl;
    cout << resultB << endl;

    return 0;
}

甚至:

int calculateDataSum(const IData &arg)
{
    return arg.getNumber();
}

template <typename... Arguments>
int calculateDataSum(const IData &arg1, const Arguments&... args)
{
    return arg1.getNumber() + calculateDataSum(args...);
}

/* alternatively, in C++17 and later...
template <typename... Arguments>
int calculateDataSum(const IData &arg1, const Arguments&... args)
{
    int sum = arg1.getNumber();
    if constexpr(sizeof...(args) > 0)
    {
        sum += calculateDataSum(args...);
    }
    return sum;
}
*/

int main()
{
    DataA dA0(10);
    DataA dA1(20);
    DataB dB0(100);
    DataB dB1(200);

    int resultA = calculateDataSum(dA0, dA1);
    int resultB = calculateDataSum(dB0, dB1);

    cout << resultA << endl;
    cout << resultB << endl;

    return 0;
}

【讨论】:

    【解决方案4】:

    这是我解决问题的最终代码,谢谢大家

    #include <iostream>
    #include <vector>
    
    using namespace std;
    
    class IData
    {
        public:
            virtual int getNumber() = 0;
    };
    
    class DataA : public IData
    {
        public:
            DataA(int value) : _value(value) { }
    
            virtual int getNumber() override
            {
                return _value;
            }
    
        private:
            int _value = 0;
    };
    
    class DataB : public IData
    {
        public:
            DataB(int value) : _value(value) { }
    
            virtual int getNumber() override
            {
                return _value;
            }
    
        private:
            int _value = 0;
    };
    
    class DataOther
    {
        public:
            DataOther(int value) : _value(value) { }
    
            int getNumber()
            {
                return _value;
            }
    
        private:
            int _value = 0;
    };
    
    template<typename T>
    int calculateDataSum(const vector<T> & datas)
    {
        static_assert(std::is_base_of<IData, T>::value, "T does not inherit from IData");
        int sum = 0;
        for (int i = 0; i < datas.size(); i++)
        {
            sum += datas[i].getNumber();
        }
        return sum;
    }
    
    int main()
    {
        DataA dA0(10);
        DataA dA1(20);
        DataB dB0(100);
        DataB dB1(200);
        DataOther dO0(500);
    
        vector<DataA> datasA;
        datasA.push_back(dA0);
        datasA.push_back(dA1);
    
        vector<DataB> datasB;
        datasB.push_back(dB0);
        datasB.push_back(dB1);
    
        vector<DataOther> datasO;
        datasO.push_back(dO0);
    
        int resultA = calculateDataSum(datasA);
        int resultB = calculateDataSum(datasB);
    
    
        //Error because DataOther does not inherit IData
        //int resultO = calculateDataSum(datasO); 
    
        cout << resultA << endl;
        cout << resultB << endl;
    
        return 0;
    }
    

    【讨论】:

    • 请注意,datas 应通过 (const) 引用传递,以避免在一起计算之前必须对所有矢量元素进行本地复制。
    猜你喜欢
    • 1970-01-01
    • 2020-04-13
    • 2011-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-07
    • 2020-07-14
    相关资源
    最近更新 更多