如何在 c++ 中将字段从类传递到函数

How to pass fields from a class to function in c++?

本文关键字:函数 字段 c++      更新时间:2023-10-16

换句话说:如何将各种字段从自定义类传递到单个函数?

现在详细介绍:我有一个包含类的std::vector,例如CustomClass我必须通过某些标准从该类的字段中提取结果,这些标准是此类中的字段,并以某种方式组合这些数据。

我解决这个问题的第一个方法是使用一个函数,该函数接受类的std::vector作为参数,以便提取数据并返回std:map。此映射中的关键是应根据其组合数据的条件类型,该值是包含来自此向量所有成员的组合数据的int

问题是标准不仅仅是一个 - 此类中的多个字段可以用作标准(为了方便起见,所有标准都std::string,如果不是 - 我可以使函数模板化)。

对我来说,现在最简单的方法是使用几乎相同的代码制作数十个函数,每个函数都从此类中提取一个简单的具体字段。但是,更改可能需要对所有数十个功能进行类似的更改,这将是一个令人头疼的维护问题。但是在这个阶段,我想不出如何将这个类中的一个字段传递给单个函数......

下面是此类的示例代码:

// this is the class with data and criteria
class CustomClass
{
public:
    std::string criteria1;
    std::string criteria2;
    std::string criteria3;
//... and others criteria
    int dataToBeCombined;
// other code
};
// this is one of these functions
std::map<std::string, int> getDataByCriteria1(std::vector<CustomClass> aVector)
{
    std::map<std::string, int> result;
    foreach(CustomClass anObject in aVector)
    {
        if(result.find(anObject.criteria1)==result.end()) // if such of key doesn't exists
        {
            result.insert(std::make_pair(anObject.criteria1, anObject.dataToBeCombined));
        }
        else
        {
            // do some other stuff in order to combine data
        }
    }
    return result;
}

以类似的方式,我应该使其他函数应该与CustomClass::criteria2CustomClass::criteria3等一起使用。

我想将这些标准放在一个数组中,并仅将条件的数量传递给此函数,但该类将被其他人用于其他目的,并且字段必须易于阅读,因此这不是一个选项(即真实姓名不criteria1criteria2等,但都是描述性的)。

有人有想法吗?

编辑:有人将我的问题提到"C++具有不同返回类型的相同函数参数",这显然是非常不同的 - 在我的情况下,函数每次都返回相同的类型,只是它采用的参数必须是类中的各种字段。

您可以使用指向成员的指针。在函数中std::string CustomClass::*pField声明一个参数,用&CustomClass::criteriaN传递它,用anObject.*pField访问它。

查看有关该主题的更多信息:指向数据成员的指针。

如果所有"标准"都是同一类型,我没有看到一个优雅的解决方案,但你可以以某种方式"枚举"它们并使用它们的数量。

例如,您可以通过这种方式在CustomClass中声明模板化getVal()方法

template <int I>
   const std::string & getVal () const;

并以这种方式逐个数字、逐个标准地实施它们(在类主体之外)

template <>
const std::string & CustomClass::getVal<1> () const
 { return criteria1; }
template <>
const std::string & CustomClass::getVal<2> () const
 { return criteria2; }
template <>
const std::string & CustomClass::getVal<3> () const
 { return criteria3; }

现在,您可以通过这种方式转换模板化函数getDataByCriteria() getDataByCriteria1()

template <int I>
std::map<std::string, int> getDataByCriteria (std::vector<CustomClass> aVector)
 {
   std::map<std::string, int> result;
   for (const auto & cc : aVector)
    {
      if ( result.find(cc.getVal<I>()) == result.end()) // if such of key doesn't exists
       {
         result.insert(std::make_pair(cc.getVal<I>(), cc.dataToBeCombined));
       }
      else
       {
            // do some other stuff in order to combine data
       }
    }
   return result;
 }

并以这种方式调用它

auto map1 = getDataByCriteria<1>(ccVec);
auto map2 = getDataByCriteria<2>(ccVec);
auto map3 = getDataByCriteria<3>(ccVec);

