C++用指针指向某个类的向量时的左右值不匹配问题。

151 阅读3分钟

今天工作开发时写了几行有意思的代码:

    Struct Struct1
{
    wxString Name;
}
void CLASS1:: Function()
{
  CLASS2* Val1=dynamic_cast<CLASS2*>(Val2);
  vector<Struct1> *Val3=Val->getVal3();
  for(int i=0;i<Val3->size();++i)
  {
   const wxString label=Val3[i].Name;//该行在编译器中会标红报错
  }
}
    

当时看到这样写,但是标红报错,当时还没反应过来,以为是变量名或者类型写错了,检查后发现都不是这些问题,我就疑惑了,Val3[i]实际上就是一个Struct1变量,我这样去获取Struct1里面的Name,为什么会报错? 于是我做了一些尝试:将标红行修改为:

    const wxString label=Val3[i][i].Name;//该行在编译器中不会报错

此时编译器没有报错,但是运行程序时会发生闪退,于是我又作了以下尝试:

    const wxString label=Val3[0][i].Name;//该行在编译器中不会报错

此时编译器同样没有报错,并且程序按照我原来编写的目的在进行,即已经实现了我想要的效果了。 但为什么会这样呢?明明Val3只是一个一维向量,却要用这种方式来进行读取里面的内容,是因为指针导致的缘故吗?于是我就进调试模式一步步看程序怎么运行,又查了一些资料,得到了我想要的答案: 我先给出getVal3()这个函数的定义:

 CLASS2
{
  public:
 vector<Struct1> CLASS2::&getVal3(){return mVal3;};
 private:
 vector<Struct1> mVal3;
}
```
```

这个现象是因为指针的返回值所导致的。当我定义了vector *Val3=Val->getVal3()这样一个变量,getVal3()返回的是一个引用,因此Val3实际上指向的是一个vector,而不是vector。所以需要Val[0][i]才能在循环中访问Struct1,是因为Val3实际上指向的是一个vector<vector>,所以我需要先使用第一个索引定位到vector,然后再使用第二个索引访问到Struct1。
更细致一点,Val3指向vector,是因为Val3在声明时是一个指向vector的指针,cvlass2::getVal3()函数返回的是一个类型为vector的引用。因此,当Val3被Prompt初始化时,编译器会将Val3自动解释为左值,也就是指向vector<vector<>>的指针。所以,Val3实际上指向的是一个vector,因此我们需要使用Val3[0][i]来访问其中的Struct1变量。 当我们使用Val3进行初始化时,编译器会检查Val3变量的类型以及左值的类型。 如果类型不匹配(例如Val3是一个vector<Struct1> *类型,但左值类型是vector<vector<Struct1>>),那么编译器就会给出一条错误信息,指出类型不匹配,内容不能赋值给左值。
对于vector<Struct1> *Val3=Val->getVal3()中,Val3 就是一个左值,而Val->getVal3() 就是一个右值,因为右值最终会被赋值给左值。同时,Val3 并不总是指向同一个东西,而只有在Val->getVal3() 函数被调用时,你才能确定它指向的是真正的内容。 也就是说,当Val->getVal3() 被调用时,Val3 所指向的要么是vector<Struct1>,要么是vector<vector<Struct1>>。因此,在循环中使用 Val3[0][i].Name 来索引第二个 vector 内部的元素是正确的,而使用 Val3[i].Name 时会报错的原因就是这个左值的类型不正确。
当然如果遇到这种情况去用Val[0][i]来在循环中访问看起来有点Low,可以用Val->at(i)来实现这个效果,at()是std::vector的一个成员函数,如果索引超出了容器的范围,at()会抛出异常,而用[]则不会,这样就避免了Val[i][i]这样导致的闪退问题。
工作经验不到一年,可能解释起来条理没那么清晰,希望各位看官多给点意见。(另外这个编辑器我不太会用,为什么我编辑器文字换行顶格了,但显示的内容没有换行?)