在 public 成员函数调用中引用私有成员变量的更安全方法?

A safer way to reference a private member variable in public member function call?

我想使用单个函数与多个私有成员变量进行交互。我想出了:

class Some_Vectors {

public:
  int is_value_in_vector(string vector_name, int value);
  void append_value_to_vector(string vector_name, int value)

private:
  vector<int> A;
  vector<int> B;

};


// returns value's index if it's there, -1 if not
int Some_Vectors::is_value_in_vector(string vector_name, int value) {

  vector<int> *selected_vector;

  if (vector_name == "A") {selected_vector = &A;}
  else if (vector_name == "B") {selected_vector = &B;}

  for (int i = 0; i < selected_vector->size(); i++){
    if (selected_vector[0][i] == value){
      return i;
    }
  }
  return -1;
}

它有效,但我觉得 unsafe/brittle 比较这样的字符串。有没有办法在函数调用中专门引用一个私有变量?

编辑为(希望)不那么主观的问题。我最终使用了 RichardCritten 的建议,即调用单个私有函数的多个 public 函数。

首先,如果您可以访问 C++17 或更高版本的编译器,可选 return 最现代和更可取的方式是使用 std::optional.

关于你的问题,正如@Dai 在评论中提到的,最好的方法是(恕我直言)使用

std::unordered_map<std::string, std::vector</*type*/>>

作为成员变量,可以如下操作。 See Live here

#include <vector>
#include <string>
#include <unordered_map>
#include <iostream>

using uMapType = std::unordered_map<std::string, std::vector<int>>;
class MyClass
{
private:
   uMapType _vecMap;
public:
  explicit MyClass(const uMapType& vecMap): _vecMap(std::move(vecMap)) {}

  int getValue(const std::string &vector_name, const int value)const
  {
     // std::unordered_map::find the key(vector name)
     auto getVec = _vecMap.find(vector_name);
     if(getVec != _vecMap.cend())   // if iterator not pointing to map's end
     {
        const std::vector<int> &selected_vector = getVec->second;
        for (std::size_t i = 0; i < selected_vector.size(); ++i)
           if (selected_vector[i] == value)
              return i;
     }
     return -1; 
  }
};


int main()
{
    MyClass obj(
    {
        {"A", {1, 2, 3, 4, 5}},
        {"B", {1, 2, 3, 4, 5}}
    });

    std::cout << obj.getValue("A", 3) << std::endl; // output: 2
    std::cout << obj.getValue("B", 5) << std::endl; // output: 5
    std::cout << obj.getValue("C", 3) << std::endl; // output: -1
    std::cout << obj.getValue("A", 0) << std::endl; // output: -1
    return 0;
}

std::optional 示例解决方案如下所示。

#include <optional>

using uMapType = std::unordered_map<std::string, std::vector<int>>;
class MyClass
{
private:
   uMapType _vecMap;
public:
  explicit MyClass(const uMapType& vecMap): _vecMap(std::move(vecMap)) {}

  std::optional<int> getValue(const std::string &vector_name, const int value)const
  {
      if(auto getVec = _vecMap.find(vector_name); getVec != _vecMap.cend())
      {
          for (std::size_t i = 0; i < getVec->second.size(); ++i)
            if (getVec->second[i] == value)
                return i;
      }
      return std::nullopt;
  }
};

您可以使用 unordered_map 来实现您的要求,如下所示。

  1. 如下声明 unordered_map

    unordered_map<string, vector<int>> umap;
    
  2. 使用 [] 运算符插入要映射的值。

    umap["A"] = {10,20}; 
    umap["B"] = {30,40};
    
  3. 使用查找功能在 unordered_map 中搜索键值,如下所示。

    string vector_name = "A";
    
    vector_name = "A";
    
    
    auto it = umap.find(vector_name);
    if (it == umap.end())
        return -1;
    
  4. 在地图中找到 key,value 对后,在 vector 中搜索特定的 int,如下所示。

    std::vector<int>::iterator iter = std::find(it->second.begin(), it->second.end(), 20);
    
  5. 如果 iter 没有指向 vector 结束然后 return intvector 中的确切位置作为下面。

    if ( iter !=  it->second.end())
        return std::distance(it->second.begin(),iter);
    else
        return -1;
    

您的完整示例代码可能如下所示。

int main() 
{ 

    unordered_map<string, vector<int>> umap; 

    // inserting values by using [] operator 
    umap["A"] = {10,20}; 
    umap["B"] = {30,40}; 


    string vector_name = "A"; 

    vector_name = "A"; 


    auto it = umap.find(vector_name);
    if (it == umap.end())
    return -1;

    std::vector<int>::iterator iter = std::find(it->second.begin(), it->second.end(), 20);

    if ( iter !=  it->second.end())
     return std::distance(it->second.begin(),iter);
    else
    return -1;

} 

我不同意建议地图或任何涉及字符串的解决方案的其他答案。

在代码中使用字符串来识别事物是非常脆弱的。一些主要缺点是:没有自动完成,没有编译时检查。在某些情况下您没有更好的选择(例如,您在编译时不知道标识符),但这不是其中之一。

一种解决方案是为函数指定有意义的名称。由于您提供了一个玩具示例,我将使用 AB 但在现实生活中它们应该是有意义的名称:

class X
{
    public:
    auto foo_a(int value) { return foo(A, value); }
    auto foo_b(int value) { return foo(B, value); }

private:
    int foo(std::vector<int>& v, int value) { return 24; }

    std::vector<int> A;
    std::vector<int> B;
};

如果您想要一个带有参数的函数 select 向量,您应该 select 带有枚举的向量。这样你就有了自动完成和 compile-time 安全(你不能传递无效的 selector - 就像你可以用字符串一样 - 除非你向后弯曲):

class Y
{
public:
    enum class Selector { A, B };

    auto foo(Selector selector, int value) { return foo(getVector(selector), value); }

private:
    std::vector<int>& getVector(Selector selector)
    {
        switch (selector)
        {
            case Selector::A:
                return A;
            case Selector::B:
                return B;
        }
    }

    int foo(std::vector<int>& v, int value) { return 24; }

    std::vector<int> A;
    std::vector<int> B;
};
Y y{};

y.foo(Y::Selector::A, 11);
y.foo(Y::Selector::B, 1024);