---编辑:为不同类型的标准添加了解决方案(仅C++14---

如果"标准"的类型不同,则略有不同。

该解决方案有效,但在 C++14 中,这要归功于autodecltype().

通过示例,如果

std::string criteria1;
int         criteria2;
long        criteria3;

您可以使用auto声明getVal()

template <int I>
   const auto & getVal () const;

并定义(autogetVal()的所有版本

template <>
const auto & CustomClass::getVal<1> () const
 { return criteria1; }
template <>
const auto & CustomClass::getVal<2> () const
 { return criteria2; }
template <>
const auto & CustomClass::getVal<3> () const
 { return criteria3; }

并将autodecltype()结合起来,可以这样修改getDataByCriteria()

template <int I>
auto getDataByCriteria (std::vector<CustomClass> aVector)
 {
   std::map<decltype(aVector[0].getVal<I>()), int> result;
   for (const auto & cc : aVector)
    {
      if ( result.find(cc.getVal<I>()) == result.end()) // if such of key doesn't exists
       {
         result.insert(std::make_pair(cc.getVal<I>(), cc.dataToBeCombined));
       }
      else
       {
            // do some other stuff in order to combine data
       }
    }
   return result;
 }

该功能的使用保持不变(再次感谢auto

auto map1 = getDataByCriteria<1>(ccVec);
auto map2 = getDataByCriteria<2>(ccVec);
auto map3 = getDataByCriteria<3>(ccVec);

PS:注意:代码未测试

p.s.2 : 对不起,我的英语不好

您可以使用函数提取文件,例如

std::string extractFiled(const CustomClass &object, int which) {
  switch (which) {
    case 1:
      return object.criteria1;
    case 2:
      return object.criteria2;
    case 3:
      return object.criteria3;
    default:
      return object.criteria1;
  }
}

getDataByCriteria添加一个参数来指示要使用的文件。或者你可以只使用宏来实现getDataByCriteria

您将其标记为 C++11,因此请使用可变参数模板。

            class VariadicTest
            {
            public:
                VariadicTest()
                {
                    std::map<std::string, int> test1 = getDataByCriteria(testValues, criteria1);
                    std::map<std::string, int> test2 = getDataByCriteria(testValues, criteria2);
                    std::map<std::string, int> test3 = getDataByCriteria(testValues, criteria1, criteria2);
                    std::map<std::string, int> test4 = getDataByCriteria(testValues, criteria1, criteria3);
                }
            private:
                std::string criteria1 = { "Hello" };
                std::string criteria2 = { "world" };
                std::string criteria3 = { "." };
                std::vector<CustomClass> testValues = { {"Hello",1}, {"world",2},{ "!",3 } };
                template<typename T> std::map<std::string, int> getDataByCriteria(std::vector<CustomClass> values, T criteria)
                {
                    std::map<std::string, int> result;
                    //do whatever is needed here to filter values
                    for (auto v : values)
                    {
                        if (v.identifier == criteria)
                        {
                            result[values[0].identifier] = values[0].value;
                        }
                    }
                    return result;
                }
                template<typename T, typename... Args> std::map<std::string, int> getDataByCriteria(std::vector<CustomClass> values, T firstCriteria, Args... args)
                {
                    std::map<std::string, int> result = getDataByCriteria(values, firstCriteria);
                    std::map<std::string, int> trailer = getDataByCriteria(values, args...);
                    result.insert(trailer.begin(), trailer.end());
                    return result;
                }
            };

您没有指定在满足条件的各种条件下要执行的实际操作,因此很难说它们实际上可以组合多少。

以下是使用 STL std::accumulate()以及一些附加功能的可能解决方案。此示例是使用 Visual Studio 2015 编译的。

如果大多数功能可以组合成一个相当小的累加函数,这种方法是有意义的,因为大多数条件都以相同的方式处理。或者,您可以让 accumulate_op() 函数在处理一般情况本身时为特定情况调用其他函数。

您可以以此为开始并进行适当的修改。

一种这样的修改可能是摆脱使用std::map来维护状态。由于使用这种方法,您将根据标准进行累积来遍历std::vector,因此我不确定如果您正在积累,您甚至不需要使用std::map来记住任何东西。

// map_fold.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <numeric>
// this is the class with data and criteria
class CustomClass
{
public:
    CustomClass() : dataToBeCombined(0) {}
    std::string criteria1;
    std::string criteria2;
    std::string criteria3;
    //... and others criteria
    int dataToBeCombined;
    // other code
};
// This is the class that will contain the results as we accumulate across the
// vector of CustomClass items.
class Criteria_Result {
public:
    Criteria_Result() : dataToBeCombined(0) {}
    CustomClass myCriteria;
    std::map<std::string, int> result1;
    std::map<std::string, int> result2;
    std::map<std::string, int> result3;
    int dataToBeCombined;
};
// This is the accumulation function we provide to std::accumulate().
// This function will build our results.
class accumulate_op {
public:
    Criteria_Result * operator ()(Criteria_Result * x, CustomClass &item);
};
Criteria_Result * accumulate_op::operator ()(Criteria_Result *result, CustomClass &item)
{
    if (!result->myCriteria.criteria1.empty() && !item.criteria1.empty()) {
        std::map<std::string, int>::iterator it1 = result->result1.find(item.criteria1);
        if (it1 == result->result1.end()) // if such of key doesn't exists
        {
            result->result1.insert(std::make_pair(item.criteria1, item.dataToBeCombined));
        }
        else
        {
            // do some other stuff in order to combine data
            it1->second += item.dataToBeCombined;
        }
        result->dataToBeCombined += item.dataToBeCombined;
    }
    if (!result->myCriteria.criteria2.empty() && !item.criteria2.empty()) {
        std::map<std::string, int>::iterator it2 = result->result2.find(item.criteria2);
        if (it2 == result->result2.end()) // if such of key doesn't exists
        {
            result->result2.insert(std::make_pair(item.criteria2, item.dataToBeCombined));
        }
        else
        {
            // do some other stuff in order to combine data
            it2->second += item.dataToBeCombined;
        }
        result->dataToBeCombined += item.dataToBeCombined;
    }
    if (!result->myCriteria.criteria3.empty() && !item.criteria3.empty()) {
        std::map<std::string, int>::iterator it3 = result->result3.find(item.criteria3);
        if (it3 == result->result3.end()) // if such of key doesn't exists
        {
            result->result3.insert(std::make_pair(item.criteria3, item.dataToBeCombined));
        }
        else
        {
            // do some other stuff in order to combine data
            it3->second += item.dataToBeCombined;
        }
        result->dataToBeCombined += item.dataToBeCombined;
    }
    return result;
}
int main()
{
    Criteria_Result  result;
    std::vector<CustomClass> aVector;
    // set up the criteria for the search
    result.myCriteria.criteria1 = "string1";
    result.myCriteria.criteria2 = "string2";
    for (int i = 0; i < 10; i++) {
        CustomClass xx;
        xx.dataToBeCombined = i;
        if (i % 2) {
            xx.criteria1 = "string";
        }
        else {
            xx.criteria1 = "string1";
        }
        if (i % 3) {
            xx.criteria2 = "string";
        }
        else {
            xx.criteria2 = "string2";
        }
        aVector.push_back (xx);
    }
    // fold the vector into our results.
    std::accumulate (aVector.begin(), aVector.end(), &result, accumulate_op());
    std::cout << "Total Data to be combined " << result.dataToBeCombined << std::endl;
    std::cout << " result1 list " << std::endl;
    for (auto jj : result.result1) {
        std::cout << "    " << jj.first << "   " << jj.second << std::endl;
    }
    std::cout << " result2 list " << std::endl;
    for (auto jj : result.result2) {
        std::cout << "    " << jj.first << "   " << jj.second << std::endl;
    }
    std::cout << " result3 list " << std::endl;
    for (auto jj : result.result3) {
        std::cout << "    " << jj.first << "   " << jj.second << std::endl;
    }
    std::cout << " Trial two nn" << std::endl;
    result.myCriteria.criteria2 = "";
    result.result1.clear();
    result.result2.clear();
    result.result3.clear();
    result.dataToBeCombined = 0;
    // fold the vector into our results.
    std::accumulate(aVector.begin(), aVector.end(), &result, accumulate_op());
    std::cout << "Total Data to be combined " << result.dataToBeCombined << std::endl;
    std::cout << " result1 list " << std::endl;
    for (auto jj : result.result1) {
        std::cout << "    " << jj.first << "   " << jj.second << std::endl;
    }
    std::cout << " result2 list " << std::endl;
    for (auto jj : result.result2) {
        std::cout << "    " << jj.first << "   " << jj.second << std::endl;
    }
    std::cout << " result3 list " << std::endl;
    for (auto jj : result.result3) {
        std::cout << "    " << jj.first << "   " << jj.second << std::endl;
    }
    return 0;
}

这将生成如下输出:

Total Data to be combined 90
 result1 list
    string   25
    string1   20
 result2 list
    string   27
    string2   18
 result3 list
 Trial two

Total Data to be combined 45
 result1 list
    string   25
    string1   20
 result2 list
 result3 